Repository navigation
Sessions: large databases' checkpoints in an object store (RESTATE_SQL_OBJECT_STORE) - #309
Conversation
…L_OBJECT_STORE) Upgrade restate-sdk-shared-core to 8.5.0, with its object store backends. Set RESTATE_SQL_OBJECT_STORE to `s3://bucket/path`, `gs://bucket/path`, `az://container/path` or `file:///a/directory`, and a database whose packs in Restate pass 4 MiB moves them to the object store in one upload. Credentials come from the provider's variables (`AWS_*`, `GOOGLE_*`, `AZURE_*`); MinIO or R2 is `s3://` with `AWS_ENDPOINT`. Unset, every pack stays in Restate, as before. The store is shared-core's `state::ObjectStoreL1`, made once per process; each namespace gets its own directory. Its requests are reqwest's, which need a tokio runtime, and a session's calls are polled on the asyncio loop's thread: the store's futures alone are polled inside pyo3-async-runtimes' runtime (`InRuntime`). Without it, the first request panics with "there is no reactor running". Crypto: `aws_lc_rs` instead of `rust_crypto`. The object stores' backends are aws-lc; with it, the tunnel's TLS and request identity use aws-lc too, and ring is no longer built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
ℹ️ No critical issues — one stale doc and one post-merge build to watch.
Reviewed changes
I reviewed the full PR: the shared-core 8.5.0 bump and crypto switch, the object store wiring in src/sessions.rs, and the new subprocess tests. I rebuilt the extension from this branch. tests/sessions_object_store.py passes (2/2), and so do tests/connect.py and tests/sessions_database.py (43/43).
- shared-core 8.5.0: compared with 8.4.0, it only adds
state/l1.rs(ObjectStoreL1,segment) and changes some docs inblobs.rs.format.rsis unchanged, so the head magic and state format are the same, and the bump can be rolled back. rust_crypto→aws_lc_rs:cargo tree --locked --target all -i ringprints nothing.ringis still listed inCargo.lock, but no target builds it.object_store(): the store is built once per process behind astd::sync::Mutex. It does not cache an error, so a bad value fails every open, the same waypage_cache()handles a bad size.InRuntime/in_runtime: every blob-store poll enters pyo3-async-runtimes' multi-thread runtime. Wake-ups from tokio worker threads reach the asyncio loop throughforeign_wakes, so a worker never waits for the GIL.- Tests: each scenario runs in a child process because the variable is read once per process. They cover promotion to a
file://store, a cold read from both tiers, a reader alongside a writer, garbage collection after a rewrite, and the error for a store the build can't serve.
ℹ️ docker.yml will build aws-lc for arm64 under QEMU right after merge
The PR's "Before release" section lists the release targets, but .github/workflows/docker.yml also runs on every push to main. It builds test-services/Dockerfile for linux/arm64,linux/amd64 through QEMU, and that uv build --wheel will now compile aws-lc as well. The Dockerfile already notes that the arm64 build "takes the better part of an hour" under QEMU. The first push to main will show whether aws-lc builds there and how much time it adds, unless docker.yml is dispatched manually on this branch before merging.
claude-opus-5-5 | 𝕏
The module docs and `TaskWaker`'s said the page cache's disk tier is the only other thread that wakes a call; with an object store, the tokio runtime driving its requests does too, through the same `foreign_wakes`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found. The stale-doc point from the last review is fixed.
Reviewed changes
I reviewed 722c841, which only changes doc comments in src/sessions.rs.
- Module docs ("Where a call runs"): the section now says two kinds of thread wake a call — the page cache's disk tier and, when an object store is configured, the tokio runtime behind
InRuntime. Both reach the asyncio loop throughforeign_wakes. TaskWakerdoc: it now names the object store's tokio runtime as a second thread that can wake a task.
From the last review, the docker.yml arm64-under-QEMU build still needs watching after merge. It isn't a problem in the code.
claude-opus-5-5 | 𝕏

Summary
Session objects can now keep a large database's checkpoints in an object store (S3, GCS, Azure, or a directory) instead of in Restate. This upgrades shared-core to 8.5.0, which adds
state::ObjectStoreL1(restatedev/sdk-shared-core#115). It is the same change sdk-sessions makes for Node.Once a database's packs in Restate pass 4 MiB (compressed), a checkpoint uploads them to the store as one pack. Restate keeps the head, the tail, the manifests and the last few MiB, so a database under 4 MiB never touches the bucket. Each database gets its own directory under the URL's path (
Ledger/ais_4cedger_2fa). Credentials come from the provider's own variables (AWS_*,GOOGLE_*,AZURE_*).Two rules:
RESTATE_SQL_OBJECT_STORE, or its opens fail.A value that names no store this build can serve fails every open with its message.
Changes
Cargo.toml: shared-core 8.5.0 withobject_store_aws,object_store_gcpandobject_store_azure. The crypto feature is nowaws_lc_rsinstead ofrust_crypto. The cloud backends need aws-lc, and with this switch the tunnel's TLS and request identity use aws-lc too, so ring is no longer built and we don't ship two crypto libraries.src/sessions.rs:object_store()builds the store once per process from the environment, andsql_opengives each database its own directory in it.InRuntime). Nothing else changes how it runs, the page cache included.tests/sessions_object_store.py: the variable is read once per process, so each scenario runs in a child process.Testing
just test-fast: 399 passed. 397 run without a store configured, which is the default path.just lint,just lint-rust, and pyright and mypy on the new test are clean.tests/connect.py: request identity verification now runs on aws-lc and passes. Its fake Cloud peer speaks plain HTTP, so no test covers a real TLS handshake.InRuntime, the first request panics withthere is no reactor running.Before release
docker.ymlbuildstest-services/Dockerfilefor linux/arm64 under QEMU on every push tomain, so after merge that build compiles aws-lc too. Its build stage already installsbuild-essential, so it should build, but under emulation it will take longer. Dispatchingdocker.ymlon this branch would check it first, but it also pushes atest-services-python:object-storageimage.🤖 Generated with Claude Code