SH:RethinkDB:
Need support?
Contact me at sam@samuelhughes.com.

RethinkDB Tips

Here is some advice on how to solve specific problems when using RethinkDB.

Contents:

  1. Single-Document Atomic Updates (with non-deterministic computations)

Single-Document Atomic Updates (with non-deterministic computations)

RethinkDB, with update or replace operations, only lets you update individual rows atomically. There are no multi-row transactions! Not only that, you can only update the row if it uses a deterministic computation evaluated by ReQL. The general data and computation flow of the operation is this:

atomically:

  1. read the document X
  2. perform a deterministic computation in ReQL, which
    • takes X as input
    • produces Y as output (or errors)
  3. write Y in place of X
then:
  1. return to the client that this succeeded (or failed)
An example:
r.table('foo').get(Id).update(x => r.expr({b: x('a')}))

If RethinkDB had some element of transactionality, you could include non-atomic ReQL computations in step 2. And if RethinkDB exposed transactions to the user, you could do the following:

transactionally (if RethinkDB had transactions):

  1. the client runs r.table(T).get(Id) and receives X
  2. the client computes document Y
  3. the client performs r.table(T).get(Id).replace(Y)
  4. the client ends the transaction and
    1. if it succeeds, we're done
    2. if it fails (because of a conflict with another client's transaction), we might try again with a new transaction

Now here is a recipe to atomically update a document with a client-side computation:

  1. Store a field {nonce: r.uuid()} field in your document, on the first insertion.
  2. To atomically update the document with id Id:
    1. Run r.table(T).get(Id) and store the nonce field in your client-side variable OriginalNonce.
    2. Compute document Y.
    3. Run the following update query:
      r.uuid().do(replacementNonce =>
          r.table(T).get(Id).replace(x =>
              r.branch(x('nonce').ne(OriginalNonce),
                  r.error("Nonce is not equal"),
                  r.expr(Y).merge({nonce: replacementNonce}))))
      
    4. If it fails, that means another client updated the document at the same time you tried to, so you could proceed to step 1 and try again. If you try again repeatedly, use an exponential back-off.
END OF PAGE