getting AR to do postgres jsbonb atomic updates

It is now easy to store a hash serialized as jsonb in posgres. It is even pretty easy with store_accessor to make individual keys in the hash look like separate attributes.

But when AR decides the jsonb column needs to be updated, it will send the entire hash as an update value. If you only changed a key or two, it will still send the entire hash – possibly overwriting changes to other keys made by other processes/clients.

Recent versions of postgres supports an atomic update on just certain keys of a jsonb hash, using jsonb_set in SQL.

I’d like to explore trying to get AR to do this, at first with a plugin. With dirty tracking, it’s fairly straightforward for AR to figure out what top-level keys have been changed and what haven’t.

But I can’t quite figure out where to intervene in AR to change the SQL generated in an ‘update’ operation. So I can generate different SQL that will do an atomic update, where possible.

If anyone could give me any advice on where might work to hook into AR to change generated SQL for ‘update’, I would appreciate it! And I hope to look into making a plugin to let AR do specified-key-only updates to postgres jsonb columns. Thank you for any advice!

It is now easy to store a hash serialized as jsonb in posgres. It is even pretty easy with store_accessor to make individual keys in the hash look like separate attributes.

But when AR decides the jsonb column needs to be updated, it will send the entire hash as an update value. If you only changed a key or two, it will still send the entire hash – possibly overwriting changes to other keys made by other processes/clients.

Recent versions of postgres supports an atomic update on just certain keys of a jsonb hash, using jsonb_set in SQL.

I’d like to explore trying to get AR to do this, at first with a plugin. With dirty tracking, it’s fairly straightforward for AR to figure out what top-level keys have been changed and what haven’t.

But I can’t quite figure out where to intervene in AR to change the SQL generated in an ‘update’ operation. So I can generate different SQL that will do an atomic update, where possible.

The most central spot would be in _update_record:

https://github.com/rails/rails/blob/263f01d93da118dc150c6ac816e70dcf10de2608/activerecord/lib/active_record/persistence.rb#L195

or in the related _substitute_values method.

This could be too late to see things like dirty tracking, though - you may need to start searching up the call stack from _update_record.

—Matt Jones

Is there any gem for this? For now I coded my own update_safely method which generates the proper SQL.

— Mario