Skip to content

Implement AtomicKvStore in RedisKvStore #1365

Description

@dahlia

Motivation

An application already using RedisKvStore should be able to adopt a feature that needs atomic multi-key state without constructing another backend-specific store. The shared contract is useful for general application state as well as delivery suspension and durable backlog. This issue makes the existing @fedify/redis adapter a usable AtomicKvStore in its documented support scope.

Proposed API and use

Implement AtomicKvStore on RedisKvStore. Keep its existing constructor and ordinary KV methods. Add the proposed signatures below, with the supporting entry types defined by the contract issue:

getMany<T = unknown>(keys: readonly KvKey[]): Promise<(T | undefined)[]>;
setMany(entries: readonly KvStoreSetEntry[]): Promise<void>;
deleteMany(keys: readonly KvKey[]): Promise<void>;
casMany(entries: readonly KvStoreCasEntry[]): Promise<boolean>;

Keep cas() and make all five methods meet the shared contract. A proposed application use is:

import type { AtomicKvStore } from "@fedify/fedify";
import Redis from "ioredis";
import { RedisKvStore } from "@fedify/redis";

const redis = new Redis(); // Standalone example.
const store: AtomicKvStore = new RedisKvStore(redis);

const keys = [["example", "notice"], ["example", "budget"]] as const;
const [notice, budget] = await store.getMany(keys);
const committed = await store.casMany([
  { key: keys[0], expectedValue: notice, newValue: nextNotice },
  { key: keys[1], expectedValue: budget, newValue: nextBudget },
]);

The application computes nextNotice and nextBudget from the snapshot and re-evaluates its rule when the result is false. Connection configuration and client shutdown remain the application's responsibility. These calls are proposed additions, not currently shipped methods.

Backend implementation and choices

Implement real snapshot and conditional-batch semantics for supported Redis modes. Pre-encode values and validate keys, time to live (TTL) and batch limits before mutation. Preserve the codec contract: existing compare-and-swap (CAS) compares encoded bytes, and object property order can affect JSON encoding.

Use an appropriate bounded script or supported transaction strategy; neither pipelining nor the presence of MULTI proves rollback semantics. Redis transaction execution errors do not generally roll back earlier commands. Test the adapter's actual failure paths rather than assuming SQL behavior.

Resolve Redis Cluster atomic scope explicitly. Cluster multi-key operations require the relevant keys to share a slot; existing keys are not automatically placed in one slot. Preserve current single-key CAS and key encoding, and do not silently relocate existing data. If a hash-tag/prefix option or a new scoped key representation is needed, specify collision isolation and migration before implementing it. Cross-slot batches must not degrade to independent operations. Standalone support may ship first if Cluster multi-key support is explicitly excluded and tracked separately; do not list Cluster as a supported atomic configuration until it is verified.

A single global CAS object avoids cross-slot operations at the cost of contention and size, so it is not the default design. Validate a scoped Redis implementation instead.

Shipping criteria

Run the shared Atomic KV conformance suite and the Redis-specific concurrency/failure cases against a supported backend. Verify the package's declared Node, Deno and Bun support, its consumer declarations, and existing KV regression tests. Document unsupported batch scope and limits; do not silently weaken an atomic call or treat method presence as proof of durability.

Revise the RedisKvStore support row and custom-adapter contract in docs/manual/kv.md, packages/redis/README.md, public JSDoc, and relevant KV-selection skill guidance. Add the adapter's changelog fragment. Ship this adapter independently of other backend implementations and of delivery suspension.

Metadata

Metadata

Assignees

No one assigned

    Labels

    component/kvKey–value store relateddriver/redisRedis driver (@fedify/redis)

    Fields

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions