see on GitHub

Counters: Overview



Why use Counters?

  • Convenient counting mechanism
    Counters are very easy to manage, using simple API methods or through the Studio.
    E.g. Use counters when you want to -

    • Keep track of the number of times a document has been viewed or rated.
    • Count how many visitors from certain countries or regions read a document.
    • Continuously record the number of visitors on an event page.
    • Avoid having to update the whole document for just a numeric value change.
    • Have a need for a high-throughput counter (also see Distributed Values below).
  • Distributed Values
    A Counter's value is distributed between cluster nodes.
    Among the advantages of this:

    • The cluster remains available even when nodes crash.
    • Any node can provide or modify a Counter's value immediately, without checking or coordinating this with other nodes.
  • High performance, Low resources
    A document includes the Counter's name, while the Counter's actual value is kept in a separate location.
    Modifying a Counter's value doesn't require the modification of the document itself.
    This results in a performant and uncostly operation.

  • High-frequency counting
    Counters are especially useful when a very large number of counting operations is required,
    because of their speed and low resources usage.
    For example:

    • Use Counters for an online election page, to continuously update a Number-Of-Votes Counter for each candidate.
    • Continuously update Counters with the number of visitors in different sections of a big online store.

Overview

  • Design
    A document's metadata contains only the Counters' names-list for this document.
    Counter Values are not kept in the document's metadata, but in a separate location.

    • Therefore, changes like adding a new counter or deleting an existing counter trigger a document change,
      while simply modifying the Counter Value does not.
  • Cumulative Counter Actions

    • Counter value-modification actions are cumulative, the order in which they are executed doesn't matter.
      E.g., It doesn't matter if a Counter has been incremented by 2 and then by 7, or by 7 first and then by 2.
    • When a Counter is deleted, the sequence of Counter actions becomes non-cumulative and may require special attention.
  • Counters and conflicts

    • Counter actions (for either name or value) do not cause conflicts.
      • Counter actions can be executed concurrently or in any order, without causing a conflict.
      • You can successfully modify Counters while their document is being modified by a different client.
    • Counters actions can still be performed when their related documents are in a conflicted state.
  • Counters cost

    • Counters are designated to lower the cost of counting, but do come with a price.
      • All the names of a document's Counters are added to its content, increasing its size.
      • Counter values occupy storage space.
    • Be aware that the negligible amount of resources required by a few Counters, may become significant when there are many.
      A single document with thousands of Counters is probably an indication of a modeling mistake, for example.

  • Counter Naming Convention

  • Counter Value

    • Valid range: Signed 64-bit integer (-9223372036854775808 to 9223372036854775807)
    • Only integer additions are supported (no floats or other mathematical operations).
  • Number of Counters per document

    • RavenDB doesn't limit the number of Counters you can create.
    • Note that the Counter names are stored in the document metadata and impact the size of the document.
  • HasCounters Flag

    • When a Counter is added to a document, RavenDB automatically sets a HasCounters Flag in the document's metadata.
    • When all Counters are removed from a document, the server automatically removes this flag.

Managing Counters

Enabling the Counters Feature

  • Counters management is currently an experimental feature of RavenDB, and is disabled by default.

  • To enable this feature, follow these steps:

    • Open the RavenDB server folder, e.g. C:\Users\Dave\Downloads\RavenDB-4.1.1-windows-x64\Server
    • Open settings.json for editing.
    • Enable the Experimental Features -
      Verify that the json file contains the following line: "Features.Availability": "Experimental"
    • Save settings.json, and restart RavenDB Server.

Counter Methods and the countersFor object

Managing Counters is performed using the countersFor Session object.

  • Counter methods:

    • countersFor.increment: Increment the value of an existing Counter, or create a new Counter if it doesn't exist.
    • countersFor.delete: Delete a Counter.
    • countersFor.get: Get the current value of a Counter.
    • countersFor.getAll: Get all the Counters of a document and their values.
  • Usage Flow:

    • Open a session.
    • Create an instance of countersFor.
    • Use Counter methods to manage the document's Counters.
    • If you execute increment or delete, call session.saveChanges for the action to take effect on the server.
  • Success and Failure:

    • As long as the document exists, counter actions (increment, get, delete etc.) always succeed.
    • When a transaction that includes a Counter modification fails for any reason (e.g. a document concurrency conflict), the Counter modification is reverted.
  • countersFor Usage Samples
    You can use countersFor by explicitly passing it a document ID (without pre-loading the document).
    You can also use countersFor by passing it the document object.

    // Use CountersFor without loading a document
    
    // 1. Open a session
    try (IDocumentSession session = docStore.openSession()) {
        // 2. pass an explicit document ID to the countersFor constructor
        ISessionDocumentCounters documentCounters = session.countersFor("products/1-C");
    
        // 3. Use `countersFor` methods to manage the product document's Counters
        documentCounters.delete("productLikes");  // Delete the "productLikes" Counter
        documentCounters.increment("productModified", 15); // Add 15 to Counter "productModified"
        Long counter = documentCounters.get("daysLeftForSale");// Get "daysLeftForSale"'s value
    
        // 4. Save changes to the session
        session.saveChanges();
    }
    // Use countersFor by passing it a document object
    
    // 1. Open a session
    try (IDocumentSession session = docStore.openSession()) {
        // 2. Use the session to load a document.
        Product document = session.load(Product.class, "products/1-C");
    
        // 3. Create an instance of `countersFor`
        //   Pass the document object returned from session.load as a param.
        ISessionDocumentCounters documentCounters = session.countersFor(document);
    
        // 4. Use `countersFor` methods to manage the product document's Counters
        documentCounters.delete("productLikes"); // Delete the "productLikes" Counter
        documentCounters.increment("productModified", 15); // Add 15 to Counter "productModified"
        Long counter = documentCounters.get("daysLeftForSale");// Get value of "daysLeftForSale"
    
        // 5. Save the changes to the session
        session.saveChanges();
    }


Managing Counters using operations

  • In addition to working with the high-level Session, you can manage Counters using the low-level Operations.

  • CounterBatchOperation can operate on a set of Counters of different documents in a single request.