Clustered Counters with Embedded Infinispan
What You Will Learn
How to configure and use Infinispan clustered counters in embedded mode, including strong bounded counters, strong unbounded counters, and weak counters.
Prerequisites
-
Java 17+
Step 1: Add the Counter Dependency
Add the Infinispan clustered counter module to your pom.xml:
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-clustered-counter</artifactId>
</dependency>
Step 2: Define Counters in the Global Configuration
Use CounterManagerConfigurationBuilder to define counters when building the global configuration:
// Setup up a clustered cache manager
GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder();
// Create the counter configuration builder
CounterManagerConfigurationBuilder builder = global.addModule(CounterManagerConfigurationBuilder.class);
// Create 3 counters.
// The first counter is bounded to 10 (upper-bound).
builder.addStrongCounter().name("counter-1").upperBound(10).initialValue(1);
// The second counter is unbounded
builder.addStrongCounter().name("counter-2").initialValue(2);
// And finally, the third counter is a weak counter.
builder.addWeakCounter().name("counter-3").initialValue(3);
// Initialize the cache manager
cm1 = new DefaultCacheManager(global.build());
// Retrieve the CounterManager from the CacheManager. Each CacheManager has it own CounterManager
counterManager = EmbeddedCounterManagerFactory.asCounterManager(cm1);
Step 3: Use Strong Counters
Strong counters provide consistent values during increment and decrement operations. All methods return CompletableFuture:
// StrongCounter provides the higher consistency. Its value is known during the increment/decrement and it may be bounded.
// Bounded counters are aimed for uses cases where a limit is needed.
counter1 = counterManager.getStrongCounter("counter-1");
// All methods return a CompletableFuture. So you can do other work while the counter value is being computed.
counter1.getValue().thenAccept(value -> System.out.println("Counter-1 initial value is " + value)).get();
// Try to add more than the upper-bound
counter1.addAndGet(10).handle((value, throwable) -> {
// Value is null since the counter is bounded and we can add 10 to it.
System.out.println("Counter-1 Exception is " + throwable.getMessage());
return 0;
}).get();
// Check the counter value. It should be the upper-bound (10)
counter1.getValue().thenAccept(value -> System.out.println("Counter-1 value is " + value)).get();
//Decrement the value. Should be 9.
counter1.decrementAndGet().handle((value, throwable) -> {
// No exception this time.
System.out.println("Counter-1 new value is " + value);
return value;
}).get();
// Similar to counter-1, counter-2 is a strong counter but it is unbounded. It will never throw the CounterOutOfBoundsException
counter2 = counterManager.getStrongCounter("counter-2");
// All counters allow a listener to be registered.
// The handle can be used to remove the listener
counter2.addListener(event -> System.out
.println("Counter-2 event: oldValue=" + event.getOldValue() + " newValue=" + event.getNewValue()));
// Adding MAX_VALUE won't throws an exception. But the all the increments won't have any effect since we can store
//any value larger 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 counter to its initial value (2)
counter2.reset().get();
counter2.getValue().thenAccept(value -> System.out.println("Counter-2 initial value is " + value)).get();
Step 4: Use Weak Counters
Weak counters are optimized for speed. Their value is computed lazily:
// Retrieve counter-3
counter3 = counterManager.getWeakCounter("counter-3");
// Weak counter doesn't have its value available during updates. This makes the increment faster than the StrongCounter
// Its value is computed lazily and stored locally.
// Its main use case is for uses-case where faster increments are needed.
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());
Step 5: Run the Tutorial
mvn package exec:java
The output shows counter operations including bounded overflow handling, compare-and-set, reset, and weak counter updates.
What’s Next
-
Try cluster execution for running tasks across nodes
-
Explore clustered locks for distributed coordination
-
Use distributed caching for data partitioning


