{"id":5437,"date":"2018-07-09T06:35:10","date_gmt":"2018-07-09T13:35:10","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5437"},"modified":"2025-06-13T19:28:04","modified_gmt":"2025-06-14T02:28:04","slug":"multi-document-transactions-acid-couchbase-2","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/","title":{"rendered":"Multi-document transactions: ACID and Couchbase Part 2"},"content":{"rendered":"<div class=\"paragraph\">\n<p><em><strong>Important note<\/strong><\/em>: Multi-Document ACID Transactions are now available in Couchbase. See: <a href=\"https:\/\/www.couchbase.com\/transactions\/\">ACID Transactions for NoSQL Applications<\/a> for more information!<\/p>\n<p>Multi-document transactions were not covered in the previous post in this series: <a href=\"https:\/\/www.couchbase.com\/blog\/acid-properties-couchbase-part-1\/\">ACID Properties and Couchbase<\/a>. That blog post covered the building blocks of ACID that Couchbase supports for the <em>single<\/em> document. In this blog post, we\u2019re going to use that foundation to build something <em>like<\/em> an atomic, distributed multi-document transaction.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>Disclaimer: the code in this blog post is not recommended for production. It\u2019s a stripped-down example that <em>might<\/em> be useful to you as-is, but will need hardening and polish before it\u2019s ready for production. The intent is to give you an idea of what it would take for those (hopefully rare) situations where you need multi-document transactions with Couchbase.<\/strong><\/p>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_a_brief_recap\">A Brief Recap<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>In part 1, we saw that the ACID properties actually are available in Couchbase at the single document level. For use cases where documents can store data together in a denormalized fashion, this is adequate. In some cases, denormalization into a single document alone isn\u2019t enough to meet requirements. For those small number of use cases, you may want to consider the example in this blog post.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A note of warning going in: this blog post is a <em>starting<\/em> point for you. Your use case, technical needs, Couchbase Server\u2019s capabilities, and the edge cases you care about will all vary. There is no one-size-fits all approach as of today.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_multi_document_transactions_example\">Multi-document transactions example<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>We\u2019re going to focus on a simple operation to keep the code simple. For more advanced cases, you can build on this code and possibly genericize it and tailor it as you see fit.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Let\u2019s say we are working on a game. This game involves creating and running farms (sounds crazy, I know). Suppose in this game, you have a barn which contains some number of chickens. Your friend also has a barn, containing some number of chickens. At some point, you might want to transfer some chickens from your barn to a friend\u2019s barn.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>In this case, data normalization probably will not help. Because:<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>A single document containing all barns is just not going to work for a game of any significant size.<\/li>\n<li>It doesn\u2019t make sense for your barn document to contain your friend\u2019s barn document (or vice versa).<\/li>\n<li>The rest of the game logic works fine with single-document atomicity: it\u2019s only the chicken transfer that\u2019s tricky.<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>To begin with, all we have are two &#8220;barn&#8221; documents (Grant Barn and Miller Barn):<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11001-barn-documents-initial.png\" alt=\"Initial barn documents\" \/><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>This method we\u2019ll use to transfer chickens is called a &#8220;two-phase commit&#8221;. There are six total steps. <a href=\"https:\/\/github.com\/couchbaselabs\/blog-source-code\/tree\/master\/Groves\/110AcidPart2\/src\">The full source code is available on GitHub<\/a>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><em>It occurred to me after taking all the screenshots and writing the code samples that chickens live in coops, not barns? But just go with me on this.<\/em><\/p>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_0_transaction_document\">0) Transaction document<\/h3>\n<div class=\"paragraph\">\n<p>The first step is to create a transaction document. This is a document that will keep track of the multi-document transaction and the <strong>state<\/strong> of the transaction. I\u2019ve created a C# <code>Enum<\/code> with the possible states used in the transaction. This will be a number when stored in Couchbase, but you could use strings or some other representation if you\u2019d like.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">public enum TransactionStates\r\n{\r\n    Initial = 0,\r\n    Pending = 1,\r\n    Committed = 2,\r\n    Done = 3,\r\n    Cancelling = 4,\r\n    Cancelled = 5\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>It will start out in a state &#8220;Initial&#8221;. Going into this transaction, we have a &#8220;source&#8221; barn, a &#8220;destination&#8221; barn, and some number of chickens to transfer.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">var transaction = _bucket.Upsert(new Document&lt;TransactionRecord&gt;\r\n{\r\n    Id = transactionDocumentKey,\r\n    Content = new TransactionRecord\r\n    {\r\n        SourceId = source.Id,\r\n        Source = source.Value,\r\n        DestinationId = destination.Id,\r\n        Destination = destination.Value,\r\n        Amount = amountToTransfer,\r\n        State = TransactionStates.Initial\r\n    }\r\n});<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Let\u2019s peek in on the data again. Now there are three documents. The transaction is new; the barn documents the same as they started.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11002-barn-documents-initial-transaction-document.png\" alt=\"Initial multi-document transactions document\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_1_switch_to_pending\">1) Switch to pending<\/h3>\n<div class=\"paragraph\">\n<p>Next, let\u2019s put the transaction document into a &#8220;pending&#8221; state. We\u2019ll see later why the &#8220;state&#8221; of a transaction is important.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">var transCas = UpdateWithCas&lt;TransactionRecord&gt;(transaction.Id, x =&gt; x.State = TransactionStates.Pending, transaction.Document.Cas);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>I\u2019ve cheated a little bit here, because I\u2019m using an <code>UpdateWithCas<\/code> function. I\u2019m going to be doing this a lot, because updating a document using a Cas operation can be a bit verbose in .NET. So I created a little helper function:<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">private ulong UpdateWithCas&lt;T&gt;(string documentId, Action&lt;T&gt; act, ulong? cas = null)\r\n{\r\n    var document = _bucket.Get&lt;T&gt;(documentId);\r\n    var content = document.Value;\r\n    act(content);\r\n    var result = _bucket.Replace(new Document&lt;T&gt;\r\n    {\r\n        Cas = cas ?? document.Cas,\r\n        Id = document.Id,\r\n        Content = content\r\n    });\r\n    \/\/ NOTE: could put retr(ies) here\r\n    \/\/ or throw exception when cas values don't match\r\n    return result.Document.Cas;\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>That\u2019s an important helper method. It uses <a href=\"https:\/\/www.couchbase.com\/blog\/optimistic-or-pessimistic-locking-which-one-should-you-pick\/\">optimistic locking<\/a> to update a document, but doesn\u2019t do any retries or error handling.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Let\u2019s get back to the data., We still have three documents, but the transaction document &#8220;state&#8221; has been updated.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11003-barns-and-transaction-pending.png\" alt=\"Multi-document Transaction pending\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_2_change_the_documents\">2) Change the documents<\/h3>\n<div class=\"paragraph\">\n<p>Next, we\u2019ll actually perform the necessary mutations to the barn documents. Subtracting a chicken from the source barn, and adding a chicken to the destination barn. At the same time, we\u2019re going to &#8220;tag&#8221; these barn documents with the transaction document ID. Again, you\u2019ll see why this is important later. I\u2019ll also store the Cas values of these mutations, because they\u2019ll be necessary later when making further changes to these documents.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">var sourceCas = UpdateWithCas&lt;Barn&gt;(source.Id, x =&gt; { x.Chickens -= amountToTransfer; x.Transaction = transaction.Id; });\r\n\r\nvar destCas = UpdateWithCas&lt;Barn&gt;(destination.Id, x =&gt; { x.Chickens += amountToTransfer; x.Transaction = transaction.Id; });<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>At this point, the code has moved a chicken between barns. Also notice the transaction &#8220;tag&#8221; on the barns.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11004-barns-tagged-and-transaction-pending.png\" alt=\"Barns tagged with transaction\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_3_switch_to_committed\">3) Switch to committed<\/h3>\n<div class=\"paragraph\">\n<p>So far, so good. The mutations are complete; it\u2019s time to mark the transaction as &#8220;committed&#8221;.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">transCas = UpdateWithCas&lt;TransactionRecord&gt;(transaction.Id, x =&gt; x.State = TransactionStates.Committed, transCas);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>The only thing that changed is the &#8220;state&#8221; of the transaction.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11005-barn-documents-tags-transaction-committed.png\" alt=\"Transaction committed\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_4_remove_transaction_tags\">4) Remove transaction tags<\/h3>\n<div class=\"paragraph\">\n<p>Now that the multi-document transaction is in a &#8220;committed&#8221; state, the barns no longer need to know that they\u2019re part of a transaction. Remove those &#8220;tags&#8221; from the barns.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">UpdateWithCas&lt;Barn&gt;(source.Id, x =&gt; { x.Transaction = null; }, sourceCas);\r\nUpdateWithCas&lt;Barn&gt;(destination.Id, x =&gt; { x.Transaction = null; }, destCas);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Now the barns are free from the transaction.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11006-barns-no-tags-transaction-committed.png\" alt=\"Multi-document Transactions tags removed\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_5_transaction_is_done\">5) Transaction is done<\/h3>\n<div class=\"paragraph\">\n<p>The last step is to change the transaction state to &#8220;done&#8221;.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">UpdateWithCas&lt;TransactionRecord&gt;(transaction.Id, x =&gt; x.State = TransactionStates.Done, transCas);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>If we\u2019ve gotten this far, then the multi-document transaction is complete. The barns have the correct number of chickens after the transfer.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11007-transaction-done.png\" alt=\"Transaction done\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_rollback_what_if_something_goes_wrong\">Rollback: what if something goes wrong?<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>It\u2019s entirely possible that something goes wrong during multi-document transactions. That\u2019s the point of a transaction, really. All the operations happen, or they don\u2019t.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>I\u2019ve put the code for steps 1 through 5 above inside of a single try\/catch block. An exception could happen anywhere along the way, but let\u2019s focus on two critical points.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>Exception during &#8220;pending&#8221;<\/strong> &#8211; How should we handle if an error occurs right in the middle of step 2. That is, AFTER a chicken is subtracted from the source barn but BEFORE a chicken is added to the destination barn. If we didn\u2019t handle this situation, a chicken would disappear right into the aether and our game players would cry fowl!<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>Exception after transaction &#8220;committed&#8221;<\/strong> &#8211; The transaction has a state of &#8220;committed&#8221;, but an error occurs before the transaction tags are no longer on the barns. If we didn\u2019t handle this, then it might appear from other processes that the barns are still inside of a transaction. The <em>first<\/em> chicken transfer would be successful, but no further chickens could be transferred.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>The code can handle these problems inside of the <code>catch<\/code> block. This is where the &#8220;state&#8221; of the transaction comes into play (as well as the transaction &#8220;tags&#8221;).<\/p>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_exception_during_pending\">Exception during &#8220;pending&#8221;<\/h3>\n<div class=\"paragraph\">\n<p>This is the situation that would lose chickens and make our gamers angry. The goal is to replace any lost chickens and get the barns back to the state they were before the transaction.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Let\u2019s assume it happens right in the middle. For this example, we\u2019ve got a new transaction: transfer 1 chicken from Burrows barn (12 chickens) to White barn (13 chickens).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11013-rollback.png\" alt=\"Barns before rollback\" \/><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>An error happened right in the middle. The source barn has one less chicken, but the destination barn didn\u2019t get it.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11008-barns-inconsistent-state-with-transaction.png\" alt=\"Barns inconsistent and transaction\" \/><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Here are the 3 steps to recovery:<\/p>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_1_cancel_transaction\">1) Cancel transaction<\/h4>\n<div class=\"paragraph\">\n<p>Change the state of the transaction to &#8220;cancelling&#8221;. Later we\u2019ll change it to &#8220;cancelled&#8221;.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">UpdateWithCas&lt;TransactionRecord&gt;(transaction.Id, x =&gt; x.State = TransactionStates.Cancelling, transactionRecord.Cas);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>The only thing that\u2019s changed so far is the transaction document:<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11009-transaction-cancelling.png\" alt=\"Transaction now cancelling\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_2_revert_changes\">2) Revert changes<\/h4>\n<div class=\"paragraph\">\n<p>Next, we need to revert the state of the barns back to what they were before. Note that this is ONLY necessary if the barn has a transaction tag on it. If it doesn\u2019t have a tag, then we know it\u2019s already in its pre-transaction state. If there is a tag, remove it.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">UpdateWithCas&lt;Barn&gt;(source.Id, x =&gt;\r\n{\r\n    if (x.Transaction != null)\r\n    {\r\n        x.Chickens += transactionRecord.Value.Amount;\r\n        x.Transaction = null;\r\n    }\r\n});\r\n\r\nUpdateWithCas&lt;Barn&gt;(destination.Id, x =&gt;\r\n{\r\n    if (x.Transaction != null)\r\n    {\r\n        x.Chickens -= transactionRecord.Value.Amount;\r\n        x.Transaction = null;\r\n    }\r\n});<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Now the barns are back to what they were before.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11010-barns-rolled-back-transaction-still-cancelling.png\" alt=\"Barns rolled back\" \/><\/span><\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_3_cancelled_transaction\">3) Cancelled transaction<\/h4>\n<div class=\"paragraph\">\n<p>The last thing to do is to set the transaction to &#8220;cancelled&#8221;.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight decode:true\"><code class=\"language-C#\">UpdateWithCas&lt;TransactionRecord&gt;(transaction.Id, x =&gt; x.State = TransactionStates.Cancelled);<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>And now, the transaction is &#8220;cancelled&#8221;.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/11011-transaction-cancelled.png\" alt=\"Transaction cancelled\" \/><\/span><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>This preserves the total number of chickens in the game. At this point, you still need to handle the error that caused the need for a rollback. You can retry, notify the players, log an error, or all of the above.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_exception_during_committed\">Exception during &#8220;committed&#8221;<\/h3>\n<div class=\"paragraph\">\n<p>Next, let\u2019s look at another case: the changes to the barns are complete, but they have not yet had their transaction tags removed. Assuming the game logic cares about these tags, future multi-document transactions might not be possible.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>The exact same rollback logic handles this situation as well.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_problems_and_edge_cases\">Problems and edge cases<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>This simplified example may be just the trick for your application, but there are a lot of edge cases to think about.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>What if the process dies part way through?<\/strong> This means that the code doesn\u2019t even reach the <code>catch<\/code> block. You may need to check for any uncompleted multi-document transactions upon application startup and perform recovery there. Or possibly have a different watchdog process that looks for incomplete multi-document transactions.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>What if there\u2019s a read during the transaction?<\/strong> Suppose I &#8220;get&#8221; the barns right between their updates. This will be a &#8220;dirty&#8221; read, which can be problematic.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>What state is everything left it?<\/strong> Whose responsibility is it to complete \/ rollback pending multi-document transactions?<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>What happens if the same document is part of two multi-document transactions concurrently?<\/strong> You will need to build in logic to prevent this from happening.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>The sample contains all the state for rolling back. But if you want more transaction types (maybe you want to transfer cows)?<\/strong> You\u2019d need a transaction type identifer, or you\u2019d need to genericize the transaction code so that you can abstract &#8220;amount&#8221; used in the above examples and instead specify the updated version of the document.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p><strong>Other edge cases<\/strong>. What happens if there\u2019s a node in your cluster that fails in the middle of the transaction? What happens if you can\u2019t get the locks you want? How long do you keep retrying? How do you identify a failed transaction (timeouts)? There are lots and lots of edge cases to deal with. You should thoroughly test all the conditions you expect to encounter in production. And in the end, you might want to consider some sort of mitigation strategy. If you detect a problem or you find a bug, you can give some free chickens to all parties involved after you fix the bug.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_other_options\">Other options<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Our engineering team has been experimenting with <strong>RAMP client-side transactions<\/strong>. RAMP (Read Atomic Multi-Partition) is a way to guarantee atomic visibility in <a href=\"https:\/\/www.couchbase.com\/blog\/distributed-databases-overview\/\">distributed databases<\/a>. For more information, check out <a href=\"https:\/\/rustyrazorblade.com\/post\/2015\/ramp-made-easy\/\">RAMP Made Easy<\/a> by Jon Haddad or <a href=\"https:\/\/www.bailis.org\/blog\/scalable-atomic-visibility-with-ramp-transactions\/\">Scalable Atomic Visibility with RAMP Transactions<\/a> by Peter Bailis.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>The most mature example put together for Couchbase is from <a href=\"https:\/\/github.com\/subalakr\/ramptransactionsimple\">Graham Pople using the Java SDK<\/a>. <strong>This is also not a production-ready library.<\/strong> But, Graham is doing some interesting stuff with client-side multi-document transactions. Stay tuned!<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Another option is the open-source <a href=\"https:\/\/ndescribe.atlassian.net\/wiki\/spaces\/DOC\/pages\/1081383\/Transactional+Documents\">NDescribe library<\/a> by Iain Cartledge (who is a <a href=\"https:\/\/www.couchbase.com\/blog\/get-to-know-couchbase-community-iain-cartledge\/\">Couchbase community champion<\/a>).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Finally, check out the <a href=\"https:\/\/www.couchbase.com\/blog\/saga-pattern-implement-business-transactions-using-microservices-part\/\">Saga Pattern<\/a>, which is especially helpful for multi-document transactions among microservices.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_conclusion\">Conclusion<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>This blog post talked about how to use the ACID primitives available to Couchbase to create a kind of atomic multi-document transaction for a distributed database. This is still not a completely solid replacement for ACID, but it is sufficient for what the vast majority of modern microservices-based applications need. For the small percentage of use cases that need additional transactional guarantees, Couchbase will continue to innovate further.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Thanks to Mike Goldsmith, Graham Pople, and Shivani Gupta who helped to review this blog post.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>If you are eager to take advantage of the benefits of a distributed database like Couchbase, but still have concerns about multi-document transactions, please reach out to us! You can ask questions on the <a href=\"https:\/\/www.couchbase.com\/forums\/\">Couchbase Forums<\/a> or you can contact me on <a href=\"https:\/\/twitter.com\">Twitter @mgroves<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Important note: Multi-Document ACID Transactions are now available in Couchbase. See: ACID Transactions for NoSQL Applications for more information! Multi-document transactions were not covered in the previous post in this series: ACID Properties and Couchbase. That blog post covered the [&hellip;]<\/p>\n","protected":false},"author":71,"featured_media":5109,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1811,1816],"tags":[9499],"ppma_author":[8937],"class_list":["post-5437","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-couchbase-server","tag-acid-transactions"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Multi-document transactions: ACID and Couchbase Part 2<\/title>\n<meta name=\"description\" content=\"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Multi-document transactions: ACID and Couchbase Part 2\" \/>\n<meta property=\"og:description\" content=\"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-07-09T13:35:10+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T02:28:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"767\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Matthew Groves\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@mgroves\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Matthew Groves\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\"},\"author\":{\"name\":\"Matthew Groves\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58\"},\"headline\":\"Multi-document transactions: ACID and Couchbase Part 2\",\"datePublished\":\"2018-07-09T13:35:10+00:00\",\"dateModified\":\"2025-06-14T02:28:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\"},\"wordCount\":1996,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg\",\"keywords\":[\"ACID transactions\"],\"articleSection\":[\".NET\",\"Couchbase Server\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\",\"name\":\"Multi-document transactions: ACID and Couchbase Part 2\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg\",\"datePublished\":\"2018-07-09T13:35:10+00:00\",\"dateModified\":\"2025-06-14T02:28:04+00:00\",\"description\":\"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg\",\"width\":1920,\"height\":767},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Multi-document transactions: ACID and Couchbase Part 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58\",\"name\":\"Matthew Groves\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ba51e6aacc53995c323a634e4502ef54\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g\",\"caption\":\"Matthew Groves\"},\"description\":\"Matthew D. Groves is a guy who loves to code. It doesn't matter if it's C#, jQuery, or PHP: he'll submit pull requests for anything. He has been coding professionally ever since he wrote a QuickBASIC point-of-sale app for his parent's pizza shop back in the 90s. He currently works as a Senior Product Marketing Manager for Couchbase. His free time is spent with his family, watching the Reds, and getting involved in the developer community. He is the author of AOP in .NET, Pro Microservices in .NET, a Pluralsight author, and a Microsoft MVP.\",\"sameAs\":[\"https:\/\/crosscuttingconcerns.com\",\"https:\/\/x.com\/mgroves\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/matthew-groves\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Multi-document transactions: ACID and Couchbase Part 2","description":"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/","og_locale":"en_US","og_type":"article","og_title":"Multi-document transactions: ACID and Couchbase Part 2","og_description":"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.","og_url":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-07-09T13:35:10+00:00","article_modified_time":"2025-06-14T02:28:04+00:00","og_image":[{"width":1920,"height":767,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg","type":"image\/jpeg"}],"author":"Matthew Groves","twitter_card":"summary_large_image","twitter_creator":"@mgroves","twitter_misc":{"Written by":"Matthew Groves","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/"},"author":{"name":"Matthew Groves","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58"},"headline":"Multi-document transactions: ACID and Couchbase Part 2","datePublished":"2018-07-09T13:35:10+00:00","dateModified":"2025-06-14T02:28:04+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/"},"wordCount":1996,"commentCount":1,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg","keywords":["ACID transactions"],"articleSection":[".NET","Couchbase Server"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/","url":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/","name":"Multi-document transactions: ACID and Couchbase Part 2","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg","datePublished":"2018-07-09T13:35:10+00:00","dateModified":"2025-06-14T02:28:04+00:00","description":"Multi-document transactions are possible using the single-document ACID properties of Couchbase Server and some client-side code.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/05\/105-acid-properties-hero.jpg","width":1920,"height":767},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/multi-document-transactions-acid-couchbase-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Multi-document transactions: ACID and Couchbase Part 2"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/3929663e372020321b0152dc4fa65a58","name":"Matthew Groves","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/ba51e6aacc53995c323a634e4502ef54","url":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","caption":"Matthew Groves"},"description":"Matthew D. Groves is a guy who loves to code. It doesn't matter if it's C#, jQuery, or PHP: he'll submit pull requests for anything. He has been coding professionally ever since he wrote a QuickBASIC point-of-sale app for his parent's pizza shop back in the 90s. He currently works as a Senior Product Marketing Manager for Couchbase. His free time is spent with his family, watching the Reds, and getting involved in the developer community. He is the author of AOP in .NET, Pro Microservices in .NET, a Pluralsight author, and a Microsoft MVP.","sameAs":["https:\/\/crosscuttingconcerns.com","https:\/\/x.com\/mgroves"],"url":"https:\/\/www.couchbase.com\/blog\/author\/matthew-groves\/"}]}},"authors":[{"term_id":8937,"user_id":71,"is_guest":0,"slug":"matthew-groves","display_name":"Matthew Groves","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/70feb1b28a099ad0112b8d21fe1e81e1a4524beed3e20b7f107d5370e85a07ab?s=96&d=mm&r=g","author_category":"","last_name":"Groves","first_name":"Matthew","job_title":"","user_url":"https:\/\/crosscuttingconcerns.com","description":"Matthew D. Groves is a guy who loves to code.  It doesn't matter if it's C#, jQuery, or PHP: he'll submit pull requests for anything.  He has been coding professionally ever since he wrote a QuickBASIC point-of-sale app for his parent's pizza shop back in the 90s.  He currently works as a Senior Product Marketing Manager for Couchbase. His free time is spent with his family, watching the Reds, and getting involved in the developer community.  He is the author of AOP in .NET, Pro Microservices in .NET, a Pluralsight author, and a Microsoft MVP."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5437","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/71"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=5437"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5437\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/5109"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=5437"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=5437"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=5437"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=5437"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}