Skip to content

Lightweight Transaction

DuyHai DOAN edited this page Nov 22, 2014 · 9 revisions

Achilles offers a complete support for the new distributed Lightweight Transaction (LWT) features of Cassandra 2.0 using the Options API

LWT for insertions

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());

LWT for conditional updates

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

LWT Result Listener

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());
		  

Home

Clone this wiki locally