Este contenido no está disponible en el idioma seleccionado.
Chapter 14. Clustered Counters
Clustered counters are distributed and shared across nodes in a Red Hat JBoss Data Grid cluster. Clustered counters allow you to record the count of objects.
Clustered counters are identified by their names and are initialized with a value, which defaults to 0. Clustered counters can also be persisted so that the values are kept after cluster restarts.
There are two types of clustered counter:
-
Strong
stores the counter value in a single key for consistency. During updates to the counter, the value is known. Updates to the counter value are performed under the key lock. However, reads of the current value of the counter do not acquire any lock. Strong counters allow the counter value to be bounded and provide atomic operations such ascompareAndSet
orcompareAndSwap
. -
Weak
stores the counter value in multiple keys. Each key stores a partial state of the counter value and can be updated concurrently. During updates to the counter, the value is not known. Retrieving the counter value does not always return the current, up to date value.
Both strong and weak clustered counters support updating the counter value, return the current value of a counter, and provide events when a counter value is updated.
14.1. The Counter API
The counter
API consists of the following:
-
EmbeddedCounterManagerFactory
initializes a counter manager from an embedded cache manager. -
RemoteCounterManagerFactory
initializes a counter manager from a remote cache manager. -
CounterManager
provides methods to create, define, and return counters. -
StrongCounter
implements strong counters. This interface provides atomic updates for a counter. All operations are performed asynchronously and use theCompletableFuture
class for completion logic. -
SyncStrongCounter
implements synchronous strong counters. -
WeakCounter
implements weak counters. All operations are performed asynchronously and use theCompletableFuture
class for completion logic. -
SyncWeakCounter
implements synchronous weak counters. -
CounterListener
listens for changes to strong counters. -
CounterEvent
returns events when changes to strong counters occur. -
Handle
extends theCounterListener
interface.
14.2. Adding Maven Dependencies
To start using clustered counters, add the following dependency to pom.xml
:
pom.xml
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-clustered-counter</artifactId> <version>...</version> <!-- 7.2.0 or later --> </dependency>
14.3. Retrieving the CounterManager
Interface
To use clustered counters in Red Hat JBoss Data Grid embedded mode, do the following:
// Create or obtain an EmbeddedCacheManager. EmbeddedCacheManager manager = ...; // Retrieve the CounterManager interface. CounterManager counterManager = EmbeddedCounterManagerFactory.asCounterManager(manager);
To use clustered counters with a Hot Rod client that interacts with a Red Hat JBoss Data Grid remote server, do the following:
// Create or obtain a RemoteCacheManager. RemoteCacheManager manager = ...; // Retrieve the CounterManager interface. CounterManager counterManager = RemoteCounterManagerFactory.asCounterManager(manager);
14.4. Using Clustered Counters
You can define and configure clustered counters in the cache-container XML configuration or programmatically.
14.4.1. XML Configuration for Clustered Counters
The following XML snippet provides an example of a clustered counters configuration:
<?xml version="1.0" encoding="UTF-8"?> <infinispan> <cache-container> <!-- cache container configuration goes here --> <!-- cache configuration goes here --> <counters> <strong-counter name="counter-1" initial-value="1"> <upper-bound value="10"/> </strong-counter> <strong-counter name="counter-2" initial-value="2"/> <weak-counter name="counter-3" initial-value="3"/> </counters> </cache-container> </infinispan>
14.4.1.1. XML Definition
The counters
element configures counters for a cluster and has the following attributes:
-
num-owners
sets the number of copies of each counter to store across the cluster. A smaller number results in faster update operations but supports a lower number of server crashes. The value must be a positive number. The default value is2
. reliability
sets the counter update behavior in a network partition and takes the following values:-
AVAILABLE
all partitions can read and update the value of the counter. This is the default value. -
CONSISTENT
the primary partition can read and update the value of the counter. The remaining partitions can only read the value of the counter.
-
The strong-counter
element creates and defines a strong clustered counter. The weak-counter
element creates and defines a weak clustered counter. The following attributes are common to both elements:
-
initial-value
sets the initial value of the counter. The default value is0
. storage
configures how counter values are stored. This attribute determines if the counter values are saved after the cluster shuts down and restarts. This attribute takes the following values:-
VOLATILE
stores the value of the counter in memory. The value of the counter is discarded when the cluster shuts down. This is the default value. -
PERSISTENT
stores the value of the counter in a private, local persistence store. The value of the counter is saved when the cluster shuts down and restarts.
-
Attributes specific to the strong-counter
element are as follows:
-
lower-bound
sets the lower bound of a strong counter. The default value isLong.MIN_VALUE
. -
upper-bound
sets the upper bound of a strong counter. The default value isLong.MAX_VALUE
.
The value of the initial-value
attribute must be between the lower-bound
value and the upper-bound
value. If you do not specify a lower and upper bound for a strong counter, the counter is not bounded.
Attributes specific to the weak-counter
element are as follows:
-
concurrency-level
sets the maximum number of concurrent updates to the value of a counter. The value must be a positive number. The default value is16
.
14.4.2. Run-time Configuration of Clustered Counters
You can configure clustered counters on-demand at run-time after the EmbeddedCacheManager
is initialized, as in the following example:
CounterManager manager = ...; // Create three counters. // The first counter is a strong counter bounded to 10. manager.defineCounter("counter-1", CounterConfiguration.builder(CounterType.BOUNDED_STRONG).initialValue(1).upperBound(10).build()); // The second counter is an unbounded strong counter. manager.defineCounter("counter-2", CounterConfiguration.builder(CounterType.UNBOUNDED_STRONG).initialValue(2).build()); // The third counter is a weak counter. manager.defineCounter("counter-3", CounterConfiguration.builder(CounterType.WEAK).initialValue(3).build());
The defineCounter()
method returns true
if the counter is defined successfully or false
if not. If the counter configuration is not valid, a CounterConfigurationException
exception is thrown.
Use the isDefined()
method to determine if a counter is already defined, as in the following example:
CounterManager manager = ... if (!manager.isDefined("someCounter")) { manager.define("someCounter", ...); }
14.4.3. Programmatic Configuration of Clustered Counters
The following code sample illustrates how to configure clustered counters programmatically with the GlobalConfigurationBuilder
:
// Set up a clustered cache manager. GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder(); // Create a counter configuration builder. CounterManagerConfigurationBuilder builder = global.addModule(CounterManagerConfigurationBuilder.class); // Create three counters. // The first counter is a strong counter bounded to 10. builder.addStrongCounter().name("counter-1").upperBound(10).initialValue(1); // The second counter is an unbounded strong counter. builder.addStrongCounter().name("counter-2").initialValue(2); // The third counter is a weak counter. builder.addWeakCounter().name("counter-3").initialValue(3); // Initialize a new default cache manager. DefaultCacheManager cacheManager = new DefaultCacheManager(global.build());
14.4.3.1. Using Clustered Counters
The following code example illustrates how you can use clustered counters that you create and define programmatically:
// Retrieve the CounterManager interface from the cache manager. CounterManager counterManager = EmbeddedCounterManagerFactory.asCounterManager(cacheManager); // Strong counters provide greater consistency than weak counters. // The value of a strong counter is known during an increment or decrement operation. // The value of a strong counter can also be bounded in cases where a limit is required. StrongCounter counter1 = counterManager.getStrongCounter("counter-1"); // All methods are asynchronous and return CompletableFuture objects so that you can perform other operations while the counter value is computed. counter1.getValue().thenAccept(value -> System.out.println("Counter-1 initial value is " + value)).get(); // Attempt to add a value that exceeds the upper-bound value. counter1.addAndGet(10).handle((value, throwable) -> { // Value is null since the counter is bounded to a maximum of 10. System.out.println("Counter-1 Exception is " + throwable.getMessage()); return 0; }).get(); // Check the counter value. The value should be 10. counter1.getValue().thenAccept(value -> System.out.println("Counter-1 value is " + value)).get(); //Decrement the counter value. The new value should be 9. counter1.decrementAndGet().handle((value, throwable) -> { // No exception is thrown. System.out.println("Counter-1 new value is " + value); return value; }).get(); // The second counter, counter2, is a strong counter that is unbounded. It never throws the CounterOutOfBoundsException. StrongCounter counter2 = counterManager.getStrongCounter("counter-2"); // All counters allow a listener to be registered. // The handle interface can remove the listener. counter2.addListener(event -> System.out .println("Counter-2 event: oldValue=" + event.getOldValue() + " newValue=" + event.getNewValue())); // Adding MAX_VALUE does not throw an exception. // No increments take effect if the value exceeds the MAX_VALUE. counter2.addAndGet(Long.MAX_VALUE).thenAccept(aLong -> System.out.println("Counter-2 value is " + aLong)).get(); // Conditional operations are allowed in strong counters. counter2.compareAndSet(Long.MAX_VALUE, 0) .thenAccept(aBoolean -> System.out.println("Counter-2 CAS result is " + aBoolean)).get(); counter2.getValue().thenAccept(value -> System.out.println("Counter-2 value is " + value)).get(); // Reset the value of the second counter to its initial value. counter2.reset().get(); counter2.getValue().thenAccept(value -> System.out.println("Counter-2 initial value is " + value)).get(); // Retrieve the third counter, counter3. WeakCounter counter3 = counterManager.getWeakCounter("counter-3"); // The value of weak counters is not available during update operations. As a result these counters can increment faster than strong counters. // The counter value is computed lazily and stored locally. counter3.add(5).thenAccept(aVoid -> System.out.println("Adding 5 to counter-3 completed!")).get(); // Check the counter value. System.out.println("Counter-3 value is " + counter3.getValue()); // Stop the cache manager and release all resources. cacheManager.stop();