Valkey Datastore
Stores swamp runtime data in Valkey/Redis with sorted-set path indexing for O(log n + k) prefix lookups and SET NX distributed locking. Compatible with local Valkey, AWS ElastiCache Serverless, and AWS MemoryDB. Implements two-phase sync to minimize time under the global lock. Emits OpenTelemetry spans for Valkey commands, lock acquisition/release, and push/pull sync so datastore activity is visible in traces when a TracerProvider is configured.
2026.07.25.1
Added: OpenTelemetry spans for every layer of the datastore. Valkey round
trips emit one span each (Valkey SET, Valkey ZRANGEBYLEX,
Valkey GETBUFFER, Valkey EVAL, …) carrying db.system.name,
db.operation.name, and the key. Pipeline flushes emit
Valkey pipeline writeFiles / deleteFiles / fetchMetadata / fetchHashes
with the batched command count. The lock emits
valkey-datastore lock acquire / release / withLock / inspect /
forceRelease, with acquire recording wait duration and whether it contended.
The sync service emits valkey-datastore pullChanged / pushChanged /
hydrateFile / preparePush / commitPush with file counts, path counts, the
remote sequence number, and fast-path indicators.
Added: Lock contention retries are recorded as retry span events on the
acquire span.
Added: Pipeline spans report valkey.pipeline.failed_commands and are
marked as errors when any command in the batch failed. pipeline.exec()
resolves with per-command errors rather than rejecting, so a partially failed
batch would otherwise have left a span reporting success. pullChanged also
reports datastore.files_skipped for paths whose metadata read failed, which
were previously dropped silently.
Changed: pushChanged and commitPush now report
datastore.files_pushed and datastore.files_deleted separately. applyChanges
returns a single count covering writes and deletes together, and reporting that
as a file count would have overstated pushes whenever tombstones were involved.
The value returned to callers is unchanged.
Changed: Nothing else observable without tracing configured. The extension
depends on @opentelemetry/api only; the host process owns the
TracerProvider, and every span is a no-op when none is registered.
Note: Spans are placed on the round trips that carry latency — index range
scans, blob reads, and pipeline flushes — rather than on every one of the
seventeen command call sites. Heartbeat renewals are deliberately uninstrumented
so a long-held lock does not bury the trace in periodic PEXPIRE spans.
Note on secrets: Blob values are file content and the connection URL embeds a password. Neither is ever recorded; span attributes carry only command names, key names, and counts.
Stores swamp runtime data in Valkey/Redis with sorted-set path indexing
Config Fields
| Field | Type | Description |
|---|---|---|
| url | string | Valkey/Redis connection URL (redis:// or rediss:// for TLS) |
| prefix | string | Key namespace prefix for all swamp data |
| db | number | Redis database number |
| password? | string | Auth password (prefer vault expression for production use) |
| connectTimeoutMs | number | Connection timeout in milliseconds |
| maxRetriesPerRequest | number | Max retries per command before failing |
2026.07.21.1
Changed: Bumped ioredis from 5.6.1 to 5.11.1.
Upgrade note: No behavioral changes. Routine dependency maintenance.
2026.07.18.2
Changed: Version bump only, no code changes.
2026.07.18.1
Changed
- Pinned the
zodimport specifier tonpm:zod@4.4.3(wasnpm:zod@4) for hermetic dependency resolution. No runtime behavior change.
2026.07.05.5
Fixed
collectFullWalkDiffsilently discarded the truncated flag fromallPaths(), causing deletions beyond the 50k cap to be invisible. Now throws when truncated, refusing to run an incomplete diff.applyChangespipeline errors were silently swallowed —pipeline.exec()returns per-command[Error, result]tuples that were never inspected. Dirty state was cleared despite partial write failures. Now inspects every result tuple and throws with the list of failed paths.allPaths()truncation check used a separateZCARDafterZRANGEBYLEX(TOCTOU race). Replaced with LIMIT+1 pattern: fetch one extra entry and check length.createRedisClientTLS CA read: wrappedreadTextFileSyncwith contextual error message for missing CA files.
2026.07.05.4
Fixed
collectOneRelDiffstill used ZRANGEBYLEX prefix scan when a file was deleted locally (stat === nullfell through to the else branch). Inverted the branch: prefix scan only forstat?.isDirectory, ZSCORE point lookup for everything else (existing files and deleted files).collectOneRelDiffdirectory branch had no LIMIT clause — added 50k cap matchingallPaths/pathsForPrefixes.- Removed dead
deleted === "true"branch inpullFiles—applyChangeshard-deletes viaredis.del/zrem, so no meta withdeleted:trueis ever written.
2026.07.05.3
Fixed
- Lock
acquire()assigned nonce before the retry loop; ifredis.set()threw (e.g. ElastiCache failover), nonce was left set, permanently blocking re-acquire. Now uses a candidate variable and only promotes to nonce on success. pathsForPrefixesusedcharCode + 1inclusive upper-bound, which could match an adjacent path (e.g.data/foo0when scanningdata/foo/). Aligned to the 0xFF exclusive sentinel used incollectOneRelDiff.- Removed
RELEASE_NOTES.mdfrom manifestadditionalFilesper project policy. - Aligned
sidecar.tsisTraversal("")to returntrue, matchingmod.tsbehavior.
2026.07.05.2
Fixed
collectOneRelDiffused ZRANGEBYLEX prefix range for single-file dirty pushes, which over-fetched sibling paths (e.g. v1 matching v10, v11) and tombstoned them. Now usesZSCOREpoint lookup for files; range scan only for directories.- Verifier called
redis.connect()explicitly, which throws on an already-connected client after sync operations. Removed — ioredis lazy connect triggers on first command. allPaths()andpathsForPrefixes()were unbounded. Added 50k LIMIT clause and truncated flag.- Pinned zod to exact version (4.4.3) in deno.json.
Added
RELEASE_NOTES.mdfor CI publish workflow.- Regression test: verifier works after sync operation (double-connect).
2026.07.05.1
Added
- Valkey/Redis datastore extension for swamp
- Sorted-set path index (
ZRANGEBYLEX) for O(log n + k) prefix lookups - SET NX with Lua-guarded release for distributed locking
- Two-phase sync (
preparePush/commitPush) to minimize time under global lock - Sidecar dirty-path tracking with sequence-counter fast path for pull
- TLS support for AWS ElastiCache Serverless and MemoryDB
- Configurable key prefix, database number, connection timeout, and retry limits
2026.07.18.1
Changed
- Pinned the
zodimport specifier tonpm:zod@4.4.3(wasnpm:zod@4) for hermetic dependency resolution. No runtime behavior change.
2026.07.05.5
Fixed
collectFullWalkDiffsilently discarded the truncated flag fromallPaths(), causing deletions beyond the 50k cap to be invisible. Now throws when truncated, refusing to run an incomplete diff.applyChangespipeline errors were silently swallowed —pipeline.exec()returns per-command[Error, result]tuples that were never inspected. Dirty state was cleared despite partial write failures. Now inspects every result tuple and throws with the list of failed paths.allPaths()truncation check used a separateZCARDafterZRANGEBYLEX(TOCTOU race). Replaced with LIMIT+1 pattern: fetch one extra entry and check length.createRedisClientTLS CA read: wrappedreadTextFileSyncwith contextual error message for missing CA files.
2026.07.05.4
Fixed
collectOneRelDiffstill used ZRANGEBYLEX prefix scan when a file was deleted locally (stat === nullfell through to the else branch). Inverted the branch: prefix scan only forstat?.isDirectory, ZSCORE point lookup for everything else (existing files and deleted files).collectOneRelDiffdirectory branch had no LIMIT clause — added 50k cap matchingallPaths/pathsForPrefixes.- Removed dead
deleted === "true"branch inpullFiles—applyChangeshard-deletes viaredis.del/zrem, so no meta withdeleted:trueis ever written.
2026.07.05.3
Fixed
- Lock
acquire()assigned nonce before the retry loop; ifredis.set()threw (e.g. ElastiCache failover), nonce was left set, permanently blocking re-acquire. Now uses a candidate variable and only promotes to nonce on success. pathsForPrefixesusedcharCode + 1inclusive upper-bound, which could match an adjacent path (e.g.data/foo0when scanningdata/foo/). Aligned to the 0xFF exclusive sentinel used incollectOneRelDiff.- Removed
RELEASE_NOTES.mdfrom manifestadditionalFilesper project policy. - Aligned
sidecar.tsisTraversal("")to returntrue, matchingmod.tsbehavior.
2026.07.05.2
Fixed
collectOneRelDiffused ZRANGEBYLEX prefix range for single-file dirty pushes, which over-fetched sibling paths (e.g. v1 matching v10, v11) and tombstoned them. Now usesZSCOREpoint lookup for files; range scan only for directories.- Verifier called
redis.connect()explicitly, which throws on an already-connected client after sync operations. Removed — ioredis lazy connect triggers on first command. allPaths()andpathsForPrefixes()were unbounded. Added 50k LIMIT clause and truncated flag.- Pinned zod to exact version (4.4.3) in deno.json.
Added
RELEASE_NOTES.mdfor CI publish workflow.- Regression test: verifier works after sync operation (double-connect).
2026.07.05.1
Added
- Valkey/Redis datastore extension for swamp
- Sorted-set path index (
ZRANGEBYLEX) for O(log n + k) prefix lookups - SET NX with Lua-guarded release for distributed locking
- Two-phase sync (
preparePush/commitPush) to minimize time under global lock - Sidecar dirty-path tracking with sequence-counter fast path for pull
- TLS support for AWS ElastiCache Serverless and MemoryDB
- Configurable key prefix, database number, connection timeout, and retry limits
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned