Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rethinkdb atomic operations

Let's say I have a document

{
    id: 1,
    fruits: []
}

fruits here acts as a SET

Now I want to atomically add a value to fruits array for document with primary key = 1 OR create such document if it does not exist(i.e. use SetInsert ReQL under the hood)

I also need to do the same for increment(ReQL .Add)

Obviously this can't be done in client code as it breaks atomicity and I end up with inconsistent data

I wish something like this was possible

r.table('results').insert({
  id: '62c70132-6516-4279-9803-8daf407ffa5c',
  counter: r.row('counter').add(1).default(0)
}, {conflict: "update"})

but it dies with "RqlCompileError: r.row is not defined in this context in"

Any help/guidance is appreciated, thanks!

like image 267
let4be Avatar asked Jul 26 '15 07:07

let4be


2 Answers

This is not possible with insert at the moment. The other mentioned solution is not atomic because it uses a subquery. We're working on a solution for this in https://github.com/rethinkdb/rethinkdb/issues/3753 .

You can however use replace to perform an atomic upsert:

r.table('results').get('62c70132-6516-4279-9803-8daf407ffa5c')
 .replace({
  id: '62c70132-6516-4279-9803-8daf407ffa5c',
  counter: r.row('counter').add(1).default(0)
})

replace will actually perform an insert if the document doesn't exist.

like image 173
Daniel Mewes Avatar answered Sep 17 '22 00:09

Daniel Mewes


In RethinkDB, all single-query updates are atomic. If Rethink doesn't think a particular update/replace operation will be atomic, it will throw an error and require you to add a non-atomic flag to the query. So normally, you don't have to worry too much about it. However, this is only with update and replace queries. It isn't possible to do this atomically with insert.

You are correct that if you retrieve that document, update it client side, and then put it back in the DB that it would be a non-atomic update by nature as well.

In a single query, though, you could do the following which is effectively an upsert in the same manner you used insert for, but using replace:

r.table('FruitStore')
.get(1)
.replace({
  id : 1, 
  fruits : r.row('fruits').default([]).append('orange') 
})

...which would be atomic. Similarly, to use the add operation:

r.table('FruitStore')
.get(1)
.replace({
  id : 1, 
  count : r.row('count').default(0).add(1) 
})
like image 38
Chris Foster Avatar answered Sep 21 '22 00:09

Chris Foster