Package eu.antidotedb.client
Usage
First add the necessary imports if you are not using an IDE that adds them automatically:
import eu.antidotedb.client.*;
import java.net.InetSocketAddress;
To connect to Antidote, create a new AntidoteClient
instance and pass it one or more
InetSocketAddress
entries:
AntidoteClient antidote = new AntidoteClient(new InetSocketAddress("localhost", 8087));
You can also pass a PoolManager
to get more control over the configuration.
Antidote-Keys
Objects in the database are addressed using a Key
.
A key contains the CRDT type and a name of type ByteString.
To create a key the static methods on Key
can be used:
For example a key for a set datatype stored under key "users" can be retrieved as follows:
SetKey<String> userSet = Key.set("users");
It is also possible to statically import the methods for creating keys:
import static eu.antidotedb.client.Key.*;
...
SetKey<String> userSet = set("users");
The type parameter of SetKey
denotes the type of elements stored in the set.
The default is String, but this can easily be configured by passing a ValueCoder
to the set
method.
For example a key for a set of ByteStrings can be created like this:
SetKey<ByteString> userSetB = set("users", ValueCoder.bytestringEncoder);
More information is available in the documentation of the ValueCoder
interface.
Buckets
In Antidote each object is stored in a Bucket
.
To create a bucket use the static bucket
method:
Bucket bucket = Bucket.bucket("mybucket");
Transactions
A unit of operation in Antidote is a transaction. A client should first start a transaction, then read and/or update several objects, and finally commit the transaction. There are two types of transactions: interactive transactions and static transactions.
Interactive transactions
With an interactive transaction, a client can execute several updates and reads before committing the transactions.
An interactive transaction can be started with the startTransaction
method on the AntidoteClient
object.
It is good practice to use the resulting InteractiveTransaction
with a
try-with-resource statement to avoid leaving transactions open accidentially.
ValueCoder<Integer> intCoder = ValueCoder.stringCoder(Object::toString, Integer::valueOf);
CounterKey c = Key.counter("my_example_counter");
SetKey<Integer> numberSet = Key.set("set_of_numbers", intCoder);
try (InteractiveTransaction tx = antidoteClient.startTransaction()) {
int val = bucket.read(tx, c);
bucket.update(tx, numberSet.add(val));
}
Static transactions
Static transactions consist of a single bulk operation. A static transaction can update multiple objects atomically or read multiple objects atomically, but it is not possible to read and update in the same static transaction.
Alternatively, use the AntidoteClient.noTransaction()
method to execute the request without any transactional context:
List<String> value = bucket.read(antidote.noTransaction(), userSet);
Reading objects
A Bucket
has a read
method, which
retrieves the current value of the
object from the database.
The read
method takes a transaction object.
For reading multiple objects simultaneously, a BatchRead
can be used.
Start a batch read with the AntidoteClient.newBatchRead()
method.
Then use the BatchRead
as the transaction context for several read-requests.
Finally, after committing the BatchRead
via the commit
method,
the results can be obtained via the BatchReadResult.get()
methods:
CounterKey c1 = Key.counter("c1");
CounterKey c2 = Key.counter("c2");
CounterKey c3 = Key.counter("c3");
BatchRead batchRead = antidoteClient.newBatchRead();
BatchReadResult<Integer> c1val = bucket.read(batchRead, c1);
BatchReadResult<Integer> c2val = bucket.read(batchRead, c2);
BatchReadResult<Integer> c3val = bucket.read(batchRead, c3);
batchRead.commit(antidoteClient.noTransaction());
int sum = c1val.get() + c2val.get() + c3val.get();
If all read values have the same time the Bucket.readAll
method provides a shortcut for performing several reads simultaneously:
List<Integer> values = bucket.readAll(antidoteClient.noTransaction(), Arrays.asList(c1, c2, c3));
The return value datatype of the read method corresponds to the datatype of the CRDT object being read:
- Reading a
register
returns T (the type passed with ValueCoder to Key.register(), default is String) - Reading a
multiValueRegister
returns List<T> (default is String) - Reading a
counter
returns Integer - Reading a
map_g
ormap_rr
returnsMapReadResult
- Reading a
set
orset_removeWins
returns List<T> - Reading a
flag_ew
orflag_dw
returns Boolean
Reading Map CRDTs
Reading a map CRDT object consists of two steps: First, reading the map object using the read
method.
This returns a MapReadResult
object which presents the result of a read request on a map CRDT.
The second step is to get the values of nested CRDTs from the MapReadResult. This can be done using the get
method:
MapKey m = Key.map_rr("test_map");
MapKey.MapReadResult mapReadResult = bucket.read(client.noTransaction(), m);
String mapReadResult.get(Key.register("map_register_entry"));
Updating objects
Each Key
has one or more methods to create update operations on the key.
To execute the update operation on a bucket it has to be passed to the Bucket.update
method.
Just like with reads, the update method take a transactional context as its first argument.
For example a SetKey
has an add
method to add
elements to the set:
bucket.update(antidote.noTransaction(), set("users").add("Hans Wurst"));
For performing several updates simultaneously the Bucket.updates
methods can be used.
When constructing map updates, updates for nested CRDTs can be created in the same way as other updates, which makes it possible to buid complex map-updates updating several components of the map at once:
MapKey testmap = map_aw("testmap2");
AntidoteStaticTransaction tx = antidoteClient.createStaticTransaction();
bucket.update(tx,
testmap.update(
counter("a").increment(5),
register("b").assign("Hello")
));
Session guarantees
To ensure session guarantees like "read your writes" Antidote uses vector clocks. Each operation returns a vector clock indicating the time after the operation. At each request to Antidote a vector clock can be given to force a minimum time for the snapshot used in the request.
TODO this is not yet implemented on the client
-
Interface Summary Interface Description BatchReadResult<T> MergeRegisterKey.ValueMerger<V> UpdateContext ValueCoder<T> A ValueCoder is used encode and decode values to and from ByteStrings. -
Class Summary Class Description AntidoteClient An AntidoteClient manages the connection to one or more Antidote servers.AntidoteConfigManager Created by Salman on 3/5/2017.AntidoteStaticTransaction The Class AntidoteStaticTransaction.AntidoteTransaction A transaction, either static (batch of updates) or interactive (mixed reads and writes)ApbCoder Created by mweber on 12.04.17.BatchRead Bucket A bucket represents a section of the database.CommitInfo Connection The Class Connection.ConnectionPool The Class ConnectionPool.CounterKey FlagKey InteractiveTransaction Key<Value> An Antidote key consists of a CRDT type and a corresponding key.MapKey MapKey.MapReadResult Presents the result of a read request on a map CRDT.MaxValueMerger<V extends java.lang.Comparable<V>> MergeRegisterKey<V> MergeRegisterKey.MergeResult<V> MessageCodes MinValueMerger<V extends java.lang.Comparable<V>> MVRegisterKey<T> NoTransaction This class can be used to execute an individual operation without any transactional contextPoolManager The Class PoolManager.RegisterKey<T> ResponseDecoder<Value> Can decode a response for a read requestSetKey<T> TransactionWithReads UpdateOp An update operation which can be executed with methods onBucket
. -
Enum Summary Enum Description InteractiveTransaction.TransactionStatus The enum types of the transaction status. -
Exception Summary Exception Description AntidoteException The Class AntidoteException.