Compare Exchange: Atomic Guards


  • Atomic guards are compare exchange key/value pairs that RavenDB creates and manages automatically to guarantee ACID transactions in cluster-wide sessions.

  • Atomic Guards coordinate work between sessions that try to write on a document at the same time. Saving a document is prevented if another session has incremented the Atomic Guard Raft index, which is triggered by changing the document.

  • Prior to the introduction of this feature (in RavenDB 5.2), client code had to administer compare-exchange entries explicitly. You can still do that if you wish by disabling the automatic usage of atomic guards in a session and defining and managing compare exchange key/value pairs yourself where needed.

    • We strongly recommend not managing atomic guards manually unless you truly know what you're doing. Doing so could cancel RavenDB's ACID transaction guarantees.
  • In this page:


When are Atomic Guards Created

  • Atomic Guards are created by default only when the session's transaction mode is set to ClusterWide:
    • New documents:
      A new Atomic Guard is created when successfully saving a new document in the cluster-wide session.
    • Existing documents that don't have Atomic Guards:
      A new Atomic Guard is created when modifying an existing document in the cluster-wide session.
  • If a document already has an associated Atomic Guard:
    Upon modifying a document in a new session, the value of its related atomic guard key will be modified, allowing RavenDB to detect that changes were made to this key..

Atomic Guard Transaction Scope

  • When working with a cluster-wide session, an Atomic Guard is created upon a successful creation of a document.

    • To set a session as cluster-wide, define its TransactionMode as ClusterWide when opening the session.
  • If the session's SaveChanges() fails, the entire session is rolled-back and the Atomic Guard is not created.

  • Atomic Guards are local to the cluster they were defined on.

  • Atomic Guards will not be replicated to another cluster in any ongoing replication task.
    We've designed them this way so that data ownership remains at the cluster level.

Syntax and Usage

In the code sample below, for example, an atomic guard is automatically created upon the creation of a new document, and then used when two sessions compete on changing the document.

using (var session = store.OpenAsyncSession
       (new SessionOptions
        // A cluster-wide session is opened.
        {TransactionMode = TransactionMode.ClusterWide}))
{
    await session.StoreAsync(new User(), "users/johndoe");
    await session.SaveChangesAsync();
    // An atomic guard is now automatically created for the new document "users/johndoe".
}

// Two cluster-wide sessions are opened.
using (var session1 = store.OpenAsyncSession(
       new SessionOptions 
       {TransactionMode = TransactionMode.ClusterWide}))
using (var session2 = store.OpenAsyncSession(
       new SessionOptions 
       {TransactionMode = TransactionMode.ClusterWide}))
{
    // Two sessions will load the same document at the same time.
    var loadedUser1 = await session1.LoadAsync<User>("users/johndoe");
    loadedUser1.Name = "jindoe";

    var loadedUser2 = await session2.LoadAsync<User>("users/johndoe");
    loadedUser2.Name = "jandoe";

    // Session1 will save changes first, which triggers a change in the 
    // version number of the associated Atomic Guard.
    await session1.SaveChangesAsync();

    // Session2.SaveChanges() will be rejected with a ConcurrencyException
    // since session1 already changed the Atomic Guard version.  
    // Session2 is expecting the version that it had when it loaded the document.
    await session2.SaveChangesAsync();
}

If you examine the compare exchange entries list after running the above sample, you'll see the atomic guard that was automatically created.
Atomic Guard

  • The generated Atomic Guard key name is composed of the prefix rvn-atomic and name of the document that it is associated with.

Disabling Atomic Guards

To disable the automatic usage of atomic guards in a session, set the session DisableAtomicDocumentWritesInClusterWideTransaction configuration option to true.

In the sample below, the session uses no atomic guards.

using (var session = store.OpenAsyncSession
       (new SessionOptions
        {TransactionMode = TransactionMode.ClusterWide,
           // no atomic guards will be used in this session
           DisableAtomicDocumentWritesInClusterWideTransaction = true}))
{
    await session.StoreAsync(new User(), "users/johndoe");
    await session.SaveChangesAsync();
}

Warning

* To enable ACIDity in cluster-wide transactions when atomic guards are disabled,
you have to explicitly set and use the required compare exchange key/value pairs.

  • Only disable and edit Atomic Guards if you truly know what you're doing as it can negatively impact ACID transaction guarantees.

  • Do not remove or edit atomic guards manually while a session is using them. A session that is currently using these removed atomic guards will not be able to save their related documents resulting in an error.

    • If you accidentally remove an active atomic guard that is associated with an existing document, recreate or save the document again in a cluster-wide session which will re-create the Atomic Guard.

When are Atomic Guards Removed

Atomic guards expire on the expiration of the documents they are used for and are automatically removed by a RavenDB cleanup task. You do not need to handle the cleanup yourself.

Since different cleanup tasks handle the removal of deleted and expired documents and the removal of expired atomic guards, it may happen that atomic guards of removed documents would linger in the compare exchange entries list a short while longer before they are removed.

  • You do not need to remove such atomic guards yourself, they will be removed by the cleanup task.
  • You can re-create expired documents whose atomic guards haven't been removed yet.
    RavenDB will create new atomic guards for such documents, and expire the old ones.