[FEAT] Valkey adapters for the state ports, the counters, and the Durable boot arm - #454
Open
justin13888 wants to merge 5 commits into
Open
Conversation
…t/valkey-adapters-403
`redis` 1.2.2 (the version the workspace declares; the caret pin resolved to 1.6.0 and is locked back with `--precise`) with `tokio-rustls-comp` so a `rediss://` URL terminates TLS in rustls, and `script` for the Lua scripts every multi-key mutation becomes. `testcontainers` and `testcontainers-modules` (`valkey`) as dev-dependencies for the env-gated live suite. No `bb8`: one multiplexed `ConnectionManager` is the whole of what a server talking to one Valkey needs. The Volatile state row in design/dependencies.md records the scope, the primitives and the two rejections (a pool, Redis Cluster). Refs #403
One adapter per port in `store::valkey` — sessions, upload sessions, the three ceremonies and the device-cohort map — and `counter::valkey` for the counter port, all over one multiplexed, self-reconnecting `ConnectionManager`. Every multi-key mutation or decide-and-write is one Lua script (`EVALSHA`, `SCRIPT LOAD` on `NOSCRIPT`): the finalize claim, the challenge consume, the enrollment redeem and the counter hit cannot be read-then-written because there is no read a caller performs separately. Every derived index — the per-user and per-uploader sets, the per-album and pending-address sets, the global progress sorted-set — resolves each member through its record inside the script and drops a stale one, so an expired record leaves no listing entry behind without a second lifetime on the index. Expiry is decided by the injected `Clock`, written into each record as `expires_at`; `PEXPIRE` on the same key is only the collector. That is what lets the shared conformance suite drive this adapter with a manual clock exactly as it drives the in-memory double, one nanosecond either side of a boundary, with no sleeps. The counter port's cases move from `counter/tests.rs` into `counter::conformance`, generic over the store and with a racing case on a multi-threaded runtime; the in-memory adapter runs them case by case and in one pass. `tests/valkey.rs` runs both suites, a contested finalize claim and a contested counter hit against a `valkey/valkey` container (`CAPSULE_TEST_VALKEY=1`) or a running server (`CAPSULE_TEST_VALKEY_URL`), and passes as skipped otherwise; `.config/nextest.toml` places it in the one-thread `containers` group. Refs #403
`assemble` on `Backends::Durable` now connects to `VALKEY_URL`, proves it answers `PING`, and builds every Valkey store before anything else is assembled; a server that cannot be reached is `BootError::Valkey`, whose detail never carries the URL. The Postgres half is not written, so `durable` then refuses as `AdapterUnavailable` naming `DATABASE_URL` and in-memory doubles — #402 fills that one function's body. `.env.example`, the local-development page and the S-C29 slice row say what `serve` does with each variable now. Refs #403
Deploying capsule with
|
| Latest commit: |
a959cb3
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://aee3ba89.capsule-22k.pages.dev |
| Branch Preview URL: | https://feat-valkey-adapters-403.capsule-22k.pages.dev |
- A failure the driver cannot place before the command was sent — a response timeout, a connection dropped mid-flight — is `Rejected`, not `Unavailable`: the script may already have burned the challenge or won the claim, and the port defines `Unavailable` as "certainly did not happen". Only a refused connection and a server that declined to execute (`LOADING`, `TRYAGAIN`, `MASTERDOWN`, `CLUSTERDOWN`) stay `Unavailable`. - A reply of the wrong shape is `Corrupt` with the driver's error *kind* only: redis-rs quotes the offending value in a type error, and for the ceremony stores that value is the record carrying the bearer secret. - A derived index set's TTL is only ever raised (`extend` in Lua), so one member's remaining life cannot shorten another's; and re-opening an upload under a different uploader, owner, hash or album unindexes the previous record, as `OPEN_SESSION` already did for a previous user. - Listings report expired members (`debug`) apart from mismatched ones (`warn`), so the routine heal is not the drift signal. - The eviction query over-fetches one page so the members sharing the horizon's microsecond do not cost a candidate; `peek` on an unopened window answers `Admitted` for every limit, as the double does; the counter adapter reuses the store's `Lua` type. Refs #403
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Lane W-VALKEY of the unattended backlog run (issue #403), stacked on #435 (
feat/server-binary-config-operator-commands-401).Summary
capsule-server/src/store/valkey.rs:redis-rsadapters for all six state ports (AuthStateStore,UploadSessionStore,ChallengeStore,EnrollmentStore,ChannelStore,CohortStore) over one multiplexedConnectionManager, plus theValkeyStoresbundle. Every multi-key mutation or decide-and-write is one Lua script (EVALSHA,SCRIPT LOADonNOSCRIPT); every derived index heals on read; expiry is decided by the injectedClockand written into each record, withPEXPIREas the collector.capsule-server/src/counter/valkey.rs:ValkeyCounters, one hash and one script per key — open, charge or refuse in one server-side step.capsule-server/src/counter/conformance.rs: the counter port's suite, extracted fromcounter/tests.rsand generic over the store, with a racing case on a multi-threaded runtime; the in-memory adapter now runs it case by case and in one pass.capsule-server/tests/valkey.rs: the store and counter suites plus a contested finalize claim and a contested counter hit, against avalkey/valkeycontainer (CAPSULE_TEST_VALKEY=1) or a running server (CAPSULE_TEST_VALKEY_URL); skipped with one line otherwise..config/nextest.tomlputs it in the one-threadcontainersgroup.capsule-server/src/boot.rs: theDurablearm connects to andPINGsVALKEY_URLfirst (BootError::Valkeyon failure, never quoting the URL), then refuses namingDATABASE_URLand server: Postgres adapters and a conformance suite for every durable port #402 until the Postgres half lands..env.example,local-development.md,dependencies.md(Volatile state row) andSLICES.md(S-C29) say so.Validation
See the lane report; filled in as gates complete.
Risks and rollout
Backends::Memoryare untouched; the Valkey adapters are reached only throughBackends::Durable, which still refuses at the end of the boot path until server: Postgres adapters and a conformance suite for every durable port #402. Nothing persists a schema; every key is undercapsule:and carries a TTL except the cohort hash.CohortStoreon Valkey is interim (decision 3); the key has no TTL so the data migrates with oneHGETALLper account.Related Issues
Closes #403. Refs #402 (the Postgres half of the
Durablearm,DATABASE_URL).Decisions taken
Unresolved review notes