Skip to content

fix(storage): bound the cold Codex log inspection and surface when it is skipped (#2605) - #2627

Merged
lidge-jun merged 2 commits into
devfrom
codex/2605-storage-cold-scan-gui
Aug 25, 2026
Merged

fix(storage): bound the cold Codex log inspection and surface when it is skipped (#2605)#2627
lidge-jun merged 2 commits into
devfrom
codex/2605-storage-cold-scan-gui

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #2605. The first Storage inspection runs count(*), two GROUP BY queries and sum(estimated_bytes) synchronously on the proxy thread. The reporter measured GROUP BY level at ~17.3s on a ~1 GB logs_2.sqlite; the source documents a 49s case. Both storage endpoints call the inspection inline, so routing and /healthz stall behind it. Memoization does not help — an active Codex process changes the WAL and invalidates the entry, which the existing comment says outright: "This bounds the repeat cost, not the first one."

Databases above 64 MiB now skip the row aggregates and keep file sizes, schema validation and capabilities. metrics: null plus metricsSkipped: { reason, thresholdBytes } makes "not scanned" distinguishable from "genuinely zero".

A Worker was considered and rejected: it turns both endpoints async and adds Worker admission/teardown cost on Windows, which is where this was reported. Query limiting was rejected because it cannot preserve an exact count, grouping, or sum — a wrong number is worse than an absent one.

The GUI half, added on review

The server change alone had a real gap: with metrics null the row block simply disappeared, so a 1 GB database rendered as one with no rows. That is exactly the confusion the null-vs-zero distinction exists to prevent, and it is most misleading precisely where the database is largest.

The row now states the reason and the threshold. File sizes still render, because only the aggregates were skipped — the inspection itself succeeded.

Rendered in a real browser against the built stylesheet rather than trusted to a passing assertion:

Status         Compatible
Logs database  1.4 GiB
WAL            4 MiB
SHM            32 KiB
DB rows        Row metrics skipped: the database is above 64 MiB, and scanning it would stall the proxy.
sqlite_home    CODEX_HOME

String added to all nine locales.

Verification

bun x tsc --noEmit                                   exit 0
bun test <31 log-guard/storage files>                175 pass / 0 fail
bun test storage-log-guard + log-guard-inspect        24 pass / 0 fail
bun run build:gui                                     ok

Falsified both hunks: removing the server size gate makes the skipped-path test receive full metrics; disabling the GUI branch reddens the render test while the under-threshold case stays green.

One thing deliberately not done: a real-server test asserting /healthz stays responsive during a cold inspection. A sparse large fixture's aggregate path completes too fast to reliably overlap the request, so the test would be timing-dependent and vacuous — it would pass whether or not the fix existed. The deterministic regression proves large databases never enter readMetrics at all, which removes the known blocking operation rather than measuring around it. Saying this rather than shipping a green test that proves nothing.

Checklist

  • Targets dev
  • Regression tests on both surfaces, falsified per hunk
  • GUI rendered and inspected, not just asserted
  • Strings in all nine locales
  • No credential, auth, workflow or release-automation surface touched

Summary by CodeRabbit

  • New Features

    • Large databases now skip row-metric calculations when scanning could block the proxy.
    • The Log Guard panel explains when metrics were skipped and displays the applicable size threshold.
    • Added localized messaging across supported languages.
  • Bug Fixes

    • Preserved full row metrics for databases below the size threshold.
    • Improved reporting consistency when metrics are unavailable.

Choose option (a): skip synchronous row aggregates once logs_2.sqlite exceeds 64 MiB, retaining file, schema, and capability inspection. The threshold matches the reporter's measured mitigation: a ~1 GB GROUP BY level took 17.3s, while bounded /api/storage returned in 628ms.

A Worker (option b) would make both management endpoints asynchronous and add startup, admission, teardown, and Windows thread-exit cost to a management-only inspector; storage Workers are already serialized specifically around Windows teardown. Bounded queries (option c) cannot make count(*), GROUP BY, or sum() exact with LIMIT, and bun:sqlite provides no interruptible async statement on this request path.

Expose metricsSkipped with the threshold so omitted aggregates are distinct from genuine zero values. Keep full metrics for small databases and cover both shapes with a falsified regression test.
… skipped

Carries the size gate for #2605 and adds the GUI half.

The server side skips the row aggregates above 64 MiB, which is what keeps a
cold inspection off the proxy thread - the reporter measured GROUP BY level at
17.3s on a ~1 GB database, and the source already admitted the gap in its own
comment.

The GUI half was missing: with metrics null the row block simply disappeared,
so a 1 GB database rendered as one with no rows. That is the exact confusion the
server's null-vs-zero distinction exists to prevent, and it is most misleading
precisely where the database is largest. The row now states the reason and the
threshold, and the file sizes still render because only the aggregates were
skipped, not the inspection.

String added to all nine locales. Rendered in a real browser against the built
stylesheet, not just asserted.

Falsified: disabling the skipped-row branch reddens the new render test while
the under-threshold case stays green.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 25, 2026 21:12
@lidge-jun
lidge-jun merged commit 8091f2e into dev Aug 25, 2026
6 of 7 checks passed
@lidge-jun
lidge-jun deleted the codex/2605-storage-cold-scan-gui branch August 25, 2026 21:12
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 25, 2026
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 98b97406-0478-48e2-a408-cb04873bb632

📥 Commits

Reviewing files that changed from the base of the PR and between 74823a5 and a6a7f31.

📒 Files selected for processing (5)
  • gui/src/components/storage-workspace/StorageWorkspace.tsx
  • gui/src/i18n/log-guard-labels.ts
  • gui/tests/storage-log-guard.test.tsx
  • src/codex/log-guard/inspect.ts
  • tests/codex-log-guard-inspect.test.ts

📝 Walkthrough

Walkthrough

The inspection now skips synchronous aggregate metrics for databases larger than 64 MiB and reports the reason and threshold. The storage workspace displays localized skip information, while backend and GUI tests cover large and normal databases.

Changes

Log metrics guard

Layer / File(s) Summary
Inspection threshold and result contract
src/codex/log-guard/inspect.ts, tests/codex-log-guard-inspect.test.ts
inspectCodexLogs skips readMetrics when the database exceeds 64 MiB. It returns metricsSkipped with the threshold and returns metricsSkipped: null for other outcomes. Tests cover normal and oversized databases.
Storage UI skip reporting
gui/src/components/storage-workspace/StorageWorkspace.tsx, gui/src/i18n/log-guard-labels.ts, gui/tests/storage-log-guard.test.tsx
StorageWorkspace renders localized skipped-row metrics with the formatted threshold. Translations cover nine locales. GUI tests cover skipped and normal metrics.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: gui

Suggested reviewers: ingwannu, wibias

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/2605-storage-cold-scan-gui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • UI screenshot required.

What to do

  • Add a screenshot of the UI change to the PR description.

Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6a7f31f77

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

...common,
schema,
metrics: readMetrics(db, columns),
metrics: metricsSkipped === null ? readMetrics(db, columns) : null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep reclaim metadata when skipping row scans

For every compatible database above 64 MiB, this sets the entire metrics object to null, even though page_size and freelist_count are cheap PRAGMAs rather than unbounded row aggregates. In the inspected Storage dashboard, reclaimAvailable requires metrics.reclaimableBytes > 0, so the Compact section now disappears for every large database—including one with substantial reclaimable space, which is precisely where compaction is most useful. Preserve the freelist metadata separately while skipping only the expensive row queries, or otherwise keep the action available.

Useful? React with 👍 / 👎.

Comment on lines +498 to +499
metrics: metricsSkipped === null ? readMetrics(db, columns) : null,
metricsSkipped,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report skipped metrics in the doctor output

When a database crosses this threshold, formatCodexLogGuardDoctor only prints its metrics line when report.metrics is present and never examines metricsSkipped. Consequently, ocx doctor silently loses the row, TRACE, and reclaimable-space diagnostics without explaining that the scan was deliberately skipped—the same ambiguity this change fixes in the dashboard. Add a doctor-formatting branch for metricsSkipped and focused coverage for the large-database report.

Useful? React with 👍 / 👎.

inspectionOnly: 'Inspection only',
externalSqliteHome: 'External SQLite storage',
inspectionUnavailable: "Diagnostic log inspection is unavailable.",
metricsSkippedLarge: "Row metrics skipped: the database is above {threshold}, and scanning it would stall the proxy.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move skipped-metrics copy into the canonical locale catalogs

This new visible string is added to the bespoke log-guard-labels.ts map rather than to src/i18n/en.ts and every locale module, and the component renders it through logGuardLabel instead of t(). That bypasses the repository's canonical TKey typing and automatic locale discovery, allowing this copy to drift outside the normal i18n validation path; add a standard locale key and render it through t("key").

AGENTS.md reference: gui/AGENTS.md:L14-L18

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant