Skip to content

Mark stateless and servo meters when removed - #1293

Merged
brharrington merged 1 commit into
Netflix:mainfrom
brharrington:removable-meters-remaining
Sep 8, 2026
Merged

brharrington merged 1 commit into
Netflix:mainfrom
brharrington:removable-meters-remaining

Conversation

@brharrington

Copy link
Copy Markdown
Contributor

StatelessMeter and the five servo meter types implement RemovableMeter, so SwapMeter.get() reads the flag instead of falling back to hasExpired().

These are the two remaining registries whose expiry check reads the wall clock, so they are the two still paying for it on every update after #1290:

registry hasExpired() pays a clock read?
Atlas now - lastUpdated > ttl fixed in #1288
Stateless clock.wallTime() - lastUpdated > ttl yes → fixed here
Servo now - lastUpdated.get() > EXPIRATION_TIME_MILLIS yes → fixed here
Sidecar return false no
metrics3 / metrics5 return false no
DefaultRegistry return false no

Servo has no shared meter base — ServoMeter is a functional interface for exposing monitors — so the field and two accessors are repeated across ServoCounter, ServoTimer, ServoGauge, ServoMaxGauge and ServoDistributionSummary. Stateless has a real base class, so it is one place.

Why the other three are left alone

Their hasExpired() is a constant false, so the instanceof fallback added in #1290 costs them nothing beyond the type test — there is no clock read to remove.

The exclusion is not purely about cost, though. Because they are not RemovableMeter, markRemoved is a no-op for them, so close()/reset() leaves a held reference bound to an orphan forever. That is unchanged from before this series and is not a regression, but adopting the flag there would be a behaviour improvement rather than a performance one, and belongs on its own terms.

Tests

StatelessRemovalMarkingTest (6) and ServoRemovalMarkingTest (5), all 11 failing with the production change reverted:

  • every meter type is removable, asserted against the exact set of concrete classes
  • a cleanup pass marks what it removes, and the meters are gone afterwards
  • a meter that survives the pass is not marked, so "marks what it removes" is distinguished from "marks everything it visits"
  • a held reference recovers from a removal with no time passing at all, so only the removal signal can explain it, and the update is asserted to land on the replacement rather than merely that a new meter appeared
  • close() marking for stateless, which overrides it
  • theWrapperAddsNoClockReadsOfItsOwn

The servo cleanup test drives ServoRegistry.getMonitors(), the registry's real cleanup entry point, rather than hand-rolling an iterator loop.

On the clock-read test: on servo an absolute count is the wrong assertion, because ServoCounter.add() reads the clock three times on its own — one per ServoPollers.NUM_POLLERS step bucket in DoubleCounter.increment, plus lastUpdated — and hard-coding that would break on any unrelated change to servo internals. Comparing the wrapper against the meter it delegates to pins the property that matters, that the wrapper adds nothing, and it fails on main where it adds exactly one read. It also asserts the measured floor is non-zero so two zeros cannot satisfy it. On stateless the raw meter reads exactly once, so that side pins the absolute count too.

Both registries keep bookkeeping counters of their own in the registry under test (ValidationHelper is wired to this with no way to redirect it), so the tests work from the ids they create rather than from everything the registry holds.

StatelessMeter and the five servo meter types implement RemovableMeter,
so SwapMeter.get() gets the flag instead of falling back to hasExpired().
These are the two remaining registries whose expiry check reads the wall
clock, so they are the two that were still paying for it on every update
after Netflix#1290.

Servo has no shared meter base, so the field and the two accessors are
repeated per type. Stateless has one, so it is a single place.

Not done for the other registries, because there is nothing to gain:
SidecarMeter, the metrics3 and metrics5 meters and the DefaultRegistry
types all return a constant false from hasExpired(), so the fallback
costs them nothing beyond the type test. Adopting the flag there would
change behaviour rather than cost, by letting a held reference recover
after a removal it currently never notices, and that is worth doing on
its own terms rather than as part of this.
@brharrington brharrington added this to the 1.10.7 milestone Sep 8, 2026
@brharrington
brharrington merged commit d0db1b5 into Netflix:main Sep 8, 2026
1 check passed
@brharrington
brharrington deleted the removable-meters-remaining branch September 8, 2026 19:26
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