diagnostics/metrics: collapse GetOrCreate* onto generic helpers#22168
Merged
Conversation
…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.
Contributor
There was a problem hiding this comment.
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}withgetOrCreate[T prometheus.Metric]. - Rework
GetOrCreateGaugeVecto usegetOrCreateVec[T prometheus.Collector], widen stored vec type toprometheus.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.
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
approved these changes
Jul 3, 2026
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#22165–erigontech#22168.
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.
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 ontogetOrCreate[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 whilenamedMetricVec.metricwas concretely typed*prometheus.GaugeVec). The generic widens the field toprometheus.Collectorand performs a real assertion with the same error shape as the scalar family (TestSetGetOrCreateGaugeVecWrongType).registerMetricVecis 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/getOrCreateVecwere themselves clones differing only in which registry (m/avsvecs/av) they touched. They now delegate to a singlegetOrCreateInover a sharednamedEntry[M]entry type (namedMetric/namedMetricVecbecome type aliases), with the wrappers wideningTthrough 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 pureparseMetric+ 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 (aquantile/leconst label, a negative summary window); a non-deferred unlock would strand the set's mutex. Also drops the dead scalarnamedMetric.isAux— never assigned, so theListMetricNamesfilter on it was unreachable.Net −169 lines in set.go. Verified: package tests incl.
-race,make lintclean, fullgo build ./....Part of a dedup series; siblings #22165, #22166, #22167.