Skip to content

chore(api): emit explicit zero for IPNS-entries gauge on empty table#561

Merged
FSM1 merged 2 commits into
mainfrom
chore/ipns-entries-gauge-zero-state
Jun 25, 2026
Merged

chore(api): emit explicit zero for IPNS-entries gauge on empty table#561
FSM1 merged 2 commits into
mainfrom
chore/ipns-entries-gauge-zero-state

Conversation

@FSM1

@FSM1 FSM1 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Two small chore cleanups, batched on this branch.

1. chore(api) — emit explicit zero for the IPNS-entries gauge on an empty table

MetricsService.collectGauges() calls this.ipnsEntriesTotal.reset() then repopulates cipherbox_ipns_entries_total from a GROUP BY record_type query over folder_ipns. When the table is empty that query returns zero rows, so reset() leaves the gauge with no series at all — Prometheus then serves the last non-zero sample through its staleness window and Grafana stat panels "stick" at it (a freshly-wiped environment mis-reporting as N IPNS entries; observed as a stale 43 after a staging reset).

Fix: seed every known record_type ('folder', 'file') to 0 after reset(), then overlay the real counts. users_total/files_total already behaved correctly because they always .set(0); this brings the label-partitioned IPNS gauge in line. Internal metrics only — no API surface change, no api-client regen.

2. chore(ci) — remove the stale release-as guard

Drops the "Check for stale release-as pins" CI step and its companion "Test stale release-as guard" step from ci.yml, and deletes .github/scripts/check-stale-release-as.js + its node:test suite. The guard failed CI whenever a release-as pin equalled its manifest version, adding friction on every release for little value. The underlying advice (don't leave an already-shipped release-as pin in place) stays documented in CLAUDE.md as a manual check — just no longer CI-enforced.

Testing

  • metrics.service.spec.ts: added an ipnsEntriesTotal gauge zero-state (regression) block — empty table → 0 for both types; partial population overlays real counts while absent types stay 0. Full spec: 11 passed.
  • eslint clean, tsc --noEmit clean on the API project.
  • ci.yml / CLAUDE.md pass prettier --check; YAML structure verified (lint job intact, typecheck job follows).

🤖 Generated with Claude Code

collectGauges() resets cipherbox_ipns_entries_total and repopulates it from a
GROUP BY record_type query. With an empty folder_ipns table that query returns
zero rows, so reset() leaves the gauge with no series at all -- Prometheus then
serves the last non-zero sample through its staleness window and Grafana stat
panels stick at it, so a freshly-wiped environment mis-reports as N IPNS
entries (observed as a stale 43 after a staging reset).

Seed every known record type ('folder', 'file') to 0 after reset, then overlay
the real counts, so an empty table reports an explicit 0 per type. Adds a
regression test covering the empty and partial-population cases.

Internal metrics only -- no API surface change, so no api-client regen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDNQVLoAw4DbPrPvtQPobh
Entire-Checkpoint: 3e3f489016fc
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The IPNS metrics collector now seeds known record_type labels with zero values before applying database counts. The spec adds regression coverage for empty input and partial overlay output for cipherbox_ipns_entries_total.

Changes

IPNS gauge zero seeding

Layer / File(s) Summary
Gauge initialization and regression coverage
apps/api/src/metrics/metrics.service.ts, apps/api/src/metrics/metrics.service.spec.ts
ipnsEntriesTotal now seeds folder and file at zero before database counts are applied, and the spec asserts zero-series emission plus overwrite behavior for present counts.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • FSM1/cipher-box#141: Introduces the MetricsService/collectGauges() path and the ipnsEntriesTotal gauge that this PR extends.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: emitting explicit zeros for the IPNS entries gauge on empty tables.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ipns-entries-gauge-zero-state

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

Copy link
Copy Markdown
Contributor

Release Preview

No version bumps detected. All changes are in unversioned paths or use exempt commit types.

@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes cipherbox_ipns_entries_total mis-reporting on an empty folder_ipns table: after reset() the gauge now seeds every known record_type ('folder', 'file') to 0 before overlaying real DB counts, so Prometheus serves an explicit zero instead of holding the last non-zero sample through its staleness window. It also includes regression tests and — unrelated to the metrics fix — removes the stale release-as CI guard entirely.

  • Core fix (metrics.service.ts): adds IPNS_RECORD_TYPES constant and a seed loop after reset(), bringing ipnsEntriesTotal in line with the scalar gauges that already call .set(0).
  • Regression tests (metrics.service.spec.ts): two new cases assert both types emit 0 on an empty table and that real counts overlay correctly while absent types remain 0.
  • Unrelated cleanup (.github/): deletes check-stale-release-as.js, its test, and the two CI steps that ran them; downgrades an automated release-pin guard to a manual convention in CLAUDE.md.

Confidence Score: 5/5

The metrics fix is minimal and correct; the unrelated CI guard removal is deliberate but unexplained.

The core change is a small, well-tested seed loop that only affects internal Prometheus gauge reporting. The logic is sound: reset → seed known types to 0 → overlay real counts. Tests cover both the zero-state and partial-count paths. The unrelated removal of the stale release-as CI guard is worth clarifying but does not affect the correctness of any deployed code.

.github/workflows/ci.yml — removal of the release-as CI guard is unexplained and unrelated to the stated PR scope.

Important Files Changed

Filename Overview
apps/api/src/metrics/metrics.service.ts Adds IPNS_RECORD_TYPES constant and seeds known record types to 0 after reset() before overlaying DB counts — correctly fixes the stale Prometheus series on empty table.
apps/api/src/metrics/metrics.service.spec.ts Adds two regression tests for the zero-state behavior: one for an empty table (both types → 0) and one for partial DB results (real count overlays, missing type → 0).
.github/workflows/ci.yml Removes the stale release-as CI guard steps without explanation in the PR description, downgrading an automated safety check to a manual one.
.github/scripts/check-stale-release-as.js Deleted — the stale release-as guard script is fully removed alongside its CI steps.
.github/scripts/check-stale-release-as.test.js Deleted — unit tests for the stale release-as guard removed alongside the script itself.
CLAUDE.md Updates the release-as pin note to acknowledge the check is no longer CI-enforced and must be done manually.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Scheduler as setInterval (30s)
    participant collectGauges
    participant DB as PostgreSQL
    participant gauge as ipnsEntriesTotal

    Scheduler->>collectGauges: tick
    collectGauges->>DB: GROUP BY record_type (folder_ipns)
    DB-->>collectGauges: "[] (empty) or [{recordType, count}]"

    collectGauges->>gauge: reset()
    Note over collectGauges,gauge: NEW: seed known types to 0
    collectGauges->>gauge: labels('folder').set(0)
    collectGauges->>gauge: labels('file').set(0)

    loop for each row returned
        collectGauges->>gauge: labels(row.recordType).set(parseInt(row.count))
    end

    Note over gauge: Prometheus now always has an explicit series,
    Note over gauge: no stale-window carry-over
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Scheduler as setInterval (30s)
    participant collectGauges
    participant DB as PostgreSQL
    participant gauge as ipnsEntriesTotal

    Scheduler->>collectGauges: tick
    collectGauges->>DB: GROUP BY record_type (folder_ipns)
    DB-->>collectGauges: "[] (empty) or [{recordType, count}]"

    collectGauges->>gauge: reset()
    Note over collectGauges,gauge: NEW: seed known types to 0
    collectGauges->>gauge: labels('folder').set(0)
    collectGauges->>gauge: labels('file').set(0)

    loop for each row returned
        collectGauges->>gauge: labels(row.recordType).set(parseInt(row.count))
    end

    Note over gauge: Prometheus now always has an explicit series,
    Note over gauge: no stale-window carry-over
Loading

Reviews (2): Last reviewed commit: "chore(ci): remove stale release-as guard" | Re-trigger Greptile

Drop the "Check for stale release-as pins" CI step and its companion
"Test stale release-as guard" step from ci.yml, and delete
.github/scripts/check-stale-release-as.js plus its node:test suite.

The guard fails CI whenever a release-as pin equals its manifest version,
which adds friction on every release for little value. The underlying advice
-- do not leave an already-shipped release-as pin in place -- stays documented
in CLAUDE.md as a manual check; it is simply no longer CI-enforced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDNQVLoAw4DbPrPvtQPobh
Entire-Checkpoint: f4dca3728594
@FSM1 FSM1 enabled auto-merge (squash) June 25, 2026 21:20
@FSM1 FSM1 merged commit 99b2586 into main Jun 25, 2026
30 checks passed
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.01%. Comparing base (4b2e81c) to head (ea8260a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #561      +/-   ##
==========================================
+ Coverage   65.96%   70.01%   +4.05%     
==========================================
  Files         149      177      +28     
  Lines       11503    21151    +9648     
  Branches     1305     1305              
==========================================
+ Hits         7588    14809    +7221     
- Misses       3673     6100    +2427     
  Partials      242      242              
Flag Coverage Δ
api 85.95% <ø> (ø)
api-client 85.95% <ø> (ø)
core 85.95% <ø> (ø)
crypto 85.95% <ø> (ø)
desktop 16.45% <ø> (-14.99%) ⬇️
rust 68.64% <ø> (?)
sdk 85.95% <ø> (ø)
sdk-core 85.95% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 85 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@FSM1 FSM1 deleted the chore/ipns-entries-gauge-zero-state branch June 27, 2026 21:48
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