Counters
Id generator¶
Java implementation of Valkey or Redis based id generator RIdGenerator produces unique numbers that are not monotonically increasing.
It owes its speed to batch allocation. The first call reserves a block of numbers from Valkey or Redis in a single operation and caches it on the Java side; later calls are then served from that block locally, with no network round trip, until it is exhausted - at which point the next block is reserved. Because each client hands out numbers from its own reserved block concurrently, the ids are globally unique but interleave out of order rather than increasing monotonically. This makes nextId far faster than RAtomicLong, which performs a round trip per number and stays strictly sequential - the trade-off being ordering, and that a client which stops before using its whole block leaves those numbers unissued, so the sequence can jump forward.
Code examples:
Initialization
tryInit sets the start value and the block size, returning true if it initialized the generator and false if it was already initialized. Since it takes effect only once, it is safe to call from every node at startup - the first call wins and the rest are no-ops. If nextId is called without an explicit tryInit, the generator is created with the default start value of 0 and block size of 5000.
Choosing the allocation size
The allocation size sets how many numbers each client reserves per round trip, and is the main tuning knob. A larger block amortises the network cost over more numbers and raises throughput, but a client that restarts discards the unused tail of its current block, so the sequence can jump forward by up to that many numbers. A smaller block keeps those jumps small at the cost of more frequent round trips. Where gaps are unwelcome and strict ordering is required, RAtomicLong is the better fit.
Use Cases¶
RIdGenerator hands out unique Long numbers cheaply and without per-id coordination, which fits anywhere a system needs identifiers but not order: primary keys generated in the application, high-volume tags for events and jobs, and correlation ids across services. Where strictly increasing ids are required, RAtomicLong is the alternative.
Application-Generated Primary Keys
Generating an entity's key in the application - rather than relying on a database sequence or auto-increment column - means the id is known before the row is written, so it can be returned immediately, used to set foreign keys, or batched, without a database round trip per insert. nextId supplies a unique key that is safe to use across all instances.
High-Throughput Identifiers for Events and Jobs
Stamping events, log records, messages, or background jobs with a unique id at high volume is where batch allocation pays off: most nextId calls are served from the local block with no network round trip, so the generator sustains high rates. Non-monotonic order and occasional gaps are irrelevant when the id is only an opaque handle.
Correlation and Trace IDs Across Services
Assigning a unique id to each request or transaction so logs and spans can be correlated across services needs uniqueness, not ordering. Because ids come from per-client blocks, there is no single hot key that every request must contend on - the bottleneck a strictly sequential RAtomicLong would introduce.
AtomicLong¶
Java implementation of Valkey or Redis based AtomicLong object provides API similar to java.util.concurrent.atomic.AtomicLong object.
It holds a single 64-bit value shared by all clients. Every operation runs as an atomic command on Valkey or Redis and returns the up-to-date result, so reads and conditional updates stay strongly consistent. Where RLongAdder trades read cost for very fast writes, each operation here is a network round trip - which makes RAtomicLong the better choice when a value is read about as often as it is written, or when an operation needs the post-update result or a compare-and-set.
Code examples:
Increment, decrement, and add
Each step operation has two forms: incrementAndGet, decrementAndGet, and addAndGet apply the change and return the new value, while getAndIncrement, getAndDecrement, and getAndAdd return the value as it was before the change.
Compare-and-set and atomic swap
compareAndSet updates the value only if it still equals the expected value and returns whether it did - the building block for lock-free retry loops. getAndSet replaces the value and returns the previous one, and getAndDelete reads the value and removes the object in a single step.
Conditional update by comparison
setIfGreater writes the new value only when the current value is greater than the given comparison value, and setIfLess only when it is less. Both return whether the update happened, which makes them a convenient way to clamp a value to a bound in one atomic step.
RAtomicLong atomicLong = redisson.getAtomicLong("myAtomicLong");
// clamp to a ceiling: replace the value only if it is greater than 100
boolean cappedHigh = atomicLong.setIfGreater(100, 100);
// clamp to a floor: replace the value only if it is less than 0
boolean cappedLow = atomicLong.setIfLess(0, 0);
Use Cases¶
RAtomicLong is the right choice when a single shared integer must stay exact and immediately consistent across instances - every read and update is an atomic operation on the server. That suits sequence numbers, live up-and-down counters whose current value is read directly, and quota or guard logic built on compare-and-set.
Sequence and Ticket Numbers
incrementAndGet hands out a unique, monotonically increasing number on every call with no coordination between instances - useful for order or ticket numbers, batch ids, or any sequence where each consumer must receive a distinct value. Where the batched id generator above trades strict ordering for throughput, this returns each number in order.
Live Counters Read in Real Time
A value that moves up and down and is read directly - active sessions, in-flight requests, open connections - is incremented and decremented atomically, with get returning the exact current value at any moment. This is where RAtomicLong is preferred over RLongAdder, whose total is only cheap to read occasionally.
Quotas and Lock-Free Guards
compareAndSet gives lock-free coordination: a worker claims a one-time action by moving the value from an expected state to a new one only if no one else has, retrying if it changed. setIfGreater caps a counter at a quota in a single step.
RAtomicLong state = redisson.getAtomicLong("job:42:state");
// claim the job: only one worker moves it from 0 (pending) to 1 (running)
boolean claimed = state.compareAndSet(0, 1);
// keep a usage counter from exceeding its quota of 1000
RAtomicLong used = redisson.getAtomicLong("quota:user:7");
boolean capped = used.setIfGreater(1000, 1000);
AtomicDouble¶
Java implementation of Valkey or Redis based AtomicDouble object. It provides the same atomic operations as AtomicLong for double values.
As with RAtomicLong, the value is shared by all clients and every operation runs atomically on Valkey or Redis and returns the up-to-date result, so it stays strongly consistent. Each operation is a network round trip, so it suits values read about as often as written, or cases that need the post-update result or a compare-and-set - whereas RDoubleAdder instead optimises for very fast writes.
Code examples:
Increment, decrement, and add
Like RAtomicLong, the ...AndGet operations return the updated value and the getAnd... operations return the value from before the change.
RedissonReactiveClient redisson = redissonClient.reactive();
RAtomicDoubleReactive atomicDouble = redisson.getAtomicDouble("myAtomicDouble");
Mono<Double> updatedMono = atomicDouble.incrementAndGet();
Mono<Double> previousMono = atomicDouble.getAndAdd(5.5);
Mono<Double> currentMono = atomicDouble.decrementAndGet();
RedissonRxClient redisson = redissonClient.rxJava();
RAtomicDoubleRx atomicDouble = redisson.getAtomicDouble("myAtomicDouble");
Single<Double> updatedRx = atomicDouble.incrementAndGet();
Single<Double> previousRx = atomicDouble.getAndAdd(5.5);
Single<Double> currentRx = atomicDouble.decrementAndGet();
Compare-and-set and atomic swap
compareAndSet performs a lock-free conditional update, getAndSet swaps in a new value and returns the previous one, and getAndDelete reads and removes the value in a single step.
RedissonReactiveClient redisson = redissonClient.reactive();
RAtomicDoubleReactive atomicDouble = redisson.getAtomicDouble("myAtomicDouble");
Mono<Boolean> updatedMono = atomicDouble.compareAndSet(2.81, 10.5);
Mono<Double> previousMono = atomicDouble.getAndSet(20.5);
Mono<Double> lastMono = atomicDouble.getAndDelete();
RedissonRxClient redisson = redissonClient.rxJava();
RAtomicDoubleRx atomicDouble = redisson.getAtomicDouble("myAtomicDouble");
Single<Boolean> updatedRx = atomicDouble.compareAndSet(2.81, 10.5);
Single<Double> previousRx = atomicDouble.getAndSet(20.5);
Single<Double> lastRx = atomicDouble.getAndDelete();
Conditional update by comparison
setIfGreater and setIfLess write the new value only when the current value is respectively greater or less than the given bound - a one-step way to clamp a double to a ceiling or floor.
RAtomicDouble atomicDouble = redisson.getAtomicDouble("myAtomicDouble");
// clamp to a ceiling: replace the value only if it is greater than 100.0
boolean cappedHigh = atomicDouble.setIfGreater(100.0, 100.0);
// clamp to a floor: replace the value only if it is less than 0.0
boolean cappedLow = atomicDouble.setIfLess(0.0, 0.0);
Use Cases¶
RAtomicDouble suits a single shared fractional value that must stay exact and immediately consistent - a balance, a live measurement, an accruing total - where each read reflects every update so far and adjustments are applied atomically.
Live Balances and Measurements
A running balance or current reading checked on every access - an account balance, a wallet, a live load factor or price - is read with get and adjusted with addAndGet, so a credit or debit and the resulting balance are a single atomic step. Where RDoubleAdder is read only occasionally, here every read is exact and current.
Atomic Adjustments and Swaps
compareAndSet applies a change only if the value has not moved since it was read - safe for updating a shared price or rate without a lock - and getAndSet swaps in a fresh reading while returning the previous one, for instance when rotating a sampled value.
Clamping to a Ceiling or Floor
setIfGreater and setIfLess hold a value within bounds in one atomic step - capping a computed score, a load factor, or an accrued amount at a maximum, or lifting it to a minimum - without a read-then-write race.
LongAdder¶
Java implementation of Valkey or Redis based LongAdder object provides API similar to java.util.concurrent.atomic.LongAdder object.
Each client keeps its own internal LongAdder and updates it in memory, so add, increment, and decrement complete locally without a network round trip per call - up to 12000x faster than a comparable AtomicLong under contention. The cost of that speed is paid on reads: sum coordinates with every RLongAdder instance to accumulate their local values into a single total, and reset clears them all. That division - very cheap writes, a heavier and less frequent read - is what makes it a fit for distributed metric counters updated constantly across many nodes and read only occasionally.
Code examples:
Bounded coordination with a timeout
Because sum and reset gather responses from every instance over the network, their asynchronous variants accept an optional timeout that bounds how long to wait for those responses before completing.
Lifecycle
The object keeps state on the client side, so call destroy once it is no longer used to release it. This is not necessary if Redisson itself is shutting down.
Use Cases¶
LongAdder accumulates a 64-bit total on the client side and flushes it to Valkey or Redis, so increments and decrements stay contention-free and run far faster than AtomicLong under load - the trade-off being that sum() is the comparatively rare read. That makes it a fit for write-heavy counters spread across many instances.
High-Throughput Event Counting
Counting discrete events - requests served, messages processed, cache hits, errors - across a fleet of instances is dominated by increments, with the total read only occasionally. increment and add are cheap and lock-free on each node, while sum accumulates the cluster-wide total when a metrics scrape or dashboard needs it.
RLongAdder requests = redisson.getLongAdder("metrics:requests");
// hot path on every node - contention-free
requests.increment();
requests.add(batchSize);
// read the cluster-wide total occasionally, e.g. on a metrics scrape
long total = requests.sum();
Windowed Counters with Reset
For per-interval volume or rate counters, the adder accumulates over a window and is then read and cleared together: sum captures the window's total and reset returns the counter to zero for the next interval. Call destroy once the counter is no longer used.
RLongAdder perWindow = redisson.getLongAdder("metrics:requests:window");
perWindow.add(observed);
// at the end of the interval: capture the total, then start the next window
long windowTotal = perWindow.sum();
perWindow.reset();
// release client-side resources when the counter is retired
perWindow.destroy();
DoubleAdder¶
Java implementation of Valkey or Redis based DoubleAdder object provides API similar to java.util.concurrent.atomic.DoubleAdder object.
Each client keeps its own internal DoubleAdder and updates it in memory, so add, increment, and decrement complete locally without a network round trip per call - up to 12000x faster than a comparable AtomicDouble under contention. As with RLongAdder, the cost is paid on reads: sum coordinates with every RDoubleAdder instance to accumulate their local values, and reset clears them all. That makes it a fit for distributed metric counters that accumulate fractional amounts across many nodes and are read only occasionally.
Code examples:
Bounded coordination with a timeout
Like RLongAdder, the asynchronous sum and reset accept an optional timeout that bounds how long to wait for all instances to respond before completing.
Lifecycle
As with RLongAdder, call destroy once the counter is no longer used to release its client-side state. This is not necessary if Redisson itself is shutting down.
Use Cases¶
DoubleAdder is the floating-point counterpart: it accumulates a double total on the client side with the same contention-free, high-throughput add and decrement, reading the aggregate with sum. It fits write-heavy accumulation of fractional quantities across instances.
Aggregating Fractional Quantities
Summing non-integer measurements at a high rate - request latencies in seconds, bytes-per-second samples, sensor readings - across many instances suits a double accumulator. add records each measurement without contention, and sum reads the running total for reporting.
RDoubleAdder latency = redisson.getDoubleAdder("metrics:latency-seconds");
// record each observation, contention-free on every node
latency.add(requestSeconds);
// running total across all instances, read when reporting
double totalSeconds = latency.sum();
Running Monetary or Measurement Totals
Accumulating decimal amounts - revenue booked, cost accrued, kilograms processed - over a reporting window uses the same pattern: add each amount, then sum and reset to close the window and begin the next. Call destroy when the accumulator is retired.