A view of Tokyo, Skyscrapers of Shinjuku


  • By Artem V. Shamsutdinov
  • Oct 30th, 2020

Ability to support transactions has always been important with AIR but the API has been in flux until now. Here is the description of the final version of the API.

It's actually really simple - final API centers around the fact that all modification operations are now done in batches and have a well defined API for exactly which records are updated and how they are validated. This is described in an earlier post.

Transactional updates

Hence transactionality becomes completely seamless at modification time. Every modification operation is automatically transactional. All modifications contain any number of entities that represent an entity graph. A natural question comes: What if you want to transactionally modify a number of entities that are in two or more disjointed graphs?

I haven't gathered any data on this but it seems that persisting two disjointed graphs of data in a single transaction is an extreme edge case. After all, the whole point behind transactions is to persist related data in one shot. None-the-less, to cover this case you would have to come up with a tree that unifies both sub-trees into a single tree - something that is perfectly doable in all relational setups I can think of.

Transactional Reads

The other use of transactions is to read a set of related data in a single shot. Here the same approach applies - just retrieve all of the data that you need to be consistent in a single graph/tree query. Such retrievals are expected to be the norm with data that is open to modifications: an entity tree is retrieved, modified and saved back.

Note that if you adhere to the immutable data architecture (where no record is ever modified - modifications are represented as new records) then you don't have to worry about transactional reads at all. And in a setup where multiple applications may be trying to modify the same exact data (almost) at the same time (as is expected with AIR applications) not modifying records is expected to be the safest way to go. Though, the modification history of every record is still available and may be examined to determine what exactly happened.

Implementation reasons

Besides the simplicity of this approach, the main driver behind adopting this model are technical limitations that AIR runs into. Primary in that is the desire to eliminate any problems with multiple applications running against the same AIR database (local to the user's device).

If AIR is to allow applications to start transactions, perform queries and then commit transactions at some later time, then this could cause issues with other applications trying to make modifications to the same exact data (such as other apps having to wait until the write lock is released). These types of issues could even become a real problem if some applications (knowingly or not) become bad actors and lock frequently accessed records for prolonged periods of time. With the finally adopted approach this becomes a non-issue. All transactions are done on the database side and have a scope limited to the amount of data being modified/retrieved.

Finally there is a technical issue with WebSQL - the native database platform for the demo implementation of AIR (with apps running as iframes inside a web-page that has the AIR database running). WebSQL does not support long-running transactions - if the thread running the transaction suspends (to wait for a child frame to perform additional computations the transaction terminates). The final solution solves this issue as well.