lance-graph-cognitive::container_bs::dn_redis is a key-shape protocol definition + a Rust-side command type model for HHTL-keyed hot lookups. It is NOT:
- A network-protocol implementation (no RESP encoder/parser, no listener, no command executor)
- A drop-in replacement that lets existing Redis clients talk to lance-graph
- A wire-protocol emulator (the FalkorDB / KùzuDB precedent does NOT apply here — those projects implement the Redis RESP protocol; dn_redis does not)
What dn_redis IS:
- A Rust API that exports key-construction helpers (
dn_key,spine_key,walk_to_root_keys,children_pattern,subtree_pattern) producingStringkeys likeada:dn:{hex}andada:spine:{hex} - A command type model (
RedisCommandenum +RedisPipelinestruct) that adopters can populate to describe the operations they need to execute - A serde layer for
CogRecordpayloads (cog_record_to_bytes,cog_record_from_bytes)
The module exports the SHAPE of the operations; adopters provide the EXECUTOR. There is no Backend trait, no listener, no connect() function, no execute() method on RedisPipeline — those are what consumer code must implement.
To use dn_redis, a consumer must:
- Decide on a backend (see below)
- Write an executor that takes
RedisPipeline(or individualRedisCommandvalues) and runs them against the chosen backend - Mount the executor at the call sites where the consumer's cognitive substrate needs hot-key lookups
This doc proposes formalizing two valid backend categories so adopters do not have to discover them by reverse-engineering the call sites.
A consumer can run a Redis server and pipe RedisCommands to it via any Redis client (redis-rs, fred, etc.). The consumer writes the executor: receive RedisCommand, translate to the client's API, return results.
Important caveat (per codex P2 review on PR #455): the documented walk_to_root operation uses MGET ada:dn:{ancestor1} ada:dn:{ancestor2} .... In a Redis Cluster deployment, MGET requires all keys to belong to the same hash slot — which means they must share a {hash_tag} substring per the Redis Cluster specification. The current key layout ada:dn:{hex} does NOT include a hash tag, so cluster MGET will return CROSSSLOT Keys in request don't hash to the same slot errors.
For Redis Cluster to be a valid backend, EITHER:
- (i) The key layout must be re-shaped to include a shared hash tag for keys that are co-queried (e.g.
ada:dn:{root_basin}:0102...so all descendants of a basin hash to one slot) - (ii) The consumer must split multi-key operations into per-key calls (defeats the pipeline)
- (iii) The consumer runs standalone Redis (not Cluster)
This caveat is the practical reason most consumers should treat dn_redis's key shape as "designed for standalone Redis OR for an in-binary executor", not for a clustered Redis deployment without re-shaping.
A consumer can implement an executor that takes the same RedisCommand shape and runs it against the local Lance dataset via DataFusion queries. The result is Redis-protocol-shaped responses (via the Rust types — not wire-protocol bytes) emitted from the consumer's own data, with no Redis service required.
What the consumer writes (per codex P2 #3 — there is no shipped trait to implement; this IS new consumer code):
// Consumer code, NOT lance-graph code. Pseudocode shape only — adopters
// implement the actual executor; types like `RedisValue` / `Error` /
// `self.lance` are illustrative.
struct LanceBackend { /* ... */ }
impl LanceBackend {
// async fn because read_by_dn / DataFusion queries are awaitable;
// earlier draft elided `async` which would have failed to compile
// (CodeRabbit critical on PR #455).
async fn execute(&self, cmd: RedisCommand) -> Result<RedisValue, Error> {
match cmd {
RedisCommand::Get(key) => {
let dn = self.parse_dn_from_key(&key)?;
let row = self.lance.read_by_dn(dn).await?;
Ok(RedisValue::Bulk(cog_record_to_bytes(&row)))
}
RedisCommand::Mget(keys) => { /* batch lookup over Lance */ }
RedisCommand::Keys(pattern) => { /* DataFusion scan filtered by prefix */ }
// ... etc per the enum's variants
}
}
}This is "Redis-shape over Lance" — the consumer projects Lance results through the command-result type. There is no protocol parsing because no wire protocol is involved; everything is in-process Rust types.
- It does not claim FalkorDB or KùzuDB use dn_redis. Those are independent products that implement the actual Redis RESP wire protocol; dn_redis does not implement RESP. The earlier version of this doc cited them as a precedent for "talk Redis without being Redis" — that framing was wrong. The honest framing is more constrained: dn_redis provides the key-shape and command-type contract that a consumer can EITHER pipe to a real Redis (standalone) OR execute in-binary against their own data.
- It does not document a network protocol. There is no listener, parser, or executor shipped by lance-graph for dn_redis. Calling it "wire-protocol emulation" (as a prior version of this doc did) misleads consumers about what is implemented vs what they must implement themselves.
- It does not claim Redis Cluster is plug-and-play. Per the caveat above, the current key shape is incompatible with cluster
MGETslot routing.
For single-binary deployments (e.g. AdaWorldAPI/bardioc/substrate-b) the natural backend is option (B): an in-binary executor over Lance. This means "zero application-level boundaries within substrate-b" (per the PR #452 / #454 append-only-Raft doc) survives the addition of HHTL-keyed hot lookups precisely because dn_redis is a TYPE MODEL, not a service dependency.
For consumers who already operate a standalone Redis (with no clustering or with cluster + re-keyed hash tags), option (A) is straightforward: write the executor that translates RedisCommand to a Redis client's API.
The earlier version of this doc framed dn_redis as wire-protocol emulation; codex review on PR #455 caught the inaccuracy. The corrected framing is more constrained but more honest: dn_redis is the SHAPE, the consumer provides the EXECUTION.
lance-graph-cognitive::container_bs::dn_redis— the key-shape protocol + command type modellab-vs-canonical-surface.md— the canonical-vs-lab discipline that frames adapters- Companion docs
APPEND_ONLY_RAFT_DOVETAIL.md+CLUSTER_ASYMMETRY.md(PR #452 / #453 / #454 — merged) AdaWorldAPI/bardiocPR #15 conversation thread (where this doc + the corrections originated)- Redis Cluster specification for the hash-slot constraint