True or False? MarkLogic is ACID compliant

by Paxton Hare

MarkLogic is an ACID compliant nosql database

TRUE

It's true. MarkLogic is a transactional, ACID compliant NoSQL database.

But that's not what I've heard. There are no ACID compliant NoSQL databases.

Why don't you believe me?

Because... umm... mongo. Because I've never heard of you. Stop lying to me.

Ok. Allow me prove it.

First, This is how MarkLogic supports ACID. Go read that if you want the gory details or don't know what ACID means. It has something for everyone.

How about a demonstration?

You are most likely interested in connecting with MarkLogic from your application server. MarkLogic provides a Java Client API and a Node JS Client API for your development pleasure. Not using Java or Node? No problem. Both of these APIS use the MarkLogic REST API under the hood. You can simply make HTTP calls to MarkLogic from your favorite language.

Following along at home

I'm going to demonstrate transactions using the REST API as it is common to both APIs. In order to follow along at home you will need MarkLogic Server 8 or greater installed and running. You will also need a REST client. I'm going to use curl because it is already installed on my Mac. If you are on Windows you can grab it from the curl download page.

Planning our attack

Our approach will be pretty simple.

  1. Create a Transaction
  2. Insert some documents
  3. Commit the Transaction

Did you see what I did there? I said "insert some documents". Not "insert a document". Other well known NoSQL solutions are limited to updating one document in a transaction.

MarkLogic supports single and multiple document transactions. If you insert a single document via our REST API it either succeeds and commits or fails and rolls back. What I'm about to demonstrate is how to achieve mult-document transactions.

Let's do this

Before we can do anything we need to create a new transaction. The output will be something like this:

HTTP/1.1 303 See Created Transaction
Set-Cookie: HostId=2066943731304981853
Location: /v1/transactions/8053141704532058295
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

The new transaction ID is returned in the response Location header. We need this transaction ID for subsequent calls.

Location: /v1/transactions/8053141704532058295

Let's insert a document. Notice how we are passing in the transaction ID from the first step.

Now insert a second document.

At this point we have inserted two documents but we have not committed the transaction. This means that the documents are not available for use yet. We can verify this by asking for our documents.

The output will be empty. No documents found.

HTTP/1.1 200 OK
Content-type: multipart/mixed; boundary=document-part-boundary
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

Now let's commit our transaction.

The result will confirm our commit.

HTTP/1.1 204 Committed
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

Let's confirm that our documents made it into the database.

The output will now contain our documents.

HTTP/1.1 200 OK
Content-type: multipart/mixed; boundary=document-part-boundary
Server: MarkLogic
Content-Length: 418
Connection: Keep-Alive
Keep-Alive: timeout=5

--document-part-boundary
Content-Type: application/json
Content-Disposition: attachment; filename="/hello-world.json"; category=content; format=json
Content-Length: 17

{"hello":"world"}
--document-part-boundary
Content-Type: application/json
Content-Disposition: attachment; filename="/goodbye-world.json"; category=content; format=json
Content-Length: 19

{"goodbye":"world"}
--document-part-boundary--

How's that for Proof?

Are you convinced yet?

Nope. I still don't believe you. What if something goes wrong?

Then we do a rollback.

Rolling Back

Let's do it all again. But this time we will roll back instead of commit.

Create the transaction.

HTTP/1.1 303 See Created Transaction
Set-Cookie: HostId=2066943731304981853
Location: /v1/transactions/1564674180533347795
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

Insert some new documents.

Now let's rollback our transaction.

HTTP/1.1 204 Rolled Back
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

Verify that we rolled back.

The output will be empty. No documents found. Success!

HTTP/1.1 200 OK
Content-type: multipart/mixed; boundary=document-part-boundary
Server: MarkLogic
Content-Length: 0
Connection: Keep-Alive
Keep-Alive: timeout=5

Convinced Yet?

I just showed you how you can create a transaction, insert some docs, and either commit or rollback. But I get it. You are skeptical.

Sometimes you just have to try it for yourself. We've all been there. Like the time our moms told us not to stare at the sun. It might hurt our eyes. "Yeah right. I'll just... My eyes!"

Put on your sunglasses and grab the sample scripts for yourself.

Want to see it work in Java? Check out this blog post on InfoQ by our Enterprise CTO Ken Krupa.

Comments

  • Cool, looks good. but can it solve/handle a lock/deadlock ? This is simple example. but this mistake happens often. (yes it is bad coding) Yes this is Oracle SQL> select table_name,ini_trans,INITIAL_EXTENT,PCT_FREE,PCT_USED from user_tables where table_name = 'DEADLOCK'; TABLE_NAME INI_TRANS INITIAL_EXTENT PCT_FREE PCT_USED ------------------------------ ---------- -------------- ---------- ---------- DEADLOCK 1 10 INSERT INTO deadlock VALUES (1,'A'); INSERT INTO deadlock values (2,'B'); COMMIT; SQL> analyze table deadlock compute statistics; Table analyzed. select table_name,ini_trans,INITIAL_EXTENT,PCT_FREE,PCT_USED from user_tables where table_name = 'DEADLOCK'; TABLE_NAME INI_TRANS INITIAL_EXTENT PCT_FREE PCT_USED ------------------------------ ---------- -------------- ---------- ---------- DEADLOCK 1 65536 10 SELECT * FROM deadlock; SQL> SELECT * FROM deadlock; ID F ---------- - 1 A 2 B Ok -session 1 UPDATE deadlock SET fld = 'M' WHERE id = 1; 1 row updated. -- session 2 UPDATE deadlock SET fld = 'N' WHERE id = 2; 1 row updated -- session 1 UPDATE deadlock SET fld = 'X' WHERE id = 2; Nothing happens anymore Session 3 -- session as SYS conn / as sysdba SELECT ( SELECT username FROM gv$session WHERE sid=a.sid) blocker, a.sid, ' is blocking ', ( SELECT username FROM gv$session WHERE sid=b.sid) blockee, b.sid FROM gv$lock a, gv$lock b WHERE a.block = 1 AND b.request > 0 AND a.id1 = b.id1 AND a.id2 = b.id2; BLOCKER SID 'ISBLOCKING' ------------------------------ ---------- ------------- BLOCKEE SID ------------------------------ ---------- TEST 57 is blocking TEST 52 We have a blocking lock -- session 2 UPDATE DEADLOCK SET fld = 'Y' WHERE id = 1; Session 1 SQL> ORA-00060: deadlock detected while waiting for resource