Skip to content

Add API to execute SLM retention on-demand #47405

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 4 commits into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.elasticsearch.client.slm.DeleteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecyclePolicyResponse;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecycleRetentionRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecyclePolicyResponse;
import org.elasticsearch.client.slm.GetSnapshotLifecycleStatsRequest;
Expand Down Expand Up @@ -467,6 +468,44 @@ public Cancellable executeSnapshotLifecyclePolicyAsync(
options, ExecuteSnapshotLifecyclePolicyResponse::fromXContent, listener, emptySet());
}

/**
* Execute snapshot lifecycle retention
* See <pre>
* https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/
* java-rest-high-ilm-slm-execute-snapshot-lifecycle-retention.html
* </pre>
* for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public AcknowledgedResponse executeSnapshotLifecycleRetention(ExecuteSnapshotLifecycleRetentionRequest request,
RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, IndexLifecycleRequestConverters::executeSnapshotLifecycleRetention,
options, AcknowledgedResponse::fromXContent, emptySet());
}

/**
* Asynchronously execute snapshot lifecycle retention
* See <pre>
* https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/
* java-rest-high-ilm-slm-execute-snapshot-lifecycle-retention.html
* </pre>
* for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
* @return cancellable that may be used to cancel the request
*/
public Cancellable executeSnapshotLifecycleRetentionAsync(
ExecuteSnapshotLifecycleRetentionRequest request, RequestOptions options,
ActionListener<AcknowledgedResponse> listener) {
return restHighLevelClient.performRequestAsyncAndParseEntity(
request, IndexLifecycleRequestConverters::executeSnapshotLifecycleRetention,
options, AcknowledgedResponse::fromXContent, listener, emptySet());
}

/**
* Retrieve snapshot lifecycle statistics.
* See <pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.elasticsearch.client.ilm.StopILMRequest;
import org.elasticsearch.client.slm.DeleteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecycleRetentionRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecycleStatsRequest;
import org.elasticsearch.client.slm.PutSnapshotLifecyclePolicyRequest;
Expand Down Expand Up @@ -217,6 +218,18 @@ static Request executeSnapshotLifecyclePolicy(ExecuteSnapshotLifecyclePolicyRequ
return request;
}

static Request executeSnapshotLifecycleRetention(ExecuteSnapshotLifecycleRetentionRequest executeSnapshotLifecycleRetentionRequest) {
Request request = new Request(HttpPost.METHOD_NAME,
new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_slm/_execute_retention")
.build());
RequestConverters.Params params = new RequestConverters.Params();
params.withMasterTimeout(executeSnapshotLifecycleRetentionRequest.masterNodeTimeout());
params.withTimeout(executeSnapshotLifecycleRetentionRequest.timeout());
request.addParameters(params.asMap());
return request;
}

static Request getSnapshotLifecycleStats(GetSnapshotLifecycleStatsRequest getSnapshotLifecycleStatsRequest) {
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_slm/stats").build();
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.client.slm;

import org.elasticsearch.client.TimedRequest;

public class ExecuteSnapshotLifecycleRetentionRequest extends TimedRequest {
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.elasticsearch.client.slm.DeleteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecyclePolicyResponse;
import org.elasticsearch.client.slm.ExecuteSnapshotLifecycleRetentionRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecyclePolicyRequest;
import org.elasticsearch.client.slm.GetSnapshotLifecyclePolicyResponse;
import org.elasticsearch.client.slm.GetSnapshotLifecycleStatsRequest;
Expand Down Expand Up @@ -987,6 +988,44 @@ public void onFailure(Exception e) {
// end::slm-delete-snapshot-lifecycle-policy-execute-async

assertTrue(deleteResp.isAcknowledged());

//////// EXECUTE RETENTION
// tag::slm-execute-snapshot-lifecycle-retention
ExecuteSnapshotLifecycleRetentionRequest req =
new ExecuteSnapshotLifecycleRetentionRequest();
// end::slm-execute-snapshot-lifecycle-retention

// tag::slm-execute-snapshot-lifecycle-retention-execute
AcknowledgedResponse retentionResp =
client.indexLifecycle()
.executeSnapshotLifecycleRetention(req,
RequestOptions.DEFAULT);
// end::slm-execute-snapshot-lifecycle-retention-execute

// tag::slm-execute-snapshot-lifecycle-retention-response
final boolean acked = retentionResp.isAcknowledged();
// end::slm-execute-snapshot-lifecycle-retention-response

// tag::slm-execute-snapshot-lifecycle-policy-execute-listener
ActionListener<AcknowledgedResponse> retentionListener =
new ActionListener<>() {
@Override
public void onResponse(AcknowledgedResponse r) {
assert r.isAcknowledged(); // <1>
}

@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::slm-execute-snapshot-lifecycle-retention-execute-listener

// tag::slm-execute-snapshot-lifecycle-retention-execute-async
client.indexLifecycle()
.executeSnapshotLifecycleRetentionAsync(req,
RequestOptions.DEFAULT, retentionListener);
// end::slm-execute-snapshot-lifecycle-retention-execute-async
}

private void assertSnapshotExists(final RestHighLevelClient client, final String repo, final String snapshotName) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--
:api: slm-execute-snapshot-lifecycle-retention
:request: ExecuteSnapshotLifecycleRetentionRequest
:response: AcknowledgedResponse
--
[role="xpack"]
[id="{upid}-{api}"]
=== Execute Snapshot Lifecycle Retention API


[id="{upid}-{api}-request"]
==== Request

The Execute Snapshot Lifecycle Retention API allows you to execute Snapshot Lifecycle Management
Retention immediately, rather than waiting for its regularly scheduled execution.

["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-request]
--------------------------------------------------

[id="{upid}-{api}-response"]
==== Response

The returned +{response}+ contains a boolean for whether the request was
acknowledged by the master node.

["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-response]
--------------------------------------------------

include::../execution.asciidoc[]


Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.core.slm.action;

import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;

public class ExecuteSnapshotRetentionAction extends ActionType<AcknowledgedResponse> {
public static final ExecuteSnapshotRetentionAction INSTANCE = new ExecuteSnapshotRetentionAction();
public static final String NAME = "cluster:admin/slm/execute-retention";

protected ExecuteSnapshotRetentionAction() {
super(NAME, AcknowledgedResponse::new);
}

public static class Request extends AcknowledgedRequest<ExecuteSnapshotRetentionAction.Request> implements ToXContentObject {

public Request() { }

public Request(StreamInput in) throws IOException {
super(in);
}

@Override
public ActionRequestValidationException validate() {
return null;
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.endObject();
return builder;
}

@Override
public int hashCode() {
return super.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()) {
return false;
}
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.elasticsearch.xpack.core.slm.SnapshotLifecycleMetadata;
import org.elasticsearch.xpack.core.slm.action.DeleteSnapshotLifecycleAction;
import org.elasticsearch.xpack.core.slm.action.ExecuteSnapshotLifecycleAction;
import org.elasticsearch.xpack.core.slm.action.ExecuteSnapshotRetentionAction;
import org.elasticsearch.xpack.core.slm.action.GetSnapshotLifecycleAction;
import org.elasticsearch.xpack.core.slm.action.GetSnapshotLifecycleStatsAction;
import org.elasticsearch.xpack.core.slm.action.PutSnapshotLifecycleAction;
Expand Down Expand Up @@ -96,11 +97,13 @@
import org.elasticsearch.xpack.slm.SnapshotRetentionTask;
import org.elasticsearch.xpack.slm.action.RestDeleteSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.RestExecuteSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.RestExecuteSnapshotRetentionAction;
import org.elasticsearch.xpack.slm.action.RestGetSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.RestGetSnapshotLifecycleStatsAction;
import org.elasticsearch.xpack.slm.action.RestPutSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.TransportDeleteSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.TransportExecuteSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.TransportExecuteSnapshotRetentionAction;
import org.elasticsearch.xpack.slm.action.TransportGetSnapshotLifecycleAction;
import org.elasticsearch.xpack.slm.action.TransportGetSnapshotLifecycleStatsAction;
import org.elasticsearch.xpack.slm.action.TransportPutSnapshotLifecycleAction;
Expand Down Expand Up @@ -230,7 +233,8 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
new RestDeleteSnapshotLifecycleAction(restController),
new RestGetSnapshotLifecycleAction(restController),
new RestExecuteSnapshotLifecycleAction(restController),
new RestGetSnapshotLifecycleStatsAction(restController)
new RestGetSnapshotLifecycleStatsAction(restController),
new RestExecuteSnapshotRetentionAction(restController)
));
}
return handlers;
Expand Down Expand Up @@ -265,7 +269,8 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
new ActionHandler<>(DeleteSnapshotLifecycleAction.INSTANCE, TransportDeleteSnapshotLifecycleAction.class),
new ActionHandler<>(GetSnapshotLifecycleAction.INSTANCE, TransportGetSnapshotLifecycleAction.class),
new ActionHandler<>(ExecuteSnapshotLifecycleAction.INSTANCE, TransportExecuteSnapshotLifecycleAction.class),
new ActionHandler<>(GetSnapshotLifecycleStatsAction.INSTANCE, TransportGetSnapshotLifecycleStatsAction.class)
new ActionHandler<>(GetSnapshotLifecycleStatsAction.INSTANCE, TransportGetSnapshotLifecycleStatsAction.class),
new ActionHandler<>(ExecuteSnapshotRetentionAction.INSTANCE, TransportExecuteSnapshotRetentionAction.class)
));
}
return actions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
public class SnapshotRetentionService implements LocalNodeMasterListener, Closeable {

static final String SLM_RETENTION_JOB_ID = "slm-retention-job";
static final String SLM_RETENTION_MANUAL_JOB_ID = "slm-execute-manual-retention-job";

private static final Logger logger = LogManager.getLogger(SnapshotRetentionService.class);

private final SchedulerEngine scheduler;
private final SnapshotRetentionTask retentionTask;
private final Clock clock;

private volatile String slmRetentionSchedule;
private volatile boolean isMaster = false;
Expand All @@ -43,8 +46,10 @@ public SnapshotRetentionService(Settings settings,
Supplier<SnapshotRetentionTask> taskSupplier,
ClusterService clusterService,
Clock clock) {
this.clock = clock;
this.scheduler = new SchedulerEngine(settings, clock);
this.scheduler.register(taskSupplier.get());
this.retentionTask = taskSupplier.get();
this.scheduler.register(this.retentionTask);
this.slmRetentionSchedule = LifecycleSettings.SLM_RETENTION_SCHEDULE_SETTING.get(settings);
clusterService.addLocalNodeMasterListener(this);
clusterService.getClusterSettings().addSettingsUpdateConsumer(LifecycleSettings.SLM_RETENTION_SCHEDULE_SETTING,
Expand Down Expand Up @@ -91,6 +96,16 @@ private void cancelRetentionJob() {
this.scheduler.scheduledJobIds().forEach(this.scheduler::remove);
}

/**
* Manually trigger snapshot retention
*/
public void triggerRetention() {
if (this.isMaster) {
long now = clock.millis();
this.retentionTask.triggered(new SchedulerEngine.Event(SLM_RETENTION_MANUAL_JOB_ID, now, now));
}
}

@Override
public String executorName() {
return ThreadPool.Names.SNAPSHOT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ public SnapshotRetentionTask(Client client, ClusterService clusterService, LongS

@Override
public void triggered(SchedulerEngine.Event event) {
assert event.getJobName().equals(SnapshotRetentionService.SLM_RETENTION_JOB_ID) :
"expected id to be " + SnapshotRetentionService.SLM_RETENTION_JOB_ID + " but it was " + event.getJobName();
assert event.getJobName().equals(SnapshotRetentionService.SLM_RETENTION_JOB_ID) ||
event.getJobName().equals(SnapshotRetentionService.SLM_RETENTION_MANUAL_JOB_ID):
"expected id to be " + SnapshotRetentionService.SLM_RETENTION_JOB_ID + " or " +
SnapshotRetentionService.SLM_RETENTION_MANUAL_JOB_ID + " but it was " + event.getJobName();

final ClusterState state = clusterService.state();
if (SnapshotLifecycleService.ilmStoppedOrStopping(state)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.slm.action;

import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.core.slm.action.ExecuteSnapshotRetentionAction;

public class RestExecuteSnapshotRetentionAction extends BaseRestHandler {

public RestExecuteSnapshotRetentionAction(RestController controller) {
controller.registerHandler(RestRequest.Method.POST, "/_slm/_execute_retention", this);
}

@Override
public String getName() {
return "slm_execute_retention";
}

@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) {
ExecuteSnapshotRetentionAction.Request req = new ExecuteSnapshotRetentionAction.Request();
req.timeout(request.paramAsTime("timeout", req.timeout()));
req.masterNodeTimeout(request.paramAsTime("master_timeout", req.masterNodeTimeout()));
return channel -> client.execute(ExecuteSnapshotRetentionAction.INSTANCE, req, new RestToXContentListener<>(channel));
}
}
Loading