RedisStore

class RedisStore(connection: StatefulRedisConnection<ByteArray, ByteArray>, indexName: String = "kmemo-idx", keyPrefix: String = "kmemo:", ttl: Duration? = null, clock: Clock = Clock.systemUTC(), closeConnection: Boolean = false) : CacheStore, AutoCloseable

A CacheStore backed by Redis with the RediSearch module, for a cache shared across processes.

kmemo splits responsibilities so this adapter stays thin: the dev.kmemo.SemanticCache owns whether a candidate is good enough to serve (threshold, guards, verification); this store only keeps vectors and returns the nearest k in a scope. It therefore reimplements no match logic — the nearest-neighbour search is Redis's FT.SEARCH ... KNN, on an exact FLAT index so results match the conformance suite's ordering.

Layout. Each entry is a Redis hash at "<keyPrefix><id>". Scope is a TAG field, the embedding a FLOAT32 VECTOR field, and expiry a numeric expiresAt (epoch millis) field. A single RediSearch index (created lazily on the first put, once the embedding dimension is known) covers all of them.

Expiry. TTL is enforced two ways that agree in production and stay testable: every read filters on expiresAt > now(clock), so expiry follows the injected clock deterministically; and a real Redis key TTL is set for reclamation, so dead keys do not accumulate. An entry with no TTL stores expiresAt = Long.MAX_VALUE.

Requirements. Redis with the RediSearch module (e.g. redis/redis-stack). If the module is absent, the first put fails fast with a clear message rather than silently degrading to no search.

Parameters

connection

an open Lettuce connection with a byte-array codec, speaking RESP2 (this store parses FT.SEARCH replies as classic RESP2 arrays; RESP3 returns a map it does not decode). The RedisClient secondary constructor handles both for you — prefer it.

indexName

RediSearch index name; give each logical cache its own.

keyPrefix

prefix for this cache's hash keys; the index only sees keys under it.

ttl

how long an entry stays valid, or null to keep it until removed.

clock

time source; substitute a fixed clock in tests instead of sleeping.

closeConnection

whether close should also close connection.

Constructors

Link copied to clipboard
constructor(connection: StatefulRedisConnection<ByteArray, ByteArray>, indexName: String = "kmemo-idx", keyPrefix: String = "kmemo:", ttl: Duration? = null, clock: Clock = Clock.systemUTC(), closeConnection: Boolean = false)
constructor(client: RedisClient, indexName: String = "kmemo-idx", keyPrefix: String = "kmemo:", ttl: Duration? = null, clock: Clock = Clock.systemUTC())

Connects to client with a byte-array codec, pinning RESP2 (RediSearch replies are parsed as RESP2 arrays), and owns the connection (closed by close).

Functions

Link copied to clipboard
open suspend override fun clear(scope: String?)
Link copied to clipboard
open override fun close()
Link copied to clipboard
open suspend override fun put(entry: CacheEntry)
Link copied to clipboard
open suspend override fun remove(id: String): Boolean
Link copied to clipboard
open suspend override fun search(scope: String, embedding: FloatArray, limit: Int): List<ScoredEntry>
Link copied to clipboard
open suspend override fun size(scope: String?): Int
Link copied to clipboard
open suspend fun touch(id: String)