Search code examples
rethinkdb

How to do an atomic update in RethinkDB with more than just the primary key?


I have a table called Wallet which can be thought of as a users balance. There's one document per user so I made the primary key userId and there is a balance field which stores the users balance. My initial thought is I need to be able to pull the document from the DB and do certain checks at the application layer before changing the user balance, so this rules out a .get(id).update(...) which I know is atomic.

What I did instead is added a property called lock which is a string. The application must first .get(id).update({ lock: reallylongstring }) and then when the application is ready to commit changes they must pass that lock back up and ideally Rethink will reject any changes if the lock is wrong (meaning someone else came in and acquired a lock afterwards).

If this was mongo I would do something like:

this.findOneAndUpdate({
  _id: id,
  lock: lock
}, {
  ...
})

And then any update that had the wrong lock would fail because the document would not be found. What is the best way to do this in Rethink? Or is my approach just all around wrong and what is a better approach?


Solution

  • You can put a branch in the update:

    .get(id).update(function(row) {
      return r.branch(row('lock').eq(LOCK), UPDATE, r.error("error: ..."));
    })
    

    A command like .getAll(id).filter({lock: LOCK}).update(...) will not work reliably because the lock could change between the filter and the update.