-
Notifications
You must be signed in to change notification settings - Fork 92
Lightweight Transaction
Achilles offers a complete support for the new distributed Lightweight Transaction (LWT) features of Cassandra 2.0 using the Options API
To insert an entity if it does not exist (the famous IF NOT EXISTS clause in CQL), you can use OptionsBuilder.ifNotExists()
as shown below
manager.insert(new MyEntity(...), OptionsBuilder.ifNotExist());
For conditional LWT updates, you can inject as many [LWTCondition] as needed using the OptionsBuilder.ifConditions()
API.
@Entity(table = "user")
public class UserEntity {
@Id
private Long id;
@Column
private String login;
@Column
private String name;
}
...
manager.update(user, OptionsBuilder.
ifConditions(Arrays.asList(
new LWTCondition("login","jdoe"),
new LWTCondition("id",10L))));
Please note that for now only equality comparison is allowed in Cassandra for LWT conditions. Should that change in the future, Achilles will be updated to support new types of comparison
To have tighter control on LWT updates or inserts, Achilles lets you inject a listener for LWT operations result. Again the OptionsBuilder.casResultListener()
API comes to the rescue.
LWTResultListener lwtListener = new LWTResultListener() {
public void onSuccess() {
// Do something on success
}
public void onError(LWTResult lwtResult) {
//Get type of LWT operation that fails
LWTResult.Operation operation = lwtResult.operation();
// Print out current values
TypedMap currentValues = lwtResult.currentValues();
for(Entry<String,Object> entry: currentValues.entrySet()) {
System.out.println(String.format("%s = %s",entry.getKey(), entry.getValue()));
}
}
};
manager.update(user, OptionsBuilder.
ifConditions(Arrays.asList(
new LWTCondition("login","jdoe")))
.lwtResultListener(lwtListener));
Let's take a concrete example. Suppose you have inserted in the user
table the following data:
INSERT INTO user(id,login,name) VALUES(10,'johndoe','John DOE');
Now you try to update with new CASCondition("login","jdoe")
UPDATE user SET name = 'Johnny DOE' WHERE id=10 IF login='jdoe';
[applied] | login
-----------+-------
False | johndoe
Had you registered a CASResultListener, the returned TypedMap of CASResult.currentValues()
would contain:
- key: "[applied]", value: false (boolean)
- key: "login", value: "johndoe" (String)
Remark: it is not possible to register more than one listener but it is very easy to create a listener composite to call a list of ordered listeners yourself:
public class CASResultListenerComposite implements CASResultListener {
private final List<CASResultListener> listeners = new LinkedList<>();
public CASResultListenerComposite(CASResultListener...listeners) {
this.listeners = Arrays.asList(listeners);
}
public void onCASSuccess() {
for(CASResultListener listener:listeners) {
listener.onCASSuccess();
}
}
public void onCASError(CASResult casResult) {
for(CASResultListener listener:listeners) {
listener.onCASError(casResult);
}
}
}
### CAS Serial Consistency
To use LOCAL_SERIAL consistency in a multi-datacenter infrastructure instead of the SERIAL consistency for CAS operations, you can call
casLocalSerial()
on the OptionsBuilder
manager.update(user, OptionsBuilder.
ifConditions(Arrays.asList(
new CASCondition("login","jdoe")))
.casLocalSerial());
-
Bootstraping Achilles at runtime
- Runtime Configuration Parameters
-
Manager
-
Consistency Level
-
Cassandra Options at runtime
-
Lightweight Transaction (LWT)
-
JSON Serialization
-
Interceptors
-
Bean Validation (JSR-303)