We all know that Cassandra is a distributed database. However there’re situations where one needs to perform an atomic operation and for such cases a consensus must be reached between all the replicas.
For instance when dealing with payments we might require that we only insert the row once.
CQL provides an easy to use IF syntax to deal with such cases.
INSERT INTO payments (payment_time, customer_id, amount) VALUES (2016-11-02 12:23:34Z, 123, 12.00) IF NOT EXISTS;
Note: You can also use IF EXISTS
or any other IF <CONDITION>.
UPDATE payments SET amount = 10.00 WHERE payment_date = 2016-11-02 12:23:34Z AND customer_id = 123 IF amount = 12.00
This simple IF NOT EXISTS
isn’t free. Under the hood it triggers a lightweight transaction (also known as CAS for Compare And Set).
It’s called lightweight because it doesn’t imply locking as it’s the case in traditional (SQL) databases.
Lightweight doesn’t mean free either. In fact such queries require a read and a write and they also need to reach consensus among all the replicas.
Underlying principle
In Cassandra the consensus is reached by implementing the Paxos algorithm (that we’ve already discussed previously).
The leader role is not a master role (any node can act as a leader or more accurately as a proposer).
Briefly the proposer picks a proposal number and sends it to the participating replicas. (Remember the number of replicas is determined by the keyset’s replication factor). If the proposal number is the highest the replica has seen, the replica promises to not accept any earlier proposal (i.e. a proposal with a smaller number).
If the majority promises to accept the proposal, the leader may proceed with its proposal. However if a replica replies with another value for that proposal it’s the value the leader must propose.
This gives the linearisable or serial property because if a leader interrupts an on-going proposal it must complete it before proposing its own value.
Cassandra uses Paxos to implement a Compare-And-Set operation so it needs to intertwin read and writes into the Paxos protocol.
Consistency levels
Now that we have a clear picture in mind of what’s happening during a lightweight transaction (LWT) let’s discuss the consistency levels.
As there is a read and a write phase involved in the transaction we need 2 consistency levels.
Serial consistency levels
The read consistency is called the serial consistency and is set using the SERIAL CONSISTENCY
command. It can take only 2 values:
SERIAL
LOCAL_SERIAL
The only difference is when Cassandra uses several datacenters. LOCAL_SERIAL
runs Paxos only in the local datacenter whereas SERIAL
runs it accross all datacenters.
The SERIAL CONSISTENCY
command is only available for conditional updates (UPDATE
or INSERT
with the IF
statement).
Write consistency
Then the write phase uses the regular CONSISTENCY
command. It default to ONE
but can be set to any of the write consistency levels.
Read consistency
The last thing is if we want to consider any on-going lightweight transactions when reading a value. In this case we want to read the value of any on-going transaction.
This can be done by setting the read consistency level to SERIAL
. This is the regular consistency level, the one set with the CONSISTENCY
command (not the serial consistency) and it applies to read queries only (i.e. SELECT
statements).