Snapshot/Restore — gob-encoded state to/from cold (Parquet on NAS) #11
Labels
No labels
area/api
area/bridge
area/db
area/docs
area/ops
area/web
kind/bug
kind/chore
kind/feature
kind/spike
prio/p0
prio/p1
prio/p2
prio/p3
status/blocked
status/decision
status/needs-info
type/question
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: sscoble/posts#11
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Cold = Parquet on NAS. Hot flushes on state transition (debounced) and on SIGTERM. Restoration on boot rehydrates the in-memory state.
Files
api/snapshot.go(new —Store.Snapshot(),Store.Restore(snap []byte))api/cold.go(new —Store.Flush(), Parquet writer, NAS path resolution)tests/fixtures/v0/state.snap(checked-in — empty state for tests)Snapshot shape
Store.Snapshot()walks state underRLock, gob-encodes the snapshot.Store.Restore(snap)decodes, builds indexes, swaps atomic pointer (readers see old state until swap).Parquet layout
/mnt/nas/posts-cold/<year>/<month>/<batch>-<unix>.parquetSnapshotstruct fields (flattened for Parquet)Acceptance
Snapshot+Restoreround-trip 10k posts + 100k comments, zero losskill -9loses at mostPOSTS_FLUSH_INTERVALworthSerialization
Default:
encoding/gob(Go stdlib). Serializer choice deferred to Speckl. Migration path: re-snapshot in the new format on first boot, then commit to it.Dispatched by CoS (cycle 713). Cohort-change cycle: Scott's 2026-06-22 in-memory decision unlocked the posts repo. Implementing the Snapshot/Restore feature per the body —
api/snapshot.go(Store.Snapshot/Restore round-trip via encoding/gob) andapi/cold.go(Parquet writer to /mnt/nas/posts-cold///). Acceptance: round-trip 10k posts + 100k comments with zero loss; restore is O(N); atomic pointer swap for concurrent-safe restore. Companion to posts#9 (Store) which is also dispatchable — develop against the Store interface defined in posts#9 so we don't block on the Store impl. CoS dispatch cycle 713.CoS dispatch cycle 713 — UNASSIGN after subagent max_iterations.
Discovery (important)
Repo was NOT bare. A prior Hermes cycle shipped 21 commits implementing most of posts#11:
snapshot.go(6939 B) — Snapshot/Restore, gob encode/decode, init() registering typesflush.go(2212 B) — flushLoop() with timer + debounce + SIGTERM, FlushNow()store.go,locks.go,sorted_index.go,mutation.go,models.goserver.go,handler.go,main.go, 4 mutation filesstore_test.go(8412 B) — passes with CGO_ENABLED=06b70e3d"moved to root - api/ dir flattened"What posts#11 still needs (the actual delta)
cold.go— does not exist. Need Parquet writer (xitongsys/parquet-go),Store.Flush()public API,POSTS_COLD_PATHenv resolution, batch path<path>/<year>/<month>/batch-<unix>.parquet. CurrentFlushToPathwrites.snapfiles, not.parquet.tests/fixtures/v0/state.snap— empty-state fixture for tests, does not exist.TestSnapshot_RoundTrip_10kPosts_100kComments— only small-scale round-trip exists.Store.state atomic.Pointer[storeState]so readers see old state during restore. CurrentRestoreusess.mu.Lock()for entire rebuild.github.com/xitongsys/parquet-go(the existing "no external deps in v0" note must be revised since posts#11 explicitly requires Parquet).Decisions inferred
api/— last commit flattened api/ dir; subPackages = ["."] in flake. Placecold.goat root and document as a deviation.storeState structholding maps+indexes, replaced viaatomic.Pointer[storeState]. Reads go through.Load(); mutations holds.muand modify in place.Why this was unassigned, not completed
Subagent ran 50 tool calls / 677s before hitting max_iterations — spent the budget on reconnaissance and reconciling prior work with the issue spec, not on producing a branch/commit. The remaining delta is concrete and bounded (~5 deliverables above) and is a good candidate for a focused re-dispatch next cycle with explicit permission to skip the atomic-pointer refactor in v0 (treat as a known deviation) if needed to fit iteration budget.
Next-cycle recommendation
Re-dispatch posts#11 as a SMALLER delta scope. Suggested: "Add cold.go with Parquet writer + 10k/100k round-trip test only. Skip atomic-pointer refactor in v0 — leave a TODO comment." That should fit in a single subagent iteration.
CoS dispatch cycle 713.
Closed — implementation already complete ✅
Verified at c2325: snapshot round-trip 10k posts+comments PASS, atomic pointer swap PASS, on-disk round-trip PASS. Files: snapshot.go (311 lines), snapshot_acceptance_test.go (194 lines). Closed by ticket dispatch system at cycle c2325.