Couchbase CTO Ravi Mayuram announced Distributed Multi-document ACID Transactions in Couchbase Server 6.5. I highly recommend reading Ravi’s blog which highlights how Couchbase transactions are an innovative union of ACID guarantees with scale, high-availability, performance and flexibility.
In this blog, I will dive deeper into our distributed ACID transactions functionality.
Simple Yet Powerful
First off let’s look at how easy it is to write a Couchbase transaction. You open a transaction, do some work and commit (or fail) with Atomicity, Consistency, Isolation and Durability. Leveraging the regular Couchbase SDK and APIs, you can utilize the capabilities of the underlying programming platform. Here is a code snippet that shows a basic debit/credit by transferring funds from one account to another with an ACID transaction:
There are many capabilities built in to make programming with transactions in Couchbase frictionless:
- Automatic commit or rollback with flexibility to also do this explicitly.
- Automatic retries for transient errors such as durability delays due to network glitches or lock conflicts.
- Configurable duration for a transaction. Deadlocks are automatically resolved since one of the transactions will timeout and release its resources.
- Automatic cleanup of orphaned transactions if the client crashes.
If you want a quick tutorial on programming with the transactions Java API and a downloadable code sample, check out Graham Pople’s blog and video.
Couchbase transactions provide Atomicity, Consistency, Isolation and Durability as the ACID guarantees require:
- Atomicity and Durability are binary properties – either you have it or you don’t.
- Isolation is a spectrum with tradeoffs. Popular relational databases such as Oracle, MySQL,SQL Server have chosen different default and maximum isolation levels.
- Consistency has different meanings to traditional ACID audience and to distributed systems audience. The traditional ACID definition says that a transaction should take the database from valid state to another. The distributed systems definition stemming from CAP theorem says each read should receive the latest write, this although starts to sound a little like Isolation semantics.
Here is a summary of the ACID guarantees provided by transactions in Couchbase Server 6.5.
|Atomicity||All-or-nothing semantics for updating multiple documents in multiple shards on different nodes.|
|Consistency||Replicas are immediately consistent with the transaction commit.|
GSI, FTS, XDCR are eventually consistent with the transaction commit (N1QL can enforce strong consistency upon read with request_plus)
|Isolation||Read Committed isolation for all concurrent readers – whether transactional reads or non-transactional reads|
|Durability||3 levels of durability available for data protection:|
Yes they are Distributed
Couchbase ACID transactions are multi-document and multi-node – so they are truly distributed as you can see in the simplified Couchbase architectural illustration below. The illustration shows a simple Couchbase cluster with 3 nodes and 9 vbuckets/shards with 2 replicas (total 3 copies of data). The transaction modifies two documents, Andy and Beth, with their active copy on two separate nodes. The successful completion of the transaction will ensure that the active copy of each of the two documents as well as a majority of their total copies (which in this case means 1 of 2 replicas in addition to the active) are updated with the new value. Failure will guarantee that the state is unchanged and identical as prior to the transaction.
Feature Drill Down
While the ACID summary provides a high level perspective, the following details provide a deeper understanding of the semantics:
Multi-node, multi-bucket, multi-document
A single transaction can span multiple documents, in multiple buckets, on multiple nodes. Since in Couchbase, a bucket maps to a database, a transaction can actually span multiple databases living on the same Couchbase cluster. This, along with Couchbase’s ability to perform JOINs across buckets, gives enormous flexibility to the application.
Only committed data is readable by DCP consumers
All reads done in any manner including from N1QL, XDCR, Analytics, Mobile, Eventing, Connectors will only return committed data. No one will ever be able to read any uncommitted/dirty data. Couchbase Data Change Protocol (DCP) protocol ensures that no uncommitted, intermediate data is ever read by a downstream consumer.
Locking and Conflict Detection
Conflicts between transactions trying to update the same documents are detected and handled by rolling back one of them and retrying it (until the transaction timeout which by default is 15s). This is done via a combination of optimistic and pessimistic locking at the document level.
Any document you want to modify in a transaction has to be first read inside the transaction and all writes to it are automatically CAS(Check and Set) writes. Hence, if a transaction reads data that subsequently gets changed by another transactional (or non-transactional) write, then at write time this transaction will detect the CAS conflict, rollback and retry.
In the debit/credit transaction above where money is being transferred from Beth’s account to Andy’s account, if Beth’s account changes after the transaction has read her account but before it has modified it, the transaction will detect that, rollback, and retry.
Once the document is modified in the transaction, it implicitly gets a write lock on the document which is released only when the transaction ends. If a second transaction tries to modify a document that is already locked by a transaction, the latter transaction will detect the write conflict, do a rollback and retry until the transaction timeout period.
Going back to the debit/credit example above, assume an in-flight transaction where Beth’s account has been deducted but Andy’s has yet to be updated. At this point Beth’s account document is locked. So if another transaction to transfer money from Beth’s account to say Bill’s account is being tried concurrently, it will detect the write conflict on Beth and rollback.
In Couchbase Server 6.5, we have a new implementation of the replication protocol that ensures a mutation is visible to readers only after it has met its replication/persistence criteria and can be rolled back if those criteria are not met. This new replication mechanism is undergoing comprehensive in-house Jepsen testing – read Korrigan’s blog to find out more.
Synchronous replication provides tunable durability allowing you to choose the level of protection against failures (node crash, disk failure, single or multiple failures) that you want for each write. There are 3 levels to choose from:
- majority – this durability level ensures that the write is propagated to a majority of the replicas before it returns (e.g. if the cluster is set up with 2 replicas, it will be written to RAM on the active and propagated to at least 1 replica before returning success to the application)
- persistToActive – this level ensures that the write is propagated in RAM to a majority of the replicas and persisted to disk on the active before it returns success to the application.
- persistToMajority – this level ensures that the write is persisted to disk on a majority of the nodes before returning success to the application.
If durability fails (timeout, node failure, etc) the write will be automatically rolled back and the client notified of the failure.
Note: The new synchronous durability writes are applicable to transactions as well as single document mutations.
Note: If you do not specify any durability level then it is handled asynchronously which is eventually durable.
Transactions are layered over the new Synchronous Replication mechanism and by default set the durability level at ‘majority’. You can override the default by choosing a different durability level per transaction. ‘persistToMajority’ provides the strongest data protection in case of multiple failures.
You can use transactions as well as synchronous durability on Ephemeral buckets as well. There are caching use cases which do not need persistence but still want to ensure items in the cache are updated with ACID guarantees.
The multi-document ACID transactions support in Couchbase Server 6.5 is groundbreaking and opens up new use cases for your NoSQL applications. We would love to hear your feedback as well as input on what further enhancements for transactions you need. Here are some resources for you to get started: