Session: How to enable optimistic concurrency?

By default, optimistic concurrency checks are turned off, which means that changes made outside our session object will be overwritten. Checks can be turned on by setting UseOptimisticConcurrency property from Advanced session operations to true and may cause ConcurrencyExceptions to be thrown.

Another option is to control optimistic concurrency per specific document.
To enable it, you can supply an ETag to Store. If you don't supply an ETag or if the ETag is null, then optimistic concurrency will be disabled. If you need to check concurrency when inserting a new document you must use Etag.Empty. It will cause to throw ConcurrencyException if the document already exists. Note that setting optimistic concurrency per specific document overrides the use of UseOptimisticConcurrency property from Advanced session operations.

Example I

using (IDocumentSession session = store.OpenSession())
{
	session.Advanced.UseOptimisticConcurrency = true;

	Product product = new Product { Name = "Some Name" };

	session.Store(product, "products/999");
	session.SaveChanges();

	using (IDocumentSession otherSession = store.OpenSession())
	{
		Product otherProduct = otherSession.Load<Product>("products/999");
		otherProduct.Name = "Other Name";

		otherSession.SaveChanges();
	}

	product.Name = "Better Name";
	session.SaveChanges(); // will throw ConcurrencyException
}

The above example shows how to enable optimistic concurrency for a particular session. However that can be also turned on globally, that is for all opened sessions by using the convention DefaultUseOptimisticConcurrency.

Example II

store.Conventions.DefaultUseOptimisticConcurrency = true;

using (IDocumentSession session = store.OpenSession())
{
	bool isSessionUsingOptimisticConcurrency = session.Advanced.UseOptimisticConcurrency; // will return true
}

Remarks

Optimistic Concurrency and Replication

When Optimistic Concurrency is used with Replication then we advise to set ForceReadFromMaster when only read operations are load-balanced (FailoverBehavior.ReadFromAllServers) and you want to perform document update.

store.Conventions.DefaultUseOptimisticConcurrency = true;

using (DocumentSession session = (DocumentSession)store.OpenSession())
using (session.DatabaseCommands.ForceReadFromMaster())
{
    // In replicated setup where ONLY reads are load balanced (FailoverBehavior.ReadFromAllServers)
    // and optimistic concurrency checks are turned on
    // you must set 'ForceReadFromMaster' to get the appropriate ETag value for the document
    // when you want to perform document updates (writes)
    // because writes will go to the master server and ETag values between servers are not synchronized

    Product product = session.Load<Product>("products/999");
    product.Name = "New Name";

    session.SaveChanges();
}