public interface PersistentDataStore
extends java.io.Closeable
This interface should be used for database integrations, or any other data store implementation that stores data in some external service. The SDK will take care of converting between its own internal data model and a serialized string form; the data store interacts only with the serialized form. The SDK will also provide its own caching layer on top of the persistent data store; the data store implementation should not provide caching, but simply do every query or update that the SDK tells it to do.
Implementations must be thread-safe.
Conceptually, each item in the store is a DataStoreTypes.SerializedItemDescriptor
which always has
a version number, and can represent either a serialized object or a placeholder (tombstone)
for a deleted item. There are two approaches a persistent store implementation can use for
persisting this data:
1. Preferably, it should store the version number and the DataStoreTypes.SerializedItemDescriptor.isDeleted()
state separately so that the object does not need to be fully deserialized to read them. In
this case, deleted item placeholders can ignore the value of DataStoreTypes.SerializedItemDescriptor.getSerializedItem()
on writes and can set it to null on reads. The store should never call DataStoreTypes.DataKind.deserialize(String)
or DataStoreTypes.DataKind.serialize(DataStoreTypes.ItemDescriptor)
.
2. If that isn't possible, then the store should simply persist the exact string from
DataStoreTypes.SerializedItemDescriptor.getSerializedItem()
on writes, and return the persisted
string on reads (returning zero for the version and false for DataStoreTypes.SerializedItemDescriptor.isDeleted()
).
The string is guaranteed to provide the SDK with enough information to infer the version and
the deleted state. On updates, the store must call DataStoreTypes.DataKind.deserialize(String)
in
order to inspect the version number of the existing item if any.
Error handling is defined as follows: if any data store operation encounters a database error, or
is otherwise unable to complete its task, it should throw a RuntimeException
to make the SDK
aware of this. The SDK will log the exception and will assume that the data store is now in a
non-operational state; the SDK will then start polling isStoreAvailable()
to determine
when the store has started working again.
Modifier and Type | Method and Description |
---|---|
DataStoreTypes.SerializedItemDescriptor |
get(DataStoreTypes.DataKind kind,
java.lang.String key)
Retrieves an item from the specified collection, if available.
|
DataStoreTypes.KeyedItems<DataStoreTypes.SerializedItemDescriptor> |
getAll(DataStoreTypes.DataKind kind)
Retrieves all items from the specified collection.
|
void |
init(DataStoreTypes.FullDataSet<DataStoreTypes.SerializedItemDescriptor> allData)
Overwrites the store's contents with a set of items for each collection.
|
boolean |
isInitialized()
Returns true if this store has been initialized.
|
boolean |
isStoreAvailable()
Tests whether the data store seems to be functioning normally.
|
boolean |
upsert(DataStoreTypes.DataKind kind,
java.lang.String key,
DataStoreTypes.SerializedItemDescriptor item)
Updates or inserts an item in the specified collection.
|
void init(DataStoreTypes.FullDataSet<DataStoreTypes.SerializedItemDescriptor> allData)
All previous data should be discarded, regardless of versioning.
The update should be done atomically. If it cannot be done atomically, then the store must first add or update each item in the same order that they are given in the input data, and then delete any previously stored items that were not in the input data.
allData
- a list of DataStoreTypes.DataKind
instances and their corresponding data setsDataStoreTypes.SerializedItemDescriptor get(DataStoreTypes.DataKind kind, java.lang.String key)
If the key is not known at all, the method should return null. Otherwise, it should return
a DataStoreTypes.SerializedItemDescriptor
as follows:
1. If the version number and deletion state can be determined without fully deserializing
the item, then the store should set those properties in the DataStoreTypes.SerializedItemDescriptor
(and can set DataStoreTypes.SerializedItemDescriptor.getSerializedItem()
to null for deleted items).
2. Otherwise, it should simply set DataStoreTypes.SerializedItemDescriptor.getSerializedItem()
to
the exact string that was persisted, and can leave the other properties as zero/false. See
comments on PersistentDataStore
for more about this.
kind
- specifies which collection to usekey
- the unique key of the item within that collectionDataStoreTypes.KeyedItems<DataStoreTypes.SerializedItemDescriptor> getAll(DataStoreTypes.DataKind kind)
If the store contains placeholders for deleted items, it should include them in the results,
not filter them out. See get(DataStoreTypes.DataKind, String)
for how to set the properties of the
DataStoreTypes.SerializedItemDescriptor
for each item.
kind
- specifies which collection to useboolean upsert(DataStoreTypes.DataKind kind, java.lang.String key, DataStoreTypes.SerializedItemDescriptor item)
If the given key already exists in that collection, the store must check the version number
of the existing item (even if it is a deleted item placeholder); if that version is greater
than or equal to the version of the new item, the update fails and the method returns false.
If the store is not able to determine the version number of an existing item without fully
deserializing the existing item, then it is allowed to call DataStoreTypes.DataKind.deserialize(String)
for that purpose.
If the item's DataStoreTypes.SerializedItemDescriptor.isDeleted()
method returns true, this is a
deleted item placeholder. The store must persist this, rather than simply removing the key
from the store. The SDK will provide a string in DataStoreTypes.SerializedItemDescriptor.getSerializedItem()
which the store can persist for this purpose; or, if the store is capable of persisting the
version number and deleted state without storing anything else, it should do so.
kind
- specifies which collection to usekey
- the unique key for the item within that collectionitem
- the item to insert or updateboolean isInitialized()
In a shared data store, the implementation should be able to detect this state even if
init(com.launchdarkly.sdk.server.subsystems.DataStoreTypes.FullDataSet<com.launchdarkly.sdk.server.subsystems.DataStoreTypes.SerializedItemDescriptor>)
was called in a different process, i.e. it must query the underlying
data store in some way. The method does not need to worry about caching this value; the SDK
will call it rarely.
boolean isStoreAvailable()
This should not be a detailed test of different kinds of operations, but just the smallest possible operation to determine whether (for instance) we can reach the database.
Whenever one of the store's other methods throws an exception, the SDK will assume that it may have
become unavailable (e.g. the database connection was lost). The SDK will then call
isStoreAvailable()
at intervals until it returns true.