Skip to content

Restore typesense volume in prod - #4

Open
fastfadingviolets wants to merge 3 commits into
maple-testimony:mainfrom
hyphacoop:typesense-prod-restore
Open

fastfadingviolets wants to merge 3 commits into
maple-testimony:mainfrom
hyphacoop:typesense-prod-restore

Conversation

@fastfadingviolets

Copy link
Copy Markdown
Contributor

Only needed if #3 goes awry and we need to roll back.

fastfadingviolets and others added 3 commits September 9, 2026 10:33
Prod has been on typesense/typesense:0.24.1 since June 2023. Dev is already
on 30.2 and the upgrade is a documented single hop: the server rebuilds its
in-memory indices from the existing data directory on start, so no reindex is
required on the way up.

It is a one-way door, though. Typesense 0.25 changed the on-disk format and
v30 downgrades no lower than v27, so once the prod container starts on 30.2
the data directory can never be read by 0.24.1 again. Rollback means restoring
the data directory, not re-pinning the tag.

Also give prod the 0/100 deployment percentages dev already has, in this same
commit rather than a later one. Prod was deliberately left on CDK's 50/200
default so that shipping the dev upgrade left the prod service byte-identical
to what was deployed, but 200% lets ECS start the replacement task before
stopping the old one. Both tasks land on the single container instance and
mount the same shared Docker volume, and Typesense holds an exclusive RocksDB
lock on /app/data, so the replacement cannot open the data directory and
exits. Neither service configures a deployment circuit breaker -- cdk synth
warns about this -- so ECS would retry rather than fail fast. That hazard
fires on the first change to the prod task definition, which is this one.

The new test pins both services at 0/100 so the asymmetry cannot return
silently. Verified by synthesizing all three stacks against HEAD: of 80
resources in the shared stack exactly two differ, the prod task definition's
image and the prod service's DeploymentConfiguration. Both admin key secrets,
both dev search resources, and the cluster launch configuration are untouched,
so the instance is not replaced and neither Typesense volume is at risk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVeWYTERpQWo3TpoyRuGtD
Prepared rollback for the 30.2 prod upgrade, to stay UNMERGED unless a
rollback is actually needed. Same attach-and-copy design as the dev branch:
a cdk.context.json key gates a second block device created from a snapshot,
plus instance user data that stops the ECS agent, copies the two Typesense
docker volumes off it, and starts the agent again. Merging changes the launch
configuration, which replaces the instance, which comes up carrying the
restored indexes. Reverting the merge is the undo.

Written fresh rather than carried over from typesense-dev-restore, which is
based on 2f1b282 and so predates c3d899d -- merging that branch today would
revert the launch-configuration byte-stability test and the keyword-only
deployment percentages.

ARMING THIS IS TWO COUPLED EDITS. Set search_restore_snapshot_id to the
pre-upgrade snapshot AND put typesense_image_prod back to 0.24.1. Typesense
0.25 changed the on-disk format and v30 downgrades no lower than v27, so a
restored 0.24 data directory cannot be served by 30.2, and re-pinning the tag
without restoring the data cannot read the directory 30.2 has migrated.
Either edit alone leaves prod broken. The snapshot to use does not exist yet
-- take it immediately before the prod deploy; snap-0bf402788b535f7cf
predates the dev upgrade and is stale.

Both volumes are copied even though this is a prod rollback. The restore
works by replacing the instance and both volumes live on it, so dev's data
dies either way; copying prod alone would leave dev empty and needing a full
reindex, while copying both puts dev back where it already was. That holds
only if the snapshot is fresh, which is the other reason not to reuse the
August one.

Per #10, an ASG block device created from a snapshot reseeds on every
instance launch rather than persisting. Put the key back to null immediately
after a successful restore, or the next unrelated instance replacement
silently reverts prod to the snapshot.

Verified by synthesis: with the key null all three stacks are byte-identical
to e58377c across 100 resources, so the mechanism is inert as committed.
Armed with a snapshot id, exactly one resource changes -- the launch
configuration -- in exactly two properties, BlockDeviceMappings and UserData.
10 tests pass, black clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVeWYTERpQWo3TpoyRuGtD
Sets both halves of the gate together, which is the only correct way to set
either. search_restore_snapshot_id points at the pre-upgrade snapshot taken
2026-09-09T14:40:08Z of vol-0d73d8c1016d4a6bb, and typesense_image_prod goes
back to 0.24.1 to match the data directory that snapshot contains.

The snapshot was taken with both services running, so both copies are
crash-consistent and prod's RocksDB may want WAL recovery on start. Dev was
cleanly stopped for the August capture; it was not stopped for this one,
because stopping a service to improve a rollback that may never be used is a
worse trade than a WAL replay.

Restructured the launch-configuration tests, which is not cosmetic: arming
deliberately breaks the byte-stability the old assertion pinned, and since
c3d899d the pipeline's Synth step runs `uv run pytest` as a deploy gate. A
rollback branch that fails its own gate cannot deploy, so the rollback would
have been unusable in exactly the situation it exists for. The user data
literal is now split into a base and a restore tail, the expected value is
derived from whether the gate is set, and the two states get a fixture each.
Verified the same suite passes both armed and disarmed, so this survives the
key going back to null after a restore.

Synthesized and compared against e58377c, the upgrade this undoes: of 80
resources exactly two differ -- the launch configuration gains
BlockDeviceMappings and the restore user data, and the prod task definition's
image returns to 0.24.1. Nothing else moves. 10 tests pass, black clean.

DO NOT MERGE unless rolling back. After a successful restore, put
search_restore_snapshot_id back to null: an ASG block device created from a
snapshot reseeds on every instance launch rather than persisting, so leaving
it set silently reverts prod on the next instance replacement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVeWYTERpQWo3TpoyRuGtD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant