Skip to content

diagnostics/metrics: collapse GetOrCreate* onto generic helpers#22168

Merged
AskAlexSharov merged 3 commits into
mainfrom
yperbasis/metrics-get-or-create
Jul 3, 2026
Merged

diagnostics/metrics: collapse GetOrCreate* onto generic helpers#22168
AskAlexSharov merged 3 commits into
mainfrom
yperbasis/metrics-get-or-create

Conversation

@yperbasis

@yperbasis yperbasis commented Jul 2, 2026

Copy link
Copy Markdown
Member

The five GetOrCreate{Histogram,Counter,Gauge,SummaryExt,GaugeVec} methods repeated the same ~32-line double-checked-locking block; each copy differed only in the constructor and type assertion. They collapse onto getOrCreate[T prometheus.Metric] / getOrCreateVec[T prometheus.Collector], and each method is now a 3-liner.

The GaugeVec copy is where clone mutation had gone wrong: its type guard compared reflect.TypeFor[*prometheus.GaugeVec]() to itself — always false, i.e. dead (and unreachable while namedMetricVec.metric was concretely typed *prometheus.GaugeVec). The generic widens the field to prometheus.Collector and performs a real assertion with the same error shape as the scalar family (TestSetGetOrCreateGaugeVecWrongType). registerMetricVec is removed: its re-check-and-insert was a duplicate of the inline double-checked insert.

First commit adds characterization tests (+127 lines) pinning existing GetOrCreate* behavior — same-instance returns, wrong-type errors, describe-once semantics — green against the old code before the refactor.

Update (third commit): getOrCreate/getOrCreateVec were themselves clones differing only in which registry (m/a vs vecs/av) they touched. They now delegate to a single getOrCreateIn over a shared namedEntry[M] entry type (namedMetric/namedMetricVec become type aliases), with the wrappers widening T through an adapter closure so the store stays compile-time safe. The double-checked locking becomes one critical section: the fast path took the same mutex anyway, and the create funcs are pure parseMetric + constructor calls, so creating under the lock eliminates the create-twice-drop-loser race. The unlock is deferred because prometheus constructors can panic on inputs reachable through the public API (a quantile/le const label, a negative summary window); a non-deferred unlock would strand the set's mutex. Also drops the dead scalar namedMetric.isAux — never assigned, so the ListMetricNames filter on it was unreachable.

Net −169 lines in set.go. Verified: package tests incl. -race, make lint clean, full go build ./....

Part of a dedup series; siblings #22165, #22166, #22167.

yperbasis added 2 commits July 2, 2026 21:17
…vior

Pin the get-or-create semantics of the Set registry before refactoring:
same-instance return on repeated calls, wrong-type errors for the scalar
families, invalid-name errors, GaugeVec identity across differing label
sets, single Describe emission per vec, and double-checked-locking
behavior under concurrency. All pass against the existing code.
The five GetOrCreate{Histogram,Counter,Gauge,SummaryExt,GaugeVec}
methods repeated the same double-checked-locking block. Replace them
with getOrCreate[T]/getOrCreateVec[T]. The GaugeVec copy carried a
type check comparing a reflect.Type to itself — dead, and unreachable
while namedMetricVec.metric was concretely typed; the generic widens
the field to prometheus.Collector and asserts for real (now tested).
registerMetricVec's double-insert guard is subsumed by the inline
double-checked insert.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors diagnostics/metrics.Set by collapsing the duplicated GetOrCreate* implementations into two generic helpers (getOrCreate for scalar metrics and getOrCreateVec for collector/vec metrics), while also correcting GetOrCreateGaugeVec’s wrong-type handling (now a real type assertion instead of an ineffective reflect comparison). It also adds characterization tests to lock in existing behaviors (same-instance reuse, wrong-type errors, invalid-name errors, and Describe semantics).

Changes:

  • Replace repeated double-checked-locking blocks in GetOrCreate{Histogram,Counter,Gauge,SummaryExt} with getOrCreate[T prometheus.Metric].
  • Rework GetOrCreateGaugeVec to use getOrCreateVec[T prometheus.Collector], widen stored vec type to prometheus.Collector, and perform a real type assertion for wrong-type errors.
  • Add tests to pin GetOrCreate* behavior, including concurrent access.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
diagnostics/metrics/set.go Deduplicates GetOrCreate logic via generics; fixes GaugeVec wrong-type assertion by storing vecs as prometheus.Collector.
diagnostics/metrics/set_test.go Adds characterization tests for same-instance reuse, wrong-type/invalid-name errors, Describe-once behavior, and concurrency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread diagnostics/metrics/set_test.go
getOrCreate and getOrCreateVec were line-for-line clones differing only
in which registry (m/a vs vecs/av) they touched. Introduce namedEntry[M]
(namedMetric and namedMetricVec become aliases) and collapse both onto
one getOrCreateIn over an explicitly passed map+slice pair; the wrappers
widen T to the entry type via an adapter closure, keeping the store
compile-time safe.

Replace the double-checked locking with a single critical section: the
fast path took the same mutex anyway, and the create funcs are pure
parseMetric + constructor calls, so creating under the lock eliminates
the create-twice-drop-loser race. The unlock is deferred because
prometheus constructors can panic on reachable inputs (a quantile/le
const label, a negative summary window); a non-deferred unlock would
strand the set's mutex.

Also drop namedMetric.isAux: nothing ever set it (its vec-side twin was
removed in the previous commit), so the ListMetricNames filter was
unreachable.
@AskAlexSharov AskAlexSharov added this pull request to the merge queue Jul 3, 2026
Merged via the queue into main with commit 836b42b Jul 3, 2026
94 checks passed
@AskAlexSharov AskAlexSharov deleted the yperbasis/metrics-get-or-create branch July 3, 2026 09:53
pull Bot pushed a commit to Dustin4444/erigon that referenced this pull request Jul 3, 2026
… resource handlers (erigontech#22169)

The package carried two parallel MCP servers — `ErigonMCPServer`
(in-process typed APIs) and `StandaloneMCPServer` (JSON-RPC proxy) —
with independently maintained copies of the tool catalog (~330 lines
each, 264 of 270 non-blank registration lines identical), the prompts
(136 lines byte-identical), the resource registration, and the log-file
handlers. The copies had already drifted the worst way possible: the
in-process resource handlers were still placeholders (raw URI used as
the address, hardcoded `"syncing": false` and `"status": "success"`)
while the standalone copies had the real logic.

Two commits:

1. **Implement the in-process resource handlers** (TDD: failing tests
first) — real address extraction via a shared `extractURIParam`, real
`eth_syncing`, receipt-status derivation, mirroring the standalone
behavior through the typed APIs.
2. **Share the registration surfaces** — one `toolSpecs()` table
consumed via per-server `toolHandlers()` maps (`registerTools` panics on
any catalog/handler mismatch); `registerPrompts`/`registerResources` as
free functions over per-server handler sets; the four log handlers
folded into a `logTools` struct embedded by both servers (also deduping
the log-file-resolution switch that was inlined 4×).
`eth_getStorageValues` stays embedded-only.
`TestEmbeddedAndStandaloneCatalogsMatch` pins both servers to identical
tool/prompt/resource catalogs so they cannot drift silently again.

Net: −248 lines despite +~130 of new tests; standalone.go alone went
1724 → 1129 lines. Verified: package tests, scoped `golangci-lint` clean
(×2), `make erigon`.

Part of a dedup series; siblings erigontech#22165erigontech#22168.
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.

3 participants