Interface KeyValueStore
- All Superinterfaces:
AutoCloseable, Closeable
- All Known Implementing Classes:
MemoryKeyValueStore, RedisKeyValueStore
A key/value store that several server instances can share: the place a cache, the sessions, rate-limit counters, scheduler locks and pub/sub notifications live once an application runs more than one process.
Two implementations ship, chosen by configuration and never by code:
cn1.kv.url=memory: # the default: this process only
cn1.kv.url=redis://user:pw@host:6379/0 # Redis, Valkey or KeyDB
cn1.kv.url=rediss://host:6380 # the same over TLS
Spring Boot's spring.data.redis.url (and .host, .port, .password,
.database, .ssl.enabled) is read when cn1.kv.url is not set, so a
configuration written for a Spring application needs no change.
KeyValueStores.current() answers the calling thread's server's store.
Keys are strings and values are bytes; what a value means is the caller's. Every method may block on the network, and parks only its own virtual thread when it does.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic interfaceReceives published messages.static interfaceAn active subscription; closing it stops the deliveries. -
Method Summary
Modifier and TypeMethodDescriptionbooleancompareAndSet(String key, byte[] expected, byte[] value, long ttlMillis) Replaces the value underkeyonly while it still equalsexpected(null: while there is none); answers whether it did.booleanRemoveskey; answers whether it existed.intdeleteByPrefix(String prefix) Removes every key that starts withprefix; answers how many.byte[]The value underkey, or null when there is none or it expired.longAddsdeltato the integer underkeyand answers the result; a missing key counts from zero and is giventtlMillis(zero or less: none).booleanisShared()Whether this store is shared with other processes.booleanping()Whether the store answers right now; what a health check asks.longSendsmessageto everybody subscribed tochannel, on every instance; answers how many subscribers received it (on Redis, how many connections).voidStoresvalueunderkey, replacing what was there.booleansetIfAbsent(String key, byte[] value, long ttlMillis) Storesvalueonly ifkeyholds nothing; answers whether it did.subscribe(String channel, KeyValueStore.MessageListener listener) Callslistenerfor every message published tochanneluntil the returned subscription is closed.longHow many millisecondskeyhas left: -1 when it never expires, -2 when it does not exist.
-
Method Details
-
get
The value underkey, or null when there is none or it expired.- Throws:
IOException
-
set
Stores
valueunderkey, replacing what was there.ttlMillis: how long it lives; zero or less keeps it until it is deleted
- Throws:
IOException
-
setIfAbsent
Storesvalueonly ifkeyholds nothing; answers whether it did. This is the primitive a lock is built on.- Throws:
IOException
-
compareAndSet
Replaces the value underkeyonly while it still equalsexpected(null: while there is none); answers whether it did. A nullvaluedeletes the key instead. What an optimistic read-modify-write loops on.- Throws:
IOException
-
delete
Removeskey; answers whether it existed.- Throws:
IOException
-
deleteByPrefix
Removes every key that starts withprefix; answers how many. Walks the key space incrementally on a shared server rather than blocking it, so keys written while it runs may survive.- Throws:
IOException
-
increment
Addsdeltato the integer underkeyand answers the result; a missing key counts from zero and is giventtlMillis(zero or less: none). The expiry of an existing key is left as it is, so a fixed window counts from its first request.- Throws:
IOException
-
ttl
How many millisecondskeyhas left: -1 when it never expires, -2 when it does not exist.- Throws:
IOException
-
publish
Sendsmessageto everybody subscribed tochannel, on every instance; answers how many subscribers received it (on Redis, how many connections). Delivery is at most once: a subscriber that is not connected at that moment never sees it.- Throws:
IOException
-
subscribe
KeyValueStore.Subscription subscribe(String channel, KeyValueStore.MessageListener listener) throws IOException Callslistenerfor every message published tochanneluntil the returned subscription is closed. The listener runs on the store's own receiving thread, one message at a time; it should hand slow work off.- Throws:
IOException
-
ping
boolean ping()Whether the store answers right now; what a health check asks.
-