Skip to content

Lightweight Transaction

DuyHai DOAN edited this page Dec 24, 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 (using the IF NOT EXISTS clause in CQL), you can use OptionsBuilder.ifNotExists() as shown below

 manager.insert(new MyEntity(...), OptionsBuilder.ifNotExists());

LWT for deletions

To delete an entity if it already exists (using the IF EXISTS clause in CQL), you can use OptionsBuilder.ifExists() as shown below

 manager.deleteById(MyEntity.class, primaryKey, OptionsBuilder.ifExists());

LWT for conditional updates or deletions

For conditional LWT updates, you can inject as many LWTCondition as needed using the OptionsBuilder.ifEqualCondition() API.

@Entity(table = "user")
public class UserEntity {
	@PartitionKey
	private Long id;

	@Column
	private String login;

	@Column
	private String name;
}

... 

// For update
manager.update(user, OptionsBuilder
		.ifEqualCondition("login","jdoe"),
		.ifEqualCondition("id",10L));
		
// For delete
manager.deleteById(UserEntity.class, OptionsBuilder
		.ifEqualCondition("login","jdoe"));
				

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.lwtResultListener() 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
		.ifEqualCondition("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 OptionsBuilder.lwtEqualCondition("login","jdoe")

    UPDATE user SET name = 'Johnny DOE' WHERE id=10 IF login='jdoe';
    
     [applied] | login
    -----------+-------
         False |  johndoe

Had you registered a LWTResultListener, the returned TypedMap of LWTResult.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 LWTResultListenerComposite implements LWTResultListener {
    
      private final List<LWTResultListener> listeners = new LinkedList<>();
      
      public LWTResultListenerComposite(LWTResultListener...listeners) {
        this.listeners = Arrays.asList(listeners);  
      }
                 
      public void onSuccess() {
        for(LWTResultListener listener:listeners) {
          listener.onSuccess();
        }
      }
     
      public void onError(LWTResult lwtResult) {
        for(LWTResultListener listener:listeners) {
          listener.onError(lwtResult);
        }  
      }
    }             	

### LWT Serial Consistency

To use LOCAL_SERIAL consistency in a multi data-center infrastructure instead of the SERIAL consistency for LWT operations, you can call lwtLocalSerial() on the OptionsBuilder

manager.update(user, OptionsBuilder
		.ifEqualCondition("login","jdoe")
		.lwtLocalSerial());
		  

Home

Clone this wiki locally