Admin CLI: check / rebuild-rollups / inspect-segment / verify-period / export-parquet#18
Merged
Conversation
…/ export-parquet
The usagedb binary doubles as an admin tool. The existing HTTP server
behavior moves under `usagedb serve` (still the default when no
subcommand is given, so prior invocations work unchanged).
Subcommands:
check [--deep]
Print manifest summary (generation, segment counts, watermark).
--deep additionally opens every segment file and verifies checksum
+ structural validity. Fails non-zero if any segment is corrupt.
rebuild-rollups --from <RFC3339> --to <RFC3339>
Drops rollup segments overlapping the range + rewinds the
watermark to `from`. Next server tick refills from raw segments.
inspect-segment <segment_id>
Prints SegmentMeta fields (bucket, time range, account/product/
meter/model sets, checksum, input_segment_ids for rollups) + first
5 rows as a sample.
verify-period --account <id> --from <RFC3339> --to <RFC3339>
Same logic as the /verify HTTP endpoint. Prints raw_total,
rollup_total, drift, and whether the period is sealed.
export-parquet <output.parquet>
Calls export_raw_segments() to write every raw segment to one
Parquet file with zstd compression.
All commands accept --db-root <path> (global, default ./data). They
operate on the on-disk state without requiring a running server.
Assumes the server is NOT running concurrently — file locking is on
the backlog.
Implementation:
- New module src/admin.rs with cmd_* async functions taking
AppState + args and returning Result<String>. Each command
formats its own output; the main binary just prints.
- open_state_for_admin() loads the manifest directly via
Manifest::load() — cheaper than full Recovery (no segment-scan
dedupe rebuild, no WAL replay) for one-shot commands.
- main.rs uses clap (derive macros) for arg parsing. The old
server logic moves into run_server() — same code path, just
behind a Command::Serve match arm.
Tests (tests/admin.rs, 10 tests):
- check_reports_manifest_summary
- check_deep_passes_for_valid_segments
- check_deep_fails_for_corrupt_segment (flips a byte mid-file)
- check_on_fresh_db_errors_with_clear_message
- inspect_segment_prints_meta_and_sample_rows
- inspect_segment_errors_for_unknown_id
- rebuild_rollups_drops_and_rewinds (tick → rebuild → verify state)
- rebuild_rollups_rejects_invalid_dates
- verify_period_reports_match_for_sealed_rollup
- export_parquet_writes_file
clap added as a regular dep (~1 MB).
Total tests: 106 (was 96; +10). Clean under RUSTFLAGS=-D warnings.
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
The
usagedbbinary doubles as an admin tool. The HTTP server behavior moves underusagedb serve(still the default when no subcommand is given, so prior invocations work unchanged).Subcommands
All commands accept
--db-root <path>(global, default./data) and operate on the on-disk state without needing a running server. Assumes the server is not running concurrently — file locking to prevent that is on the backlog.Implementation
src/admin.rs—cmd_*async functions takeAppState+ args and returnResult<String>. Each command formats its own output;main.rsjust prints.open_state_for_admin()— loads the manifest directly viaManifest::load()(no full Recovery, no segment-scan dedupe rebuild, no WAL replay). One-shot commands don't need the full startup cost.main.rsusesclapderive macros. The old server logic moves intorun_server()— same code path, just behind aCommand::Servematch arm.Tests
10 new tests in
tests/admin.rs, callingcmd_*directly (no subprocess):check_reports_manifest_summarycheck_deep_passes_for_valid_segmentscheck_deep_fails_for_corrupt_segment— flips a byte mid-segment-filecheck_on_fresh_db_errors_with_clear_messageinspect_segment_prints_meta_and_sample_rowsinspect_segment_errors_for_unknown_idrebuild_rollups_drops_and_rewinds— tick → rebuild → verify staterebuild_rollups_rejects_invalid_datesverify_period_reports_match_for_sealed_rollupexport_parquet_writes_fileSmoke test
Test plan
cargo build --all-targetsclean with-D warningscargo test --all-targets— 106 tests pass (was 96; +10)--help, all subcommands resolve)Dependency
clap = "4"withderivefeature, ~1 MB compiled.🤖 Generated with Claude Code