[Store] Make NoF storage gauges projection-only - #6
Open
Hubert-Zhu wants to merge 1 commit into
Open
Hubert-Zhu wants to merge 1 commit into
Hubert-Zhu wants to merge 1 commit into
Conversation
Since the storage metrics projection landed, the NoF gauges have had two writers with incompatible semantics: project_storage_usage() sets absolute values from the authoritative NoFSegmentManager snapshot, while allocate, deallocate, mount and unmount still applied relative increments. The two agree only as long as every increment is paired correctly, so a single missed decrement makes the exported value jump whenever the projection next runs, and every NoF allocation pays for gauge bookkeeping it no longer needs. Drop the incremental writers and let the projection own the NoF gauges. The read side is unchanged: the same gauge names and per-segment labels are still exported, now refreshed from NoFSegmentManager::GetUsageSnapshot(). RefreshStorageMetrics() runs before every externally visible read, and project_storage_usage() already retires labels for segments absent from the snapshot, which is what remove_nof_segment_metrics() did on unmount. This also fixes a latent leak. MasterMetricManager is a process-lifetime singleton that outlives MasterService, which is reconstructed per HA leadership term. ~MasterService releases the capacity of still-mounted DRAM segments via SegmentManager::releaseCapacityMetrics(), but there has never been a NoFSegmentManager equivalent, so NoF segments mounted at demotion left their inc_total_nof_capacity() behind and the next term mounted on top of it. Absolute projection makes that unreachable by construction. The unmount API carried a metrics_dec_capacity out-parameter that existed only to hand the segment size from Prepare to Commit for the decrement, so it goes away with the decrement it fed. The DRAM path has a separate copy of that parameter and keeps it until its own writers are removed. The decoupling guard test no longer has to re-inflate the gauges before teardown, because teardown no longer decrements them. Add a test pinning the new contract: allocating moves domain state while the previously projected values stay put, and the per-segment label appears and is retired purely as a function of what the projection sees. That label path previously had no end-to-end coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
Hubert-Zhu
force-pushed
the
hzhu/store-drop-nof-gauge-dual-write
branch
from
September 4, 2026 01:58
e307fc4 to
bd8c11b
Compare
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.
Description
Since the storage metrics projection landed in
#3385, the NoF gauges
have had two writers with incompatible semantics.
project_storage_usage()sets absolute values from the authoritative
NoFSegmentManagersnapshot,while
allocate,deallocate,MountSegmentandCommitUnmountSegmentstill applied relative increments. The two agree only as long as every
increment is paired correctly, so a single missed decrement makes the
exported value jump the next time the projection runs, and every NoF
allocation pays for gauge bookkeeping it no longer needs.
This drops the incremental writers and lets the projection own the NoF
gauges, following the direction in
RFC #3158.
The read side is unchanged. The same gauge names and per-segment labels are
still exported, now refreshed from
NoFSegmentManager::GetUsageSnapshot().Two things make this behavior-preserving for consumers:
MasterAdminServer::RefreshStorageMetrics()runs before every externallyvisible read (
/metricsscrape, metrics summary, and the periodicreporting loop), so no consumer can observe a stale gauge.
project_storage_usage()already drops per-segment labels for segmentsabsent from the snapshot via
projected_nof_segments_, which is whatremove_nof_segment_metrics()used to do on unmount.The gauge names are untouched, and the only consumer outside this module is
monitoring/grafana/dashboards/mooncake.json, which queriesmaster_nof_allocated_bytesandmaster_total_nof_capacity_bytesand neverthe per-segment variants. Nothing under
docs/references either name, sothere is no documentation to update.
The NoF unmount API carried a
metrics_dec_capacityout-parameter whose onlypurpose was handing the segment size from
PrepareUnmountSegmenttoCommitUnmountSegmentfor that decrement, so it goes away with the decrementit fed. The DRAM path has a separate copy of that parameter and keeps it
until its own writers are removed.
This also fixes a latent leak.
MasterMetricManageris a process-lifetimesingleton that outlives
MasterService, which is reconstructed per HAleadership term.
~MasterServicereleases the capacity of still-mounted DRAMsegments via
SegmentManager::releaseCapacityMetrics(), but there has neverbeen a
NoFSegmentManagerequivalent, so NoF segments that were mounted atdemotion left their
inc_total_nof_capacity()behind and the next termmounted on top of it. Absolute projection makes that unreachable by
construction: each refresh recomputes the gauge from the segments that are
actually mounted.
Scoped to the NoF tier deliberately. NoF and DRAM have independent gauges,
managers and allocator paths, so removing one tier's writers is atomic and
leaves no half-migrated state. The DRAM tier is larger and carries three
design decisions of its own (standby accounting, the
CxlRegionDrivercapacity-gauge-as-refcount, and the
ApplySnapshotStatemetric rebuild); itwill follow separately.
Module
mooncake-store)Type of Change
How Has This Been Tested?
NoFUsageSnapshotSurvivesMetricsResetno longer has to re-inflate the gaugesbefore teardown, because teardown no longer decrements them; it now zeroes
them the way a standby transition does, by projecting an empty snapshot.
Added
NoFGaugesMoveOnlyWhenProjected, which pins the new contract:allocating from a mounted NoF segment moves domain state while the previously
projected gauge values stay put, and only the next projection brings them
back in sync.
The same test also covers the per-segment label lifecycle that
remove_nof_segment_metrics()used to own, asserting the label appears inserialize_metrics()once the mounted segment is projected and is gone oncethe unmounted one is. That path previously had no end-to-end coverage:
ProjectStorageUsageRemovesAbsentSegmentLabelsexercises the projection withsynthetic snapshots only, and no NoF test asserted on
StorageUsageSnapshot::segmentsat all.Test commands:
Test results:
Not yet verified locally: the development machine has no cmake,
clang-format or pre-commit available, so this branch has not been compiled,
run or formatted locally. Every added line was checked by hand against the
80-column limit, and the repository was swept for remaining references to the
removed APIs, but clang-format's exact wrapping is unverified.
Checklist
./scripts/code_format.shAI Assistance Disclosure
Cursor was used to enumerate the legacy write call sites, confirm the
projection already covers per-segment label cleanup and the
refresh-before-read paths, and apply the removals.
Made with Cursor