Skip to content

fix(engine): deprecate EngineMetrics.Throughput, which always reads 0 (celeris#653) - #695

Merged
FumingPower3925 merged 3 commits into
mainfrom
fix/653-deprecate-throughput
Sep 27, 2026
Merged

FumingPower3925 merged 3 commits into
mainfrom
fix/653-deprecate-throughput

Conversation

@FumingPower3925

@FumingPower3925 FumingPower3925 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

engine.EngineMetrics.Throughput is exported and documented as "the recent requests-per-second rate", but no engine has ever assigned it, so it always reads 0. That zero looks exactly like a measured rate of zero.

Removing the field is a breaking change and belongs to v2.0.0 (#651). This PR deprecates it in v1.6.0, as the plan's D6 recommends.

Fixes #653. That is the v1.6.0 decision #653 was filed for; the removal is v2.0.0's (#651).

Changes

  • engine/engine.go: a Deprecated: paragraph on the field. It says:
    • the field always reads 0, and why: no engine computes it, and a snapshot has no interval to compute a rate over
    • use RequestCount, sampled twice over the caller's own interval, instead
    • the field is removed in v2.0.0 (Release checklist: v2.0.0 #651)
  • staticcheck SA1019 now flags every reader outside the package. Checked on a mutant; see below.
  • adaptive/engine.go: removed the Throughput: pm.Throughput + sm.Throughput pass-through (0 + 0). Nothing in the tree touches the field any more.
  • adaptive/close_accounting_test.go: the reflective adaptive: Metrics() silently drops Workers, AcceptCount, CloseCount, BytesRead, BytesWritten and RecvResumeWhileCancelPending #627 aggregation test exempts Throughput by name, with the reason.
  • engine/epoll/loop.go: one comment no longer names the field.
  • engine/epoll/async_reqcount_test.go:144 (review round 2): the comment no longer lists Throughput among the values derived from RequestCount. What is derived from it is the adaptive controller's ThroughputRPS (adaptive/telemetry.go:85) and BytesPerReq.

Docs examples that print it: goceleris/docs (engines.md, performance.md), in a separate docs PR.

Consumers. probatorium reads the field at validation/refapp/internal/debugvars/debugvars.go:510. It is unchanged here, per the lane rules; its EngineKeysNotParsed entry already names #653. Its next celeris bump will get an SA1019 finding wherever golangci-lint covers that module.

Test Plan

Evidence: evidence/celeris-673-679-653-424/lane-20260926/653/ (per-finding index for round 2: ROUND2.md).

  • TestThroughputIsDeprecatedAsAlwaysZero parses the field's doc with go/parser.

  • TestNothingSetsThroughput walks every non-test .go file in the repository and fails on any composite-literal key or selector named Throughput.

    • It skips hidden directories, so a checkout's .claude/worktrees copies are not scanned.
    • It is deliberately name-based, not type-based: see "Review notes" below.
  • On 9f4d89b: both FAIL, with the test file as pushed (base-9f4d89b-pushedtext.log, from base-pushedtext.sh).

    • The pushed engine/throughput_deprecated_test.go (sha256 71bd4786…, unchanged since 38d2c4d) is copied onto a detached 9f4d89b worktree, whole.
    • The command is the one MANIFEST.txt records for base-9f4d89b.log: go test -v -run 'TestThroughputIsDeprecatedAsAlwaysZero|TestNothingSetsThroughput' ./engine/.
    • Tally: 0 PASS, 2 FAIL, 0 SKIP.
    • throughput_deprecated_test.go:34: the doc is Throughput is the recent requests-per-second rate. and has no Deprecated paragraph.
    • :107: 3 sites touch the field, all on adaptive/engine.go:1018.
    • The line numbers match the pushed file. The first base log's :105 came from an earlier revision.
  • Fixed, at 3477bbe:

    • ./engine natively with -v: 17 PASS, 0 FAIL, 0 SKIP.
    • go vet ./engine/... ./adaptive/... passes for linux/amd64 and linux/arm64 (round2-vet-and-engine-v.log).
    • golangci-lint v2.13.2 (CI's Lint job pins v2.13) on ./engine/... ./adaptive/...: 0 issues, natively (round2-golangci-native.log) and with GOOS=linux (round2-golangci-linux.log).
  • Mutants, each killed. Re-run on 3477bbe (round2-mutants/); every tree is restored by cp from a saved copy.

    Mutant Test that fails
    M1: Deprecated paragraph removed TestThroughputIsDeprecatedAsAlwaysZero
    M2: adaptive forwards the field again TestNothingSetsThroughput; golangci-lint also reports SA1019 at adaptive/engine.go:1018
    M3: std sets it (a non-test file writes EngineMetrics.Throughput) TestNothingSetsThroughput
  • ./adaptive is Linux-only and is judged by this PR's CI.

    • Run 36254874202 on 3477bbe: 9/9 jobs green.
    • Adaptive job: 103 PASS, 0 FAIL and 0 SKIP (--- PASS/FAIL/SKIP: Test lines), including TestMetricsCarriesEveryFieldReflectively (ci-36254874202-adaptive.log).
    • Lint job: 0 issues in every module it lints (ci-36254874202-lint.log).
    • Run 36241544621 on 38d2c4d was also 9/9 green.

Review notes (round 2)

  • Why the test is not type-based. TestNothingSetsThroughput could fail on an unrelated future field named Throughput.
    • Disputed, not tightened. Type information without golang.org/x/tools, which is not in celeris's go.mod, means type-checking with go/types over the source importer, once per GOOS and build-tag set, across 10 modules. That type check would also stop seeing the files behind tags such as linux, validation, race and celeris_closeprobe, which the syntactic walk reads today.
    • A false positive fails loudly, with the file and line. That is safer than a silent miss.

Release notes

  • Breaking change? No: the field stays, deprecated.
  • Labeled for release notes

… (celeris#653)

EngineMetrics.Throughput was exported and documented as "the recent
requests-per-second rate", and no engine ever assigned it: std, epoll and
io_uring never wrote it, and adaptive's aggregator summed the two
sub-engines' zeros. A field that always reads 0 cannot be told apart from a
measured rate of zero.

Removing it breaks the exported struct, so that is v2.0.0's (celeris#651).
v1.6.0 deprecates it: the doc now says it always reads 0 and why, and points
at RequestCount sampled over the caller's own interval. staticcheck's SA1019
then flags every reader outside the package. Adaptive's pass-through of the
zeros is removed, so nothing in the tree touches the field, and the
reflective aggregation test (celeris#627) exempts it by name.

Tests:
- TestThroughputIsDeprecatedAsAlwaysZero reads the field's doc with
  go/parser. On 9f4d89b it FAILS: the doc is "Throughput is the recent
  requests-per-second rate." with no Deprecated paragraph.
- TestNothingSetsThroughput walks every non-test Go file of the repository
  and fails on any composite-literal key or selector named Throughput, so
  the "always reads 0" claim and the code cannot drift apart. On 9f4d89b it
  FAILS on adaptive/engine.go:1018.
- Mutants, each killed: the Deprecated paragraph removed; adaptive
  forwarding the field again (which golangci-lint also reports as SA1019);
  std setting it.
…leris#653)

TestAsyncPromotedConnRequestCount's comment named EngineMetrics.Throughput
among the values that went flat with RequestCount. Throughput was never
derived from anything; it is always 0. What does derive from RequestCount
is the adaptive controller's ThroughputRPS (telemetry.go) and BytesPerReq.
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: goceleris/celeris/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 84d64a66-686a-4ca7-b949-f5d667ca5772

📥 Commits

Reviewing files that changed from the base of the PR and between 3477bbe and 22245ea.

📒 Files selected for processing (3)
  • adaptive/engine.go
  • engine/engine.go
  • engine/epoll/loop.go
💤 Files with no reviewable changes (1)
  • adaptive/engine.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • engine/engine.go
  • engine/epoll/loop.go

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Summary

Summary by CodeRabbit

  • Bug Fixes

    • Request counts now include batches received on asynchronously promoted connections, improving adaptive-controller metrics such as requests per second and bytes per request.
  • Updates

    • EngineMetrics.Throughput is deprecated and always returns zero. To calculate requests per second, compare two RequestCount readings; the field is planned for removal in v2.0.0.

Walkthrough

EngineMetrics.Throughput remains declared but is documented as deprecated and always zero. The adaptive engine no longer aggregates it. Tests check the documentation and scan non-test Go files for assignments or selectors.

Changes

Throughput field deprecation

Layer / File(s) Summary
Document and stop aggregating Throughput
engine/engine.go, adaptive/engine.go
EngineMetrics.Throughput is documented as deprecated and always zero. The documentation describes deriving a rate from RequestCount snapshots and planned removal in v2.0.0. The adaptive engine no longer aggregates the field.
Verify deprecation and update related checks
engine/throughput_deprecated_test.go, adaptive/close_accounting_test.go, engine/epoll/async_reqcount_test.go, engine/epoll/loop.go
Tests verify the field documentation and declaration, scan non-test Go files for Throughput uses, and exclude the field from a reflective zero-value check. Related epoll comments identify adaptive-controller throughput metrics.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 22245

The deprecation change has no identified merge-blocking issue and is ready to merge after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 3477b

The metric remains available, and the reviewed production changes do not alter request handling or add an attacker-accessible path. Consumers will need to migrate from the deprecated field before its planned removal. Downstream adoption is not fully established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The assessed exposure is metrics consumers of the exported field, not a newly reachable request path. The extent of use by consumers outside this repository is unverified.

Trust Boundaries and Controls

  • inferred — No changed production line in the assessed diff grants authority, changes credentials, or bypasses a request-processing control. The existing epoll counter increment remains on the loop thread.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #653 requires a clear response to the always-zero EngineMetrics.Throughput field. engine/engine.go keeps the field for compatibility, marks it deprecated, explains why snapshots cannot compu…
Out of Scope Changes check ✅ Passed The reviewed changes stay connected to issue #653. The adaptive aggregation change prevents misleading pass-through behavior. The comments and tests document and enforce the deprecated-field behavior.…
Title check ✅ Passed The title uses the required fix(engine): summary format, describes the deprecation of EngineMetrics.Throughput, and ends with the issue reference (celeris#653).
Description check ✅ Passed The description clearly explains the deprecation, code changes, tests, and issue context for EngineMetrics.Throughput.

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

@FumingPower3925

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@FumingPower3925
FumingPower3925 marked this pull request as ready for review September 27, 2026 14:04
@codecov

codecov Bot commented Sep 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@FumingPower3925
FumingPower3925 merged commit a3ff192 into main Sep 27, 2026
15 checks passed
@FumingPower3925
FumingPower3925 deleted the fix/653-deprecate-throughput branch September 27, 2026 14:12
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.

engine.EngineMetrics.Throughput is exported, documented, and assigned by no engine: it reports a plausible 0 instead of "not measured"

1 participant