Skip to content

Track more snapshot-releated node-level stats #130301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 74 commits into
base: main
Choose a base branch
from

Conversation

nicktindall
Copy link
Contributor

@nicktindall nicktindall commented Jun 30, 2025

Adds additional snapshot metrics and publishes them via APM

Apologies for the size of this change, but most of it is plumbing. The change itself is quite small.

Relates: ES-12055, ES-11927

@elasticsearchmachine elasticsearchmachine added the serverless-linked Added by automation, don't add manually label Jun 30, 2025
@nicktindall nicktindall added >non-issue :Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs labels Jun 30, 2025
Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

Looks good (after wading through all the plumbing changes!). I left some comments inline.

@@ -191,6 +191,10 @@ public Stage getStage() {
return stage.get();
}

public long getTotalTime() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is the name of the field but could we include the unit (millis?) in the name of this getter at least?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 4a8fb9d

Comment on lines 78 to 86
in.readLong(),
in.readLong(),
in.readLong(),
totalReadThrottledNanos,
totalWriteThrottledNanos,
in.readLong(),
in.readLong(),
in.readLong(),
in.readLong()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd expect these to be represented using VLong rather than a bare Long - mostly they're going to be quite close to zero. Tho that means we can't just use -1 to mean "missing". Maybe we should also include a boolean up front to indicate that we only include the legacy throttling stats?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e344693, I just put zeroes for the BWC as I imagine it's fairly intermittent.

Comment on lines 364 to 365
default LongWithAttributes getShardSnapshotsInProgress() {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'd rather not have a default implementation here and instead just require all the non-blobstore repository implementations to return null explicitly. But also all the non-blobstore repository implementations are read-only, so they can reasonably return 0 here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in ff82158

Comment on lines 368 to 369
default RepositoriesStats.SnapshotStats getSnapshotStats() {
return new RepositoriesStats.SnapshotStats(getRestoreThrottleTimeInNanos(), getSnapshotThrottleTimeInNanos());
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise here, can we not have a default implementation and instead push this down to the subclasses? We should be able to drop getRestoreThrottleTimeInNanos() and getSnapshotThrottleTimeInNanos() from the Repository interface with this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 0d9a62d

* {@link SnapshotInProgressAllocationDecider}, or states that might delay
* a snapshot's completion.
*/
private static final List<ShardState> TRACKED_SHARD_STATES = List.of(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a good reason for not just tracking all the states? I can see some value in knowing about completed shard snapshots too (e.g. to investigate delays in snapshot finalization)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah not really, I was going for a minimal "interesting" set, but happy to enable them all.

Enabled in e9f32a0

for (SnapshotsInProgress.Entry snapshot : snapshotsInProgress.forRepo(projectId, repository.name())) {
for (ShardSnapshotStatus shardSnapshotStatus : snapshot.shards().values()) {
if (shardCounts.containsKey(shardSnapshotStatus.state())) {
shardCounts.put(shardSnapshotStatus.state(), shardCounts.get(shardSnapshotStatus.state()) + 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest using com.carrotsearch.hppc.ObjectIntMap#addTo (saves looking up the entry twice)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in ba62ff6

Comment on lines 4514 to 4515
for (SnapshotsInProgress.Entry snapshot : snapshotsInProgress.forRepo(projectId, repository.name())) {
for (ShardSnapshotStatus shardSnapshotStatus : snapshot.shards().values()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I worry about these potentially-deeply-nested loops happening on each metric collection cycle. We're already keeping track of these things in applyClusterState (the only place they can change) - could we compute these counts there instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in e90ff65

@@ -1069,7 +1069,7 @@ public static NodeStats createNodeStats() {
);
}
RepositoriesStats repositoriesStats = new RepositoriesStats(
Map.of("test-repository", new RepositoriesStats.ThrottlingStats(100, 200))
Map.of("test-repository", new RepositoriesStats.SnapshotStats(100, 200))
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we use the full SnapshotStats here rather than the legacy throttling-only one? And assert its contents?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 1dec741

…hrottleTimeInNanos() and Repository#getRestoreThrottleTimeInNanos()
…ot_stats_as_metrics

# Conflicts:
#	server/src/main/java/org/elasticsearch/TransportVersions.java
…ot_stats_as_metrics

# Conflicts:
#	server/src/main/java/org/elasticsearch/TransportVersions.java
@nicktindall nicktindall requested a review from ywangd July 10, 2025 03:57
@nicktindall nicktindall requested a review from DaveCTurner July 10, 2025 06:33
Comment on lines 473 to +476
+ ", startTime="
+ startTime
+ startTimeMillis
+ ", totalTime="
+ totalTime
+ totalTimeMillis
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could rename the labels here too

this(entries, stateSummaries.v1(), stateSummaries.v2());
}

private static Tuple<Map<State, Integer>, Map<ShardState, Integer>> calculateStateSummaries(List<Entry> entries) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm I think this means we do this computation on every node now which seems wasteful. Could we do it in SnapshotsService still, just on the master?

When I suggested doing this in applyClusterState I meant just updating the existing stats according to the new cluster state, not computing everything from scratch. If we have to do it from scratch every time then I guess it'd be better to happen on the stats-collection thread rather than the cluster applier. At least we could cache the results assuming they won't change before the next stats collection?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs >non-issue serverless-linked Added by automation, don't add manually Team:Distributed Coordination Meta label for Distributed Coordination team v9.2.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants