Interface KeyValueStore

All Superinterfaces:
AutoCloseable, Closeable
All Known Implementing Classes:
MemoryKeyValueStore, RedisKeyValueStore

public interface KeyValueStore extends Closeable

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 Classes
    Modifier and Type
    Interface
    Description
    static interface 
    Receives published messages.
    static interface 
    An active subscription; closing it stops the deliveries.
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    compareAndSet(String key, byte[] expected, byte[] value, long ttlMillis)
    Replaces the value under key only while it still equals expected (null: while there is none); answers whether it did.
    boolean
    Removes key; answers whether it existed.
    int
    Removes every key that starts with prefix; answers how many.
    byte[]
    get(String key)
    The value under key, or null when there is none or it expired.
    long
    increment(String key, long delta, long ttlMillis)
    Adds delta to the integer under key and answers the result; a missing key counts from zero and is given ttlMillis (zero or less: none).
    boolean
    Whether this store is shared with other processes.
    boolean
    Whether the store answers right now; what a health check asks.
    long
    publish(String channel, byte[] message)
    Sends message to everybody subscribed to channel, on every instance; answers how many subscribers received it (on Redis, how many connections).
    void
    set(String key, byte[] value, long ttlMillis)
    Stores value under key, replacing what was there.
    boolean
    setIfAbsent(String key, byte[] value, long ttlMillis)
    Stores value only if key holds nothing; answers whether it did.
    Calls listener for every message published to channel until the returned subscription is closed.
    long
    ttl(String key)
    How many milliseconds key has left: -1 when it never expires, -2 when it does not exist.

    Methods inherited from interface Closeable

    close
  • Method Details

    • get

      byte[] get(String key) throws IOException
      The value under key, or null when there is none or it expired.
      Throws:
      IOException
    • set

      void set(String key, byte[] value, long ttlMillis) throws IOException

      Stores value under key, replacing what was there.

      • ttlMillis: how long it lives; zero or less keeps it until it is deleted
      Throws:
      IOException
    • setIfAbsent

      boolean setIfAbsent(String key, byte[] value, long ttlMillis) throws IOException
      Stores value only if key holds nothing; answers whether it did. This is the primitive a lock is built on.
      Throws:
      IOException
    • compareAndSet

      boolean compareAndSet(String key, byte[] expected, byte[] value, long ttlMillis) throws IOException
      Replaces the value under key only while it still equals expected (null: while there is none); answers whether it did. A null value deletes the key instead. What an optimistic read-modify-write loops on.
      Throws:
      IOException
    • delete

      boolean delete(String key) throws IOException
      Removes key; answers whether it existed.
      Throws:
      IOException
    • deleteByPrefix

      int deleteByPrefix(String prefix) throws IOException
      Removes every key that starts with prefix; 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

      long increment(String key, long delta, long ttlMillis) throws IOException
      Adds delta to the integer under key and answers the result; a missing key counts from zero and is given ttlMillis (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

      long ttl(String key) throws IOException
      How many milliseconds key has left: -1 when it never expires, -2 when it does not exist.
      Throws:
      IOException
    • publish

      long publish(String channel, byte[] message) throws IOException
      Sends message to everybody subscribed to channel, 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

      Calls listener for every message published to channel until 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.
    • isShared

      boolean isShared()
      Whether this store is shared with other processes. A local store's locks and counters are only good within one process.