One of the key issues with no-SQL is the lack of in-built atomic transactions. This can make it difficult to manage state across multiple documents when required. To account for this, a transactional pattern can be built which is still efficient, but keeps document updates from being accessible until after the transaction has completed.
In short this is managed by versioning documents and linking each version to a transaction document. If the transaction document is not marked complete the previous versions of the document are queried in order until a completed document is found.
To ensure the consistency of documents in a transaction, while those documents are a part of a transaction they are also locked, preventing concurrent updates while allowing reads.
To make use of transactions a document must be declared as transactional. This is to ensure that the right artifact documents are created.
Once you have some documents which will be part of a transaction, we need to setup configuration for transactions This is done using a TransactionConfiguration object.
The first parameter is the key of the counter document for transactions.
The second parameter is a factory function for building transaction keys based on the counter value.
To use the transaction configuration, call the StartTransaction extension method and enclose it in a using block.
Any transactional documents that are stored within the using block will automatically become part of the transaction (including all the locking logic as above).
When transactional documents are not included as part of a transaction they will still use versioning, but will not include a transaction in the document definition.
When a transactional document is read, it will go backwards from the highest version, checking if the document has a transaction and if that transaction is completed (if it exists). If a completed transaction is found, or a document is found that was not part of a transaction when created, that version of the document will be read.