How to Enable Optimistic Concurrency
-
By default, optimistic concurrency checks are disabled. Changes made outside of the session object will be overwritten. Concurrent changes to the same document will use the Last Write Wins strategy so a lost update anomaly is possible with the default configuration of the session.
-
Optimistic concurrency can be enabled for:
- A specific document
- A specific session (enable on a per-session basis)
- All sessions (enable globally, at the document store level)
-
With optimistic concurrency enabled, RavenDB will generate a concurrency exception (and abort all modifications in the current transaction) when trying to save a document that has been modified on the server side after the client loaded and modified it.
-
The
ConcurrencyException
that might be thrown upon thesaveChanges
call needs to be handled by the caller. The operation can be retried (the document needs to be reloaded since it got changed meanwhile) or handle the error in a way that is suitable in a given scenario. -
In this page:
-
Note that the
useOptimisticConcurrency
setting only applies to documents that have been modified by the current session. E.g., if you load documentsusers/1-A
andusers/2-A
in a session, make modifications only tousers/1-A
, and then callsaveChanges
, the operation will succeed regardless of the optimistic concurrency setting, even ifusers/2-A
has been changed by another process in the meantime. -
However, if you modify both documents and attempt to save changes with optimistic concurrency enabled, an exception will be raised if
users/2-A
has been modified externally.
In this case, the updates to bothusers/1-A
andusers/2-A
will be cancelled.
A detailed description of transactions and concurrency control in RavenDB is available here: Transaction support in RavenDB
Enable for specific session
// Enable optimistic concurrency for this session
const session = store.openSession();
session.advanced.useOptimisticConcurrency = true;
const product = new Product();
product.name = "Some Name";
// Save a document in this session
await session.store(product, "products/999");
await session.saveChanges();
{
// Modify the document 'externally' by another session
const anotherSession = store.openSession();
const otherProduct = await anotherSession.load("products/999");
otherProduct.name = "Other Name";
await anotherSession.saveChanges();
}
// Trying to modify the document without reloading it first will throw
product.name = "Better Name";
await session.saveChanges(); // This will throw a ConcurrencyException
-
Enabling optimistic concurrency in a session will ensure that changes made to a document will only be persisted if the version of the document sent in the
saveChanges()
call matches its version from the time it was initially read (loaded from the server). -
Note that it's necessary to enable optimistic concurrency for ALL sessions that modify the documents for which you want to guarantee that no writes will be silently discarded. If optimistic concurrency is enabled in some sessions but not in others, and they modify the same documents, the risk of the lost update anomaly still exists.
Enable globally
-
Optimistic concurrency can also be enabled for all sessions that are opened under a document store.
-
Use the store.Conventions.UseOptimisticConcurrency convention to enable globally.
// Enable for all sessions that will be opened within this document store
store.conventions.useOptimisticConcurrency = true;
{
const session = store.openSession();
const isSessionUsingOptimisticConcurrency
= session.advanced.useOptimisticConcurrency; // true
}
Disable for specific document (when enabled on session)
-
Optimistic concurrency can be disabled when storing a specific document,
even when it is enabled for an entire session (or globally). -
This is done by passing
null
as a change vector value to the store method.
{
const session = store.openSession();
const product = new Product();
product.name = "Some Name";
// Store document 'products/999'
await session.store(product, "products/999");
await session.saveChanges();
}
{
const session = store.openSession();
// Enable optimistic concurrency for the session
session.advanced.useOptimisticConcurrency = true;
const product = new Product();
product.name = "Some Other Name";
// Store the same document
// Pass 'null' as the changeVector to turn OFF optimistic concurrency for this document
await session.store(product, "products/999", { "changeVector": null });
// This will NOT throw a ConcurrencyException, and the document will be saved
await session.saveChanges();
}
Enable for specific document (when disabled on session)
-
Optimistic concurrency can be enabled when storing a specific document,
even when it is disabled for an entire session (or globally). -
This is done by passing
string.Empty
as the change vector value to the store method.
Setting the change vector to an empty string will cause RavenDB to ensure that this document is a new one and doesn't already exist. AConcurrencyException
will be thrown if the document already exists. -
If you do not provide a change vector or if the change vector is
null
, optimistic concurrency will be disabled. -
Setting optimistic concurrency for a specific document overrides the
useOptimisticConcurrency
property from theadvanced
session operations.
{
const session = store.openSession();
const product = new Product();
product.name = "Some Name";
// Store document 'products/999'
await session.store(product, "products/999");
await session.saveChanges();
}
{
const session = store.openSession();
// Disable optimistic concurrency for the session
session.advanced.useOptimisticConcurrency = false; // This is also the default value
const product = new Product();
product.name = "Some Other Name";
// Store the same document
// Pass an empty string as the changeVector to turn ON optimistic concurrency for this document
await session.store(product, "products/999", { "changeVector": "" });
// This will throw a ConcurrencyException, and the document will NOT be saved
await session.saveChanges();
}