Here is some advice on how to solve specific problems when using RethinkDB.
Contents:
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:
An example:atomically:
then:
- read the document X
- perform a deterministic computation in ReQL, which
- takes X as input
- produces Y as output (or errors)
- write Y in place of X
- return to the client that this succeeded (or failed)
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):
- the client runs r.table(T).get(Id) and receives X
- the client computes document Y
- the client performs r.table(T).get(Id).replace(Y)
- the client ends the transaction and
- if it succeeds, we're done
- 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:
- Store a field {nonce: r.uuid()} field in your document, on the first insertion.
- To atomically update the document with id Id:
- Run r.table(T).get(Id) and store the nonce field in your client-side variable OriginalNonce.
- Compute document Y.
- 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}))))- 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.