Skip to content

Commit aa11a9d

Browse files
committed
Introduce retention lease actions
This commit introduces actions for some common retention lease operations that clients need to be able to perform remotely. These actions include add/renew/remove.
1 parent ec4f487 commit aa11a9d

File tree

10 files changed

+560
-15
lines changed

10 files changed

+560
-15
lines changed

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
package org.elasticsearch.action;
2121

22-
import org.apache.logging.log4j.Logger;
2322
import org.apache.logging.log4j.LogManager;
23+
import org.apache.logging.log4j.Logger;
2424
import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainAction;
2525
import org.elasticsearch.action.admin.cluster.allocation.TransportClusterAllocationExplainAction;
2626
import org.elasticsearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction;
@@ -209,6 +209,7 @@
209209
import org.elasticsearch.common.settings.IndexScopedSettings;
210210
import org.elasticsearch.common.settings.Settings;
211211
import org.elasticsearch.common.settings.SettingsFilter;
212+
import org.elasticsearch.index.seqno.RetentionLeaseActions;
212213
import org.elasticsearch.indices.breaker.CircuitBreakerService;
213214
import org.elasticsearch.persistent.CompletionPersistentTaskAction;
214215
import org.elasticsearch.persistent.RemovePersistentTaskAction;
@@ -220,6 +221,7 @@
220221
import org.elasticsearch.rest.RestHandler;
221222
import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
222223
import org.elasticsearch.rest.action.RestMainAction;
224+
import org.elasticsearch.rest.action.admin.cluster.RestAddVotingConfigExclusionAction;
223225
import org.elasticsearch.rest.action.admin.cluster.RestCancelTasksAction;
224226
import org.elasticsearch.rest.action.admin.cluster.RestClearVotingConfigExclusionsAction;
225227
import org.elasticsearch.rest.action.admin.cluster.RestClusterAllocationExplainAction;
@@ -251,7 +253,6 @@
251253
import org.elasticsearch.rest.action.admin.cluster.RestRestoreSnapshotAction;
252254
import org.elasticsearch.rest.action.admin.cluster.RestSnapshotsStatusAction;
253255
import org.elasticsearch.rest.action.admin.cluster.RestVerifyRepositoryAction;
254-
import org.elasticsearch.rest.action.admin.cluster.RestAddVotingConfigExclusionAction;
255256
import org.elasticsearch.rest.action.admin.indices.RestAnalyzeAction;
256257
import org.elasticsearch.rest.action.admin.indices.RestClearIndicesCacheAction;
257258
import org.elasticsearch.rest.action.admin.indices.RestCloseIndexAction;
@@ -529,6 +530,10 @@ public <Request extends ActionRequest, Response extends ActionResponse> void reg
529530
actions.register(CompletionPersistentTaskAction.INSTANCE, CompletionPersistentTaskAction.TransportAction.class);
530531
actions.register(RemovePersistentTaskAction.INSTANCE, RemovePersistentTaskAction.TransportAction.class);
531532

533+
// retention leases
534+
actions.register(RetentionLeaseActions.Add.INSTANCE, RetentionLeaseActions.Add.TransportAction.class);
535+
actions.register(RetentionLeaseActions.Renew.INSTANCE, RetentionLeaseActions.Renew.TransportAction.class);
536+
532537
return unmodifiableMap(actions.getRegistry());
533538
}
534539

server/src/main/java/org/elasticsearch/index/engine/Engine.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ public enum SearcherScope {
744744
/**
745745
* Acquires a lock on the translog files and Lucene soft-deleted documents to prevent them from being trimmed
746746
*/
747-
public abstract Closeable acquireRetentionLockForPeerRecovery();
747+
public abstract Closeable acquireRetentionLock();
748748

749749
/**
750750
* Creates a new history snapshot from Lucene for reading operations whose seqno in the requesting seqno range (both inclusive).
@@ -771,6 +771,13 @@ public abstract int estimateNumberOfHistoryOperations(String source,
771771
*/
772772
public abstract boolean hasCompleteOperationHistory(String source, MapperService mapperService, long startingSeqNo) throws IOException;
773773

774+
/**
775+
* Gets the minimum retained sequence number for this engine.
776+
*
777+
* @return the minimum retained sequence number
778+
*/
779+
public abstract long getMinRetainedSeqNo();
780+
774781
public abstract TranslogStats getTranslogStats();
775782

776783
/**

server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,13 +2567,13 @@ public boolean hasCompleteOperationHistory(String source, MapperService mapperSe
25672567
* Returns the minimum seqno that is retained in the Lucene index.
25682568
* Operations whose seq# are at least this value should exist in the Lucene index.
25692569
*/
2570-
final long getMinRetainedSeqNo() {
2570+
public final long getMinRetainedSeqNo() {
25712571
assert softDeleteEnabled : Thread.currentThread().getName();
25722572
return softDeletesPolicy.getMinRetainedSeqNo();
25732573
}
25742574

25752575
@Override
2576-
public Closeable acquireRetentionLockForPeerRecovery() {
2576+
public Closeable acquireRetentionLock() {
25772577
if (softDeleteEnabled) {
25782578
return softDeletesPolicy.acquireRetentionLock();
25792579
} else {

server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public void syncTranslog() {
272272
}
273273

274274
@Override
275-
public Closeable acquireRetentionLockForPeerRecovery() {
275+
public Closeable acquireRetentionLock() {
276276
return () -> {};
277277
}
278278

@@ -311,6 +311,11 @@ public boolean hasCompleteOperationHistory(String source, MapperService mapperSe
311311
return false;
312312
}
313313

314+
@Override
315+
public long getMinRetainedSeqNo() {
316+
throw new UnsupportedOperationException();
317+
}
318+
314319
@Override
315320
public TranslogStats getTranslogStats() {
316321
return translogStats;
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.seqno;
21+
22+
import org.apache.logging.log4j.LogManager;
23+
import org.apache.logging.log4j.Logger;
24+
import org.elasticsearch.action.Action;
25+
import org.elasticsearch.action.ActionListener;
26+
import org.elasticsearch.action.ActionRequestValidationException;
27+
import org.elasticsearch.action.ActionResponse;
28+
import org.elasticsearch.action.support.ActionFilters;
29+
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
30+
import org.elasticsearch.action.support.single.shard.TransportSingleShardAction;
31+
import org.elasticsearch.cluster.ClusterState;
32+
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
33+
import org.elasticsearch.cluster.routing.ShardsIterator;
34+
import org.elasticsearch.cluster.service.ClusterService;
35+
import org.elasticsearch.common.inject.Inject;
36+
import org.elasticsearch.common.io.stream.StreamInput;
37+
import org.elasticsearch.common.io.stream.StreamOutput;
38+
import org.elasticsearch.common.lease.Releasable;
39+
import org.elasticsearch.common.util.concurrent.FutureUtils;
40+
import org.elasticsearch.index.IndexService;
41+
import org.elasticsearch.index.shard.IndexShard;
42+
import org.elasticsearch.index.shard.ShardId;
43+
import org.elasticsearch.indices.IndicesService;
44+
import org.elasticsearch.threadpool.ThreadPool;
45+
import org.elasticsearch.transport.TransportService;
46+
47+
import java.io.IOException;
48+
import java.util.Objects;
49+
import java.util.concurrent.CompletableFuture;
50+
51+
public class RetentionLeaseActions {
52+
53+
static abstract class TransportRetentionLeaseAction extends TransportSingleShardAction<Request, Response> {
54+
55+
private Logger logger = LogManager.getLogger(getClass());
56+
57+
private final IndicesService indicesService;
58+
59+
@Inject
60+
public TransportRetentionLeaseAction(
61+
final String name,
62+
final ThreadPool threadPool,
63+
final ClusterService clusterService,
64+
final TransportService transportService,
65+
final ActionFilters actionFilters,
66+
final IndexNameExpressionResolver indexNameExpressionResolver,
67+
final IndicesService indicesService) {
68+
super(name, threadPool, clusterService, transportService, actionFilters, indexNameExpressionResolver, Request::new, ThreadPool.Names.MANAGEMENT);
69+
this.indicesService = Objects.requireNonNull(indicesService);
70+
}
71+
72+
@Override
73+
protected ShardsIterator shards(ClusterState state, InternalRequest request) {
74+
return state
75+
.routingTable()
76+
.shardRoutingTable(request.concreteIndex(), request.request().getShardId().id())
77+
.primaryShardIt();
78+
}
79+
80+
@Override
81+
protected Response shardOperation(final Request request, final ShardId shardId) {
82+
final IndexService indexService = indicesService.indexServiceSafe(request.getShardId().getIndex());
83+
final IndexShard indexShard = indexService.getShard(request.getShardId().id());
84+
85+
final CompletableFuture<Releasable> permit = new CompletableFuture<>();
86+
final ActionListener<Releasable> onAcquired = new ActionListener<Releasable>() {
87+
88+
@Override
89+
public void onResponse(Releasable releasable) {
90+
if (permit.complete(releasable) == false) {
91+
releasable.close();
92+
}
93+
}
94+
95+
@Override
96+
public void onFailure(Exception e) {
97+
permit.completeExceptionally(e);
98+
}
99+
100+
};
101+
102+
indexShard.acquirePrimaryOperationPermit(onAcquired, ThreadPool.Names.SAME, request);
103+
104+
// block until we have the permit
105+
try (Releasable ignore = FutureUtils.get(permit)) {
106+
doRetentionLeaseAction(indexShard, request);
107+
} finally {
108+
// just in case we got an exception (likely interrupted) while waiting for the get
109+
permit.whenComplete((r, e) -> {
110+
if (r != null) {
111+
r.close();
112+
}
113+
if (e != null) {
114+
logger.trace("suppressing exception on completion (it was already bubbled up or the operation was aborted)", e);
115+
}
116+
});
117+
}
118+
119+
return new Response();
120+
}
121+
122+
abstract void doRetentionLeaseAction(IndexShard indexShard, Request request);
123+
124+
@Override
125+
protected Response newResponse() {
126+
return new Response();
127+
}
128+
129+
@Override
130+
protected boolean resolveIndex(final Request request) {
131+
return false;
132+
}
133+
134+
}
135+
136+
public static class Add extends Action<Response> {
137+
138+
public static final Add INSTANCE = new Add();
139+
public static final String NAME = "indices:admin/seq_no/add_retention_lease";
140+
141+
private Add() {
142+
super(NAME);
143+
}
144+
145+
public static class TransportAction extends TransportRetentionLeaseAction {
146+
147+
@Inject
148+
public TransportAction(
149+
final ThreadPool threadPool,
150+
final ClusterService clusterService,
151+
final TransportService transportService,
152+
final ActionFilters actionFilters,
153+
final IndexNameExpressionResolver indexNameExpressionResolver,
154+
final IndicesService indicesService) {
155+
super(NAME, threadPool, clusterService, transportService, actionFilters, indexNameExpressionResolver, indicesService);
156+
}
157+
158+
@Override
159+
void doRetentionLeaseAction(final IndexShard indexShard, final Request request) {
160+
indexShard.addRetentionLease(request.getId(), request.getRetainingSequenceNumber(), request.getSource(), ActionListener.wrap(() -> {}));
161+
}
162+
}
163+
164+
@Override
165+
public Response newResponse() {
166+
return new Response();
167+
}
168+
169+
}
170+
171+
public static class Renew extends Action<Response> {
172+
173+
public static final Renew INSTANCE = new Renew();
174+
public static final String NAME = "indices:admin/seq_no/renew_retention_lease";
175+
176+
private Renew() {
177+
super(NAME);
178+
}
179+
180+
public static class TransportAction extends TransportRetentionLeaseAction {
181+
182+
@Inject
183+
public TransportAction(
184+
final ThreadPool threadPool,
185+
final ClusterService clusterService,
186+
final TransportService transportService,
187+
final ActionFilters actionFilters,
188+
final IndexNameExpressionResolver indexNameExpressionResolver,
189+
final IndicesService indicesService) {
190+
super(NAME, threadPool, clusterService, transportService, actionFilters, indexNameExpressionResolver, indicesService);
191+
}
192+
193+
194+
@Override
195+
void doRetentionLeaseAction(final IndexShard indexShard, final Request request) {
196+
indexShard.renewRetentionLease(request.getId(), request.getRetainingSequenceNumber(), request.getSource());
197+
}
198+
}
199+
200+
@Override
201+
public Response newResponse() {
202+
return new Response();
203+
}
204+
205+
}
206+
207+
public static class Request extends SingleShardRequest<Request> {
208+
209+
public static long RETAIN_ALL = -1;
210+
211+
private ShardId shardId;
212+
213+
public ShardId getShardId() {
214+
return shardId;
215+
}
216+
217+
private String id;
218+
219+
public String getId() {
220+
return id;
221+
}
222+
223+
private long retainingSequenceNumber;
224+
225+
public long getRetainingSequenceNumber() {
226+
return retainingSequenceNumber;
227+
}
228+
229+
private String source;
230+
231+
public String getSource() {
232+
return source;
233+
}
234+
235+
public Request() {
236+
}
237+
238+
public Request(final ShardId shardId, final String id, final long retainingSequenceNumber, final String source) {
239+
super(Objects.requireNonNull(shardId).getIndexName());
240+
this.shardId = shardId;
241+
this.id = Objects.requireNonNull(id);
242+
if (retainingSequenceNumber < 0 && retainingSequenceNumber != RETAIN_ALL) {
243+
throw new IllegalArgumentException(
244+
"retention lease retaining sequence number [" + retainingSequenceNumber + "] out of range");
245+
}
246+
this.retainingSequenceNumber = retainingSequenceNumber;
247+
this.source = Objects.requireNonNull(source);
248+
}
249+
250+
@Override
251+
public ActionRequestValidationException validate() {
252+
return null;
253+
}
254+
255+
@Override
256+
public void readFrom(StreamInput in) throws IOException {
257+
super.readFrom(in);
258+
shardId = ShardId.readShardId(in);
259+
id = in.readString();
260+
retainingSequenceNumber = in.readZLong();
261+
source = in.readString();
262+
}
263+
264+
@Override
265+
public void writeTo(StreamOutput out) throws IOException {
266+
super.writeTo(out);
267+
shardId.writeTo(out);
268+
out.writeString(id);
269+
out.writeZLong(retainingSequenceNumber);
270+
out.writeString(source);
271+
}
272+
273+
}
274+
275+
public static class Response extends ActionResponse {
276+
277+
}
278+
279+
}

0 commit comments

Comments
 (0)