Skip to content

Commit 5718b14

Browse files
committed
Add a java level freeze/unfreeze API (#35353)
This change adds a high level freeze API that allows to mark an index as frozen and vice versa. Indices must be closed in order to become frozen and an open but frozen index must be closed to be defrosted. This change also adds a index.frozen setting to mark frozen indices and integrates the frozen engine with the SearchOperationListener that resets and releases the directory reader after and before search phases. Relates to #34352 Depends on #34357
1 parent 9bdea09 commit 5718b14

File tree

5 files changed

+535
-0
lines changed

5 files changed

+535
-0
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/index/engine/FrozenEngine.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
import org.apache.lucene.util.Bits;
3232
import org.elasticsearch.common.SuppressForbidden;
3333
import org.elasticsearch.common.lucene.Lucene;
34+
import org.elasticsearch.common.settings.Setting;
3435
import org.elasticsearch.core.internal.io.IOUtils;
36+
import org.elasticsearch.index.shard.SearchOperationListener;
37+
import org.elasticsearch.search.internal.SearchContext;
38+
import org.elasticsearch.transport.TransportRequest;
3539

3640
import java.io.IOException;
3741
import java.io.UncheckedIOException;
@@ -59,6 +63,8 @@
5963
* stats in order to obtain the number of reopens.
6064
*/
6165
public final class FrozenEngine extends ReadOnlyEngine {
66+
public static final Setting<Boolean> INDEX_FROZEN = Setting.boolSetting("index.frozen", false, Setting.Property.IndexScope,
67+
Setting.Property.PrivateIndex);
6268
private volatile DirectoryReader lastOpenedReader;
6369

6470
public FrozenEngine(EngineConfig config) {
@@ -232,6 +238,49 @@ static LazyDirectoryReader unwrapLazyReader(DirectoryReader reader) {
232238
return null;
233239
}
234240

241+
/*
242+
* We register this listener for a frozen index that will
243+
* 1. reset the reader every time the search context is validated which happens when the context is looked up ie. on a fetch phase
244+
* etc.
245+
* 2. register a releasable resource that is cleaned after each phase that releases the reader for this searcher
246+
*/
247+
public static class ReacquireEngineSearcherListener implements SearchOperationListener {
248+
249+
@Override
250+
public void validateSearchContext(SearchContext context, TransportRequest transportRequest) {
251+
Searcher engineSearcher = context.searcher().getEngineSearcher();
252+
LazyDirectoryReader lazyDirectoryReader = unwrapLazyReader(engineSearcher.getDirectoryReader());
253+
if (lazyDirectoryReader != null) {
254+
try {
255+
lazyDirectoryReader.reset();
256+
} catch (IOException e) {
257+
throw new UncheckedIOException(e);
258+
}
259+
// also register a release resource in this case if we have multiple roundtrips like in DFS
260+
registerRelease(context, lazyDirectoryReader);
261+
}
262+
}
263+
264+
private void registerRelease(SearchContext context, LazyDirectoryReader lazyDirectoryReader) {
265+
context.addReleasable(() -> {
266+
try {
267+
lazyDirectoryReader.release();
268+
} catch (IOException e) {
269+
throw new UncheckedIOException(e);
270+
}
271+
}, SearchContext.Lifetime.PHASE);
272+
}
273+
274+
@Override
275+
public void onNewContext(SearchContext context) {
276+
Searcher engineSearcher = context.searcher().getEngineSearcher();
277+
LazyDirectoryReader lazyDirectoryReader = unwrapLazyReader(engineSearcher.getDirectoryReader());
278+
if (lazyDirectoryReader != null) {
279+
registerRelease(context, lazyDirectoryReader);
280+
}
281+
}
282+
}
283+
235284
/**
236285
* This class allows us to use the same high level reader across multiple search phases but replace the underpinnings
237286
* on/after each search phase. This is really important otherwise we would hold on to multiple readers across phases.

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
package org.elasticsearch.xpack.core;
77

88
import org.elasticsearch.action.ActionListener;
9+
import org.elasticsearch.action.support.master.AcknowledgedResponse;
910
import org.elasticsearch.client.Client;
1011
import org.elasticsearch.common.settings.SecureString;
1112
import org.elasticsearch.license.LicensingClient;
1213
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
1314
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
15+
import org.elasticsearch.xpack.core.action.TransportFreezeIndexAction;
1416
import org.elasticsearch.xpack.core.action.XPackInfoAction;
1517
import org.elasticsearch.xpack.core.action.XPackInfoRequestBuilder;
1618
import org.elasticsearch.xpack.core.ccr.client.CcrClient;
@@ -103,4 +105,8 @@ public XPackInfoRequestBuilder prepareInfo() {
103105
public void info(XPackInfoRequest request, ActionListener<XPackInfoResponse> listener) {
104106
client.execute(XPackInfoAction.INSTANCE, request, listener);
105107
}
108+
109+
public void freeze(TransportFreezeIndexAction.FreezeRequest request, ActionListener<AcknowledgedResponse> listener) {
110+
client.execute(TransportFreezeIndexAction.FreezeIndexAction.INSTANCE, request, listener);
111+
}
106112
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
3838
import org.elasticsearch.env.Environment;
3939
import org.elasticsearch.env.NodeEnvironment;
40+
import org.elasticsearch.index.IndexModule;
4041
import org.elasticsearch.index.IndexSettings;
4142
import org.elasticsearch.index.engine.EngineFactory;
43+
import org.elasticsearch.index.engine.FrozenEngine;
4244
import org.elasticsearch.license.LicenseService;
4345
import org.elasticsearch.license.LicensesMetaData;
4446
import org.elasticsearch.license.Licensing;
@@ -55,6 +57,7 @@
5557
import org.elasticsearch.snapshots.SourceOnlySnapshotRepository;
5658
import org.elasticsearch.threadpool.ThreadPool;
5759
import org.elasticsearch.watcher.ResourceWatcherService;
60+
import org.elasticsearch.xpack.core.action.TransportFreezeIndexAction;
5861
import org.elasticsearch.xpack.core.action.TransportXPackInfoAction;
5962
import org.elasticsearch.xpack.core.action.TransportXPackUsageAction;
6063
import org.elasticsearch.xpack.core.action.XPackInfoAction;
@@ -266,6 +269,8 @@ public Collection<Object> createComponents(Client client, ClusterService cluster
266269
List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> actions = new ArrayList<>();
267270
actions.add(new ActionHandler<>(XPackInfoAction.INSTANCE, TransportXPackInfoAction.class));
268271
actions.add(new ActionHandler<>(XPackUsageAction.INSTANCE, TransportXPackUsageAction.class));
272+
actions.add(new ActionHandler<>(TransportFreezeIndexAction.FreezeIndexAction.INSTANCE,
273+
TransportFreezeIndexAction.class));
269274
actions.addAll(licensing.getActions());
270275
return actions;
271276
}
@@ -359,14 +364,26 @@ public Map<String, Repository.Factory> getRepositories(Environment env, NamedXCo
359364
public Optional<EngineFactory> getEngineFactory(IndexSettings indexSettings) {
360365
if (indexSettings.getValue(SourceOnlySnapshotRepository.SOURCE_ONLY)) {
361366
return Optional.of(SourceOnlySnapshotRepository.getEngineFactory());
367+
} else if (indexSettings.getValue(FrozenEngine.INDEX_FROZEN)) {
368+
return Optional.of(FrozenEngine::new);
362369
}
370+
363371
return Optional.empty();
364372
}
365373

366374
@Override
367375
public List<Setting<?>> getSettings() {
368376
List<Setting<?>> settings = super.getSettings();
369377
settings.add(SourceOnlySnapshotRepository.SOURCE_ONLY);
378+
settings.add(FrozenEngine.INDEX_FROZEN);
370379
return settings;
371380
}
381+
382+
@Override
383+
public void onIndexModule(IndexModule indexModule) {
384+
if (FrozenEngine.INDEX_FROZEN.get(indexModule.getSettings())) {
385+
indexModule.addSearchOperationListener(new FrozenEngine.ReacquireEngineSearcherListener());
386+
}
387+
super.onIndexModule(indexModule);
388+
}
372389
}

0 commit comments

Comments
 (0)