Skip to content

Add state query param to GET snapshots API #128635

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

Merged
merged 25 commits into from
Jun 16, 2025

Conversation

JeremyDahlgren
Copy link
Contributor

@JeremyDahlgren JeremyDahlgren commented May 29, 2025

This PR picks up where Elena left off in #123618, addressing the last round of review comments and adding to the tests.

PR description details copied from #123618:


Closes #97446

This PR introduces a new state query parameter for the Get Snapshots API, allowing users to filter snapshots by state.
The parameter accepts comma-separated values for states: SUCCESS, IN_PROGRESS, FAILED, PARTIAL, INCOMPATIBLE (case-insensitive).

🎯 Motivation & Use Case

This is my mini-project for the AppEx team’s On-Week, where I aimed to explore contributing to Elasticsearch while addressing several Kibana issues.

In Kibana's Snapshot Restore page, multiple open issues could be resolved by adding this parameter:

1️⃣ Fixing incorrect selection of last successful managed snapshot

2️⃣ Enhancing the Snapshots table:

✅ Summary of Changes

  • Added state as an optional query parameter to GET _snapshot/{repository}/_all
  • Updated filtering logic in TransportGetSnapshotsAction
  • Added validation of the parameter value
  • Added integration tests in GetSnapshotsIT.java

@JeremyDahlgren JeremyDahlgren added >enhancement :Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs Team:Distributed Coordination Meta label for Distributed Coordination team v9.1.0 labels May 29, 2025
@elasticsearchmachine
Copy link
Collaborator

Hi @JeremyDahlgren, I've created a changelog YAML for you.

@JeremyDahlgren JeremyDahlgren changed the title Add state query param to Get snapshots API Add state query param to GET snapshots API May 29, 2025
@JeremyDahlgren JeremyDahlgren marked this pull request as ready for review May 29, 2025 19:56
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-distributed-coordination (Team:Distributed Coordination)

@@ -572,11 +578,19 @@ private boolean matchesPredicates(SnapshotId snapshotId, RepositoryData reposito
return false;
}

final var details = repositoryData.getSnapshotDetails(snapshotId);

if (states.isEmpty() == false
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can omit emptiness check

Copy link
Contributor

Choose a reason for hiding this comment

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

++ we could reasonably reject a request with an empty states set in org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest#validate

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed isEmpty() check, added a check in GetSnapshotsRequest#validate, 3ed9350.

@@ -82,6 +128,24 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
final SortOrder order = SortOrder.fromString(request.param("order", getSnapshotsRequest.order().toString()));
getSnapshotsRequest.order(order);
getSnapshotsRequest.includeIndexNames(request.paramAsBoolean(INDEX_NAMES_XCONTENT_PARAM, getSnapshotsRequest.includeIndexNames()));

final String stateString = request.param("state");
if (Strings.hasLength(stateString) == false) {
Copy link
Contributor

@mhl-b mhl-b Jun 11, 2025

Choose a reason for hiding this comment

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

State param with empty value seems valid in this condition even if feature is not available ("state="). I think having param keyword should be illegal when feature is off.

Copy link
Contributor

@mhl-b mhl-b Jun 11, 2025

Choose a reason for hiding this comment

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

Nvm :) I confused myself with another case. If feature is supported but state param is null, will it throw NPE at stateString.split?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added separate handling for null vs empty, 64d3729.

@@ -572,11 +578,19 @@ private boolean matchesPredicates(SnapshotId snapshotId, RepositoryData reposito
return false;
}

final var details = repositoryData.getSnapshotDetails(snapshotId);

if (states.isEmpty() == false
Copy link
Contributor

Choose a reason for hiding this comment

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

++ we could reasonably reject a request with an empty states set in org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest#validate

@@ -585,6 +599,10 @@ private boolean matchesPredicates(SnapshotInfo snapshotInfo) {
return false;
}

if (states.isEmpty() == false && snapshotInfo.state() != null && states.contains(snapshotInfo.state()) == false) {
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, states should never be empty

@@ -82,6 +128,24 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
final SortOrder order = SortOrder.fromString(request.param("order", getSnapshotsRequest.order().toString()));
getSnapshotsRequest.order(order);
getSnapshotsRequest.includeIndexNames(request.paramAsBoolean(INDEX_NAMES_XCONTENT_PARAM, getSnapshotsRequest.includeIndexNames()));

final String stateString = request.param("state");
if (Strings.hasLength(stateString) == false) {
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 we could reasonably reject requests that include ?state= without a value, in constrast to the parameter being missing (i.e. null) which should mean "all states" as done 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.

Added separate handling for null vs empty, 64d3729.

Comment on lines 141 to 147
// Consume these response parameters used in SnapshotInfo now, to avoid assertion errors in BaseRestHandler for requests where they
// may not get used.
if (Assertions.ENABLED) {
for (final var responseParameter : SUPPORTED_RESPONSE_PARAMETERS) {
request.param(responseParameter);
}
}
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'm suspicious about this, are you sure it's necessary? If so, it seems like a broader problem than just this one API so maybe we should fix it more fundamentally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you comment this if block out you can get an error running this test:

./gradlew :rest-api-spec:yamlRestTest --tests org.elasticsearch.test.rest.ClientYamlTestSuiteIT -Dtests.method='test {yaml=snapshot.get/10_basic/*}'

This will fail with Connection refused errors, but if you rerun with --debug you can see the root cause:

2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]     Caused by: java.lang.AssertionError: get_snapshots_action: consumed params [after, error_trace, filter_path, format, from_sort_value, human, ignore_unavailable, index_names, master_timeout, offset, order, pretty, repository, size, slm_policy_filter, snapshot, sort, state, verbose] while supporting [after, error_trace, filter_path, format, from_sort_value, human, ignore_unavailable, include_repository, index_details, index_names, master_timeout, offset, order, pretty, repository, size, slm_policy_filter, snapshot, sort, state, verbose]
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.server@9.1.0-SNAPSHOT/org.elasticsearch.rest.BaseRestHandler.assertConsumesSupportedParams(BaseRestHandler.java:158)
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.server@9.1.0-SNAPSHOT/org.elasticsearch.rest.BaseRestHandler.handleRequest(BaseRestHandler.java:101)
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.server@9.1.0-SNAPSHOT/org.elasticsearch.rest.RestController$1.onResponse(RestController.java:468)
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.server@9.1.0-SNAPSHOT/org.elasticsearch.rest.RestController$1.onResponse(RestController.java:462)
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.security@9.1.0-SNAPSHOT/org.elasticsearch.xpack.security.rest.SecurityRestFilter.intercept(SecurityRestFilter.java:69)
2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]          at org.elasticsearch.server@9.1.0-SNAPSHOT/org.elasticsearch.rest.RestController.dispatchRequest(RestController.java:462)

I removed this if block and added exclusion support for response parameters in BaseRestHandler.assertConsumesSupportedParams(), 627c9cc.

Copy link
Contributor

@mhl-b mhl-b Jun 13, 2025

Choose a reason for hiding this comment

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

I think it's substantial change in our API design - relaxing unconsumed parameters check. Do we need this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Right yeah this AssertionError is there to catch discrepancies between the parameters we claim to consume and the ones we actually consume. We definitely shouldn't be working around it like this. But the assertion itself seems buggy:

2025-06-12T14:37:37.881-0400 [DEBUG] [TestEventLogger]     Caused by: java.lang.AssertionError: get_snapshots_action: consumed params 
[after, error_trace, filter_path, format, from_sort_value, human, ignore_unavailable,                                    index_names, master_timeout, offset, order, pretty, repository, size, slm_policy_filter, snapshot, sort, state, verbose] while supporting 
[after, error_trace, filter_path, format, from_sort_value, human, ignore_unavailable, include_repository, index_details, index_names, master_timeout, offset, order, pretty, repository, size, slm_policy_filter, snapshot, sort, state, verbose]

Note that the missing ones are include_repository and index_details which are response parameters and therefore aren't expected to be consumed in prepareRequest. So I think we should fix the assertion.

We must be very careful with code that branches on Assertions.ENABLED because it means that the tests are not testing the exact production behaviour. Sometimes that's what we want, but not in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DaveCTurner - In 627c9cc I added the exclusion for the response parameters in BaseRestHandler.assertConsumesSupportedParams() and removed the if block. Are you looking for a different approach for fixing the assertion?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah sorry I missed that you'd made this change. Let me look again.

Comment on lines 157 to 160
// Add exclusions for response parameters since they may not always be consumed for every request.
final var unconsumedResponseParams = new HashSet<>(responseParams(request.getRestApiVersion()));
unconsumedResponseParams.removeAll(consumed);
supportedAndCommon.removeAll(unconsumedResponseParams);
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 this'll permit a case where we declare a response parameter but don't include it in the supported parameters, which would be a bug. Could we instead count the response parameters in consumed (they're implicitly consumed since they're made available to the response rendering)?

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.

LGTM (one optional nit)

import java.util.Set;

public class GetSnapshotsFeatures implements FeatureSpecification {
public static final NodeFeature GET_SNAPSHOTS_STATE_PARAMETER = new NodeFeature("get.snapshots.state_parameter");
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: we don't really have a formal convention for the names of these features but informally we seem to be converging on hierarchical.with.dot.delimiter like with setting names. So I think I'd have used snapshots.get.state_parameter here (also needs adjusting elsewhere in this PR)

Suggested change
public static final NodeFeature GET_SNAPSHOTS_STATE_PARAMETER = new NodeFeature("get.snapshots.state_parameter");
public static final NodeFeature GET_SNAPSHOTS_STATE_PARAMETER = new NodeFeature("snapshots.get.state_parameter");

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 >enhancement Team:Distributed Coordination Meta label for Distributed Coordination team v9.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a STATE query parameter to Get snapshot API
5 participants