Make cache_size gauge additive across caches sharing a name - #385
Make cache_size gauge additive across caches sharing a name#385stasimus wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughCache size metrics now aggregate contributions from multiple cache instances. Each cache tracks its previous reported size and withdraws its contribution when finalized. Tests cover aggregation, scheduled reporting, release behavior, and released-cache operations. ChangesCache size metrics
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to Cache-size gauges now aggregate contributions from same-named caches and withdraw released caches’ contributions. The covered aggregation and release behavior presents no remaining merge-readiness risk. Sequence Diagram(s)sequenceDiagram
participant CacheMetered
participant CacheMetrics
participant sizeGauge
CacheMetered->>CacheMetrics: Report cache size
CacheMetrics->>sizeGauge: Apply per-cache size delta
CacheMetered->>CacheMetrics: Report size 0 on finalization
CacheMetrics->>sizeGauge: Withdraw cache contribution
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (2 skipped: 2 unsupported.) ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Some background on why this is needed. A service can build several caches under one metrics name (say one per topic, then one per partition of that topic), all wrapped in The fix: each
|
mr-git
left a comment
There was a problem hiding this comment.
I wonder, how often we have several caches reporting against the same set of metrics 🤔 ...
| ().pure[F].flatMap { _ => | ||
| val delta = size - sizeReported.getAndSet(size) | ||
| sizeGauge1.inc(delta.toDouble).whenA(delta != 0) | ||
| } |
There was a problem hiding this comment.
This looks convoluted, feels that usage of Ref might look better than direct usage of AtomicInteger.
Is it done this way in attempt to NOT change the public API? If so, with #369 we will have to change the public API - we could change this one too (from : Monad to : Async?)
There was a problem hiding this comment.
As an alternative, we could introduce the def make[F[_]: Concurrent] which builds the Ref-based and deprecate the "expanded" of like:
@deprecated(message = "use `make`", since = "2026-09-03")
def of[F[_]: Concurrent](
collectorRegistry: CollectorRegistry[F],
prefix: Prefix = Prefix.Default,
sizeReported: Option[Ref[F, Int]] = None,
): Resource[F, Name => CacheMetrics[F]]In .of we can keep the old behaviour on None, but on next major bump we can drop the .of.
Not sure, if it is worth the effort, but usage of Ref feels to be nicer fit that relying on raw AtomicInteger
There was a problem hiding this comment.
one more option - just introduce the def make as an alternative, with copy-paste-improved code from deprecated .of.
There was a problem hiding this comment.
Yes, the AtomicInteger was there to keep of on Monad and the return type intact, since this PR is meant to ship in 6.x.
Went with the third option, 30952a1: make[F: Concurrent] returns Name => Resource[F, CacheMetrics[F]], keeps the reported size in a Ref and withdraws it in its own finalizer, so callers get the right numbers without remembering to report 0. of is deprecated but keeps the additive fix, so existing users get correct sums without a code change. Both share the collector wiring, no copy paste. On the 7.0.0 branch of can go.
There was a problem hiding this comment.
It seems to be OK, but very convoluted. If we plan to discard .of right after release of 6.1.0, then I'd go with copy-paste-amend implementation with less wrappers and indirections, which will be harder to roll-back after 6.1.0
| a <- cache.get(0) | ||
| _ <- IO { a shouldEqual none[Int] } | ||
| _ <- metrics.expect(metrics.expectedGet(hit = false) -> 1) | ||
| _ <- metrics.expect(metrics.expectedGet(hit = false) -> 1, metrics.expectedSize(0) -> 1) |
There was a problem hiding this comment.
IMHO, here and in whole file, it would look better, if in case of 2+ "expects", we'd go multi-line, though I'd prefer to go multi-line in all cases, even with single check, like:
_ <- metrics.expect(
// all checks listed here, one per line
)
mr-git
left a comment
There was a problem hiding this comment.
LGTM, though I do not like the increased complexity of code. I'd go with deprecation of .of and improved duplicate of .make in 6.1.0 and clean deletion of .of in 7.0.0
Every cache reporting under the same name used to overwrite the
cache_sizegauge, so it showed whichever reported last. Each cache now adds its delta and withdraws its share on release.cache_sizewill show the true sum for a name, so dashboards will step up after upgrading.Summary by CodeRabbit
New Features
Bug Fixes
Tests