feat(storage): vectordb persistence + FTS5 opt-in + 24h search clamp#72
Merged
Merged
Conversation
Storage rebalance initiative: shrink hot-path disk footprint at 150 services
× 7 days while keeping triage latency intact. Five logically-distinct phases
implemented in one cohesive change because they share files and only make
sense together.
PR1 — FTS5 disable + reclaim + 24h clamp on log search
* SQLite FTS5 (logs_fts + porter+unicode61 + bm25 + sync triggers) is now
opt-in via LOG_FTS_ENABLED. Default is off. Saves 30-40% disk at scale
because the FTS table doubles every indexed log body.
* POST /api/admin/drop_fts: refuses while flag is on; otherwise drops the
virtual table + triggers and runs VACUUM to reclaim disk.
* search_logs MCP tool + /api/logs?q= clamped to last 24h via
storage.ClampSearchWindowTo24h to bound the LIKE-fallback worst case.
PR2 — vectordb snapshot wire format
* Versioned wire format: VDB1 magic + uint32 version + CRC32-IEEE +
gob payload. Version/magic mismatch or CRC failure rejects the file
so the loader can fall back cleanly to a full DB rebuild.
* Atomic write via tmp+sync+rename, mode 0o600. EXDEV fallback for
cross-device data dirs.
* LogVector.Vec exported for gob; Index gains lastIndexedID watermark.
PR3 — DB tail-replay
* vectordb.ReplaySource interface + ReplayRow struct (no storage import
in vectordb).
* Repository.LogsForVectorReplay: WHERE id > ? AND severity IN
(ERROR/WARN/...) ORDER BY id ASC, paged.
* Index.ReplayFromDB walks pages from LastIndexedID() so a snapshot
plus tail replay reconstructs the post-snapshot delta with no
double-indexing.
* main.go: vectorReplayAdapter projects storage.Log → vectordb.ReplayRow
so the two packages stay decoupled.
PR4 — periodic snapshot loop + shutdown hook
* Index.SnapshotLoop(ctx, path, interval): periodic ticks plus one final
write on ctx.Done so SIGTERM captures the maximum in-memory state.
* Shutdown step 3a: snapCancel + 5s wait on snapDone right after
graphRAG.Stop() so the final snapshot lands before DB close.
PR5 — observability + docs
* 5 metrics: otelcontext_vectordb_snapshot_writes_total{result},
snapshot_duration_seconds, snapshot_size_bytes,
snapshot_load_total{result}, replay_logs_total.
* Index.SetSnapshotObserver: wiring-layer hook so vectordb stays free of
telemetry imports. main.go wires metrics.RecordVectorSnapshotWrite
plus load/replay reporting.
* CLAUDE.md updated: storage-architecture row for vectordb persistence,
FTS5 reframed as opt-in with /api/admin/drop_fts reclaim, 24h clamp on
search_logs, new env vars (VECTOR_INDEX_SNAPSHOT_PATH,
VECTOR_INDEX_SNAPSHOT_INTERVAL, LOG_FTS_ENABLED).
Verification
* go vet ./... clean
* go test ./... — 516 pass across 27 packages
* go build . — 43.3 MB binary
Plan: docs/superpowers/specs/2026-04-30-storage-rebalance-plan.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
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.



Summary
Storage rebalance initiative to shrink hot-path disk footprint at 150 services × 7 days while keeping triage latency intact. Five logically distinct phases shipped together because they share files and only make sense as a unit.
logs_fts+ porter+unicode61 + bm25 + sync triggers) is now opt-in viaLOG_FTS_ENABLED. Default off — saves 30–40% disk at scale because the FTS table doubles every indexed log body. NewPOST /api/admin/drop_ftsreclaims the table + indexes (refused while the flag is on).search_logsMCP tool and/api/logs?q=clamped to last 24h viastorage.ClampSearchWindowTo24hto bound the LIKE-fallback worst case.VDB1magic +uint32version + CRC32-IEEE + gob payload. Magic/version mismatch or CRC failure rejects the file so the loader falls back cleanly to a full DB rebuild. Atomic write via tmp+sync+rename, mode0o600, EXDEV fallback for cross-device data dirs.LogVector.Vecexported for gob;IndexgainslastIndexedIDwatermark.vectordb.ReplaySource+ReplayRowkeep the package free ofstorageimports.Repository.LogsForVectorReplayisWHERE id > ? AND severity IN (...)ORDER BY id ASC, paged.Index.ReplayFromDBwalks pages fromLastIndexedID()so a snapshot plus tail replay reconstructs the post-snapshot delta with no double-indexing.main.gowires avectorReplayAdapterto projectstorage.Log → vectordb.ReplayRow.Index.SnapshotLoop(ctx, path, interval)ticks periodically and fires one final write onctx.Doneso SIGTERM captures the maximum in-memory state. Shutdown step 3a (snapCancel+ 5s wait onsnapDoneright aftergraphRAG.Stop()) lands the final snapshot before DB close.otelcontext_vectordb_snapshot_writes_total{result},snapshot_duration_seconds,snapshot_size_bytes,snapshot_load_total{result},replay_logs_total.Index.SetSnapshotObserveris the wiring-layer hook so vectordb stays free of telemetry imports.CLAUDE.mdrefreshed: storage architecture row for vectordb persistence, FTS5 reframed as opt-in, 24h clamp documented, three new env vars (VECTOR_INDEX_SNAPSHOT_PATH,VECTOR_INDEX_SNAPSHOT_INTERVAL,LOG_FTS_ENABLED).Plan and design notes:
docs/superpowers/specs/2026-04-30-storage-rebalance-plan.md.Test plan
go vet ./...— cleango test ./...— 516 pass / 27 packagesgo build .— 46 MB binary./otelcontextwithLOG_FTS_ENABLED=false(default) on an existing SQLite data dir; verify daemon starts,/api/logs?q=errreturns 400 when window straddles >24h ago, vectordb snapshot file appears atdata/vectordb.snapshotafter 5m🔍 Vector index: loaded snapshotlog +otelcontext_vectordb_snapshot_load_total{result="success"}incrementcurl -X POST http://localhost:8080/api/admin/drop_ftswhile flag is off — returns 200 withreclaimed_bytesbody; while flag is on — returns 405🤖 Generated with Claude Code