Add state query param to GET snapshots API#128635
Add state query param to GET snapshots API#128635JeremyDahlgren merged 25 commits intoelastic:mainfrom
state query param to GET snapshots API#128635Conversation
|
Hi @JeremyDahlgren, I've created a changelog YAML for you. |
|
Pinging @elastic/es-distributed-coordination (Team:Distributed Coordination) |
|
|
||
| final var details = repositoryData.getSnapshotDetails(snapshotId); | ||
|
|
||
| if (states.isEmpty() == false |
There was a problem hiding this comment.
nit: can omit emptiness check
There was a problem hiding this comment.
++ we could reasonably reject a request with an empty states set in org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest#validate
There was a problem hiding this comment.
Removed isEmpty() check, added a check in GetSnapshotsRequest#validate, 3ed9350.
server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java
Show resolved
Hide resolved
| getSnapshotsRequest.includeIndexNames(request.paramAsBoolean(INDEX_NAMES_XCONTENT_PARAM, getSnapshotsRequest.includeIndexNames())); | ||
|
|
||
| final String stateString = request.param("state"); | ||
| if (Strings.hasLength(stateString) == false) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Nvm :) I confused myself with another case. If feature is supported but state param is null, will it throw NPE at stateString.split?
There was a problem hiding this comment.
Added separate handling for null vs empty, 64d3729.
|
|
||
| final var details = repositoryData.getSnapshotDetails(snapshotId); | ||
|
|
||
| if (states.isEmpty() == false |
There was a problem hiding this comment.
++ we could reasonably reject a request with an empty states set in org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest#validate
| return false; | ||
| } | ||
|
|
||
| if (states.isEmpty() == false && snapshotInfo.state() != null && states.contains(snapshotInfo.state()) == false) { |
There was a problem hiding this comment.
Likewise here, states should never be empty
server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetSnapshotsAction.java
Outdated
Show resolved
Hide resolved
| getSnapshotsRequest.includeIndexNames(request.paramAsBoolean(INDEX_NAMES_XCONTENT_PARAM, getSnapshotsRequest.includeIndexNames())); | ||
|
|
||
| final String stateString = request.param("state"); | ||
| if (Strings.hasLength(stateString) == false) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Added separate handling for null vs empty, 64d3729.
| // 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I think it's substantial change in our API design - relaxing unconsumed parameters check. Do we need this?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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?
There was a problem hiding this comment.
Ah sorry I missed that you'd made this change. Let me look again.
…states is not empty in validate()
| // 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); |
There was a problem hiding this comment.
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)?
DaveCTurner
left a comment
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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)
| 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"); |
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