Skip to content

Commit 74ff50f

Browse files
authored
Omit loading IndexMetaData when inspecting shards (#50214)
Loading shard state information during shard allocation sometimes runs into a situation where a data node does not know yet how to look up the shard on disk if custom data paths are used. The current implementation loads the index metadata from disk to determine what the custom data path looks like. This PR removes this dependency, simplifying the lookup. Relates #48701
1 parent ac90145 commit 74ff50f

16 files changed

+246
-164
lines changed

server/src/main/java/org/elasticsearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java

+17-14
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.elasticsearch.cluster.block.ClusterBlockLevel;
3333
import org.elasticsearch.cluster.health.ClusterHealthStatus;
3434
import org.elasticsearch.cluster.health.ClusterShardHealth;
35+
import org.elasticsearch.cluster.metadata.IndexMetaData;
3536
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
3637
import org.elasticsearch.cluster.node.DiscoveryNode;
3738
import org.elasticsearch.cluster.node.DiscoveryNodes;
@@ -43,6 +44,7 @@
4344
import org.elasticsearch.cluster.service.ClusterService;
4445
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
4546
import org.elasticsearch.common.collect.ImmutableOpenMap;
47+
import org.elasticsearch.common.collect.Tuple;
4648
import org.elasticsearch.common.inject.Inject;
4749
import org.elasticsearch.common.io.stream.StreamInput;
4850
import org.elasticsearch.common.util.concurrent.CountDown;
@@ -100,7 +102,7 @@ protected void masterOperation(Task task, IndicesShardStoresRequest request, Clu
100102
final RoutingTable routingTables = state.routingTable();
101103
final RoutingNodes routingNodes = state.getRoutingNodes();
102104
final String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request);
103-
final Set<ShardId> shardIdsToFetch = new HashSet<>();
105+
final Set<Tuple<ShardId, String>> shardsToFetch = new HashSet<>();
104106

105107
logger.trace("using cluster state version [{}] to determine shards", state.version());
106108
// collect relevant shard ids of the requested indices for fetching store infos
@@ -109,11 +111,12 @@ protected void masterOperation(Task task, IndicesShardStoresRequest request, Clu
109111
if (indexShardRoutingTables == null) {
110112
continue;
111113
}
114+
final String customDataPath = IndexMetaData.INDEX_DATA_PATH_SETTING.get(state.metaData().index(index).getSettings());
112115
for (IndexShardRoutingTable routing : indexShardRoutingTables) {
113116
final int shardId = routing.shardId().id();
114117
ClusterShardHealth shardHealth = new ClusterShardHealth(shardId, routing);
115118
if (request.shardStatuses().contains(shardHealth.getStatus())) {
116-
shardIdsToFetch.add(routing.shardId());
119+
shardsToFetch.add(Tuple.tuple(routing.shardId(), customDataPath));
117120
}
118121
}
119122
}
@@ -123,7 +126,7 @@ protected void masterOperation(Task task, IndicesShardStoresRequest request, Clu
123126
// we could fetch all shard store info from every node once (nNodes requests)
124127
// we have to implement a TransportNodesAction instead of using TransportNodesListGatewayStartedShards
125128
// for fetching shard stores info, that operates on a list of shards instead of a single shard
126-
new AsyncShardStoresInfoFetches(state.nodes(), routingNodes, shardIdsToFetch, listener).start();
129+
new AsyncShardStoresInfoFetches(state.nodes(), routingNodes, shardsToFetch, listener).start();
127130
}
128131

129132
@Override
@@ -135,46 +138,46 @@ protected ClusterBlockException checkBlock(IndicesShardStoresRequest request, Cl
135138
private class AsyncShardStoresInfoFetches {
136139
private final DiscoveryNodes nodes;
137140
private final RoutingNodes routingNodes;
138-
private final Set<ShardId> shardIds;
141+
private final Set<Tuple<ShardId, String>> shards;
139142
private final ActionListener<IndicesShardStoresResponse> listener;
140143
private CountDown expectedOps;
141144
private final Queue<InternalAsyncFetch.Response> fetchResponses;
142145

143-
AsyncShardStoresInfoFetches(DiscoveryNodes nodes, RoutingNodes routingNodes, Set<ShardId> shardIds,
146+
AsyncShardStoresInfoFetches(DiscoveryNodes nodes, RoutingNodes routingNodes, Set<Tuple<ShardId, String>> shards,
144147
ActionListener<IndicesShardStoresResponse> listener) {
145148
this.nodes = nodes;
146149
this.routingNodes = routingNodes;
147-
this.shardIds = shardIds;
150+
this.shards = shards;
148151
this.listener = listener;
149152
this.fetchResponses = new ConcurrentLinkedQueue<>();
150-
this.expectedOps = new CountDown(shardIds.size());
153+
this.expectedOps = new CountDown(shards.size());
151154
}
152155

153156
void start() {
154-
if (shardIds.isEmpty()) {
157+
if (shards.isEmpty()) {
155158
listener.onResponse(new IndicesShardStoresResponse());
156159
} else {
157160
// explicitely type lister, some IDEs (Eclipse) are not able to correctly infer the function type
158161
Lister<BaseNodesResponse<NodeGatewayStartedShards>, NodeGatewayStartedShards> lister = this::listStartedShards;
159-
for (ShardId shardId : shardIds) {
160-
InternalAsyncFetch fetch = new InternalAsyncFetch(logger, "shard_stores", shardId, lister);
162+
for (Tuple<ShardId, String> shard : shards) {
163+
InternalAsyncFetch fetch = new InternalAsyncFetch(logger, "shard_stores", shard.v1(), shard.v2(), lister);
161164
fetch.fetchData(nodes, Collections.<String>emptySet());
162165
}
163166
}
164167
}
165168

166-
private void listStartedShards(ShardId shardId, DiscoveryNode[] nodes,
169+
private void listStartedShards(ShardId shardId, String customDataPath, DiscoveryNode[] nodes,
167170
ActionListener<BaseNodesResponse<NodeGatewayStartedShards>> listener) {
168-
var request = new TransportNodesListGatewayStartedShards.Request(shardId, nodes);
171+
var request = new TransportNodesListGatewayStartedShards.Request(shardId, customDataPath, nodes);
169172
client.executeLocally(TransportNodesListGatewayStartedShards.TYPE, request,
170173
ActionListener.wrap(listener::onResponse, listener::onFailure));
171174
}
172175

173176
private class InternalAsyncFetch extends AsyncShardFetch<NodeGatewayStartedShards> {
174177

175-
InternalAsyncFetch(Logger logger, String type, ShardId shardId,
178+
InternalAsyncFetch(Logger logger, String type, ShardId shardId, String customDataPath,
176179
Lister<? extends BaseNodesResponse<NodeGatewayStartedShards>, NodeGatewayStartedShards> action) {
177-
super(logger, type, shardId, action);
180+
super(logger, type, shardId, customDataPath, action);
178181
}
179182

180183
@Override

server/src/main/java/org/elasticsearch/env/NodeEnvironment.java

+18-21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.logging.log4j.LogManager;
2323
import org.apache.logging.log4j.Logger;
2424
import org.apache.logging.log4j.message.ParameterizedMessage;
25+
import org.apache.logging.log4j.util.Strings;
2526
import org.apache.lucene.index.IndexWriter;
2627
import org.apache.lucene.index.SegmentInfos;
2728
import org.apache.lucene.store.Directory;
@@ -610,7 +611,7 @@ public void deleteShardDirectoryUnderLock(ShardLock lock, IndexSettings indexSet
610611
acquireFSLockForPaths(indexSettings, paths);
611612
IOUtils.rm(paths);
612613
if (indexSettings.hasCustomDataPath()) {
613-
Path customLocation = resolveCustomLocation(indexSettings, shardId);
614+
Path customLocation = resolveCustomLocation(indexSettings.customDataPath(), shardId);
614615
logger.trace("acquiring lock for {}, custom path: [{}]", shardId, customLocation);
615616
acquireFSLockForPaths(indexSettings, customLocation);
616617
logger.trace("deleting custom shard {} directory [{}]", shardId, customLocation);
@@ -687,7 +688,7 @@ public void deleteIndexDirectoryUnderLock(Index index, IndexSettings indexSettin
687688
logger.trace("deleting index {} directory, paths({}): [{}]", index, indexPaths.length, indexPaths);
688689
IOUtils.rm(indexPaths);
689690
if (indexSettings.hasCustomDataPath()) {
690-
Path customLocation = resolveIndexCustomLocation(indexSettings);
691+
Path customLocation = resolveIndexCustomLocation(indexSettings.customDataPath(), index.getUUID());
691692
logger.trace("deleting custom index {} directory [{}]", index, customLocation);
692693
IOUtils.rm(customLocation);
693694
}
@@ -933,7 +934,7 @@ public Path[] indexPaths(Index index) {
933934
* returned paths. The returned array may contain paths to non-existing directories.
934935
*
935936
* @see IndexSettings#hasCustomDataPath()
936-
* @see #resolveCustomLocation(IndexSettings, ShardId)
937+
* @see #resolveCustomLocation(String, ShardId)
937938
*
938939
*/
939940
public Path[] availableShardPaths(ShardId shardId) {
@@ -1233,17 +1234,12 @@ private static boolean isIndexMetaDataPath(Path path) {
12331234

12341235
/**
12351236
* Resolve the custom path for a index's shard.
1236-
* Uses the {@code IndexMetaData.SETTING_DATA_PATH} setting to determine
1237-
* the root path for the index.
1238-
*
1239-
* @param indexSettings settings for the index
12401237
*/
1241-
public static Path resolveBaseCustomLocation(IndexSettings indexSettings, Path sharedDataPath) {
1242-
String customDataDir = indexSettings.customDataPath();
1243-
if (customDataDir != null) {
1238+
public static Path resolveBaseCustomLocation(String customDataPath, Path sharedDataPath) {
1239+
if (Strings.isNotEmpty(customDataPath)) {
12441240
// This assert is because this should be caught by MetaDataCreateIndexService
12451241
assert sharedDataPath != null;
1246-
return sharedDataPath.resolve(customDataDir).resolve("0");
1242+
return sharedDataPath.resolve(customDataPath).resolve("0");
12471243
} else {
12481244
throw new IllegalArgumentException("no custom " + IndexMetaData.SETTING_DATA_PATH + " setting available");
12491245
}
@@ -1254,30 +1250,31 @@ public static Path resolveBaseCustomLocation(IndexSettings indexSettings, Path s
12541250
* Uses the {@code IndexMetaData.SETTING_DATA_PATH} setting to determine
12551251
* the root path for the index.
12561252
*
1257-
* @param indexSettings settings for the index
1253+
* @param customDataPath the custom data path
12581254
*/
1259-
private Path resolveIndexCustomLocation(IndexSettings indexSettings) {
1260-
return resolveIndexCustomLocation(indexSettings, sharedDataPath);
1255+
private Path resolveIndexCustomLocation(String customDataPath, String indexUUID) {
1256+
return resolveIndexCustomLocation(customDataPath, indexUUID, sharedDataPath);
12611257
}
12621258

1263-
private static Path resolveIndexCustomLocation(IndexSettings indexSettings, Path sharedDataPath) {
1264-
return resolveBaseCustomLocation(indexSettings, sharedDataPath).resolve(indexSettings.getUUID());
1259+
private static Path resolveIndexCustomLocation(String customDataPath, String indexUUID, Path sharedDataPath) {
1260+
return resolveBaseCustomLocation(customDataPath, sharedDataPath).resolve(indexUUID);
12651261
}
12661262

12671263
/**
12681264
* Resolve the custom path for a index's shard.
12691265
* Uses the {@code IndexMetaData.SETTING_DATA_PATH} setting to determine
12701266
* the root path for the index.
12711267
*
1272-
* @param indexSettings settings for the index
1268+
* @param customDataPath the custom data path
12731269
* @param shardId shard to resolve the path to
12741270
*/
1275-
public Path resolveCustomLocation(IndexSettings indexSettings, final ShardId shardId) {
1276-
return resolveCustomLocation(indexSettings, shardId, sharedDataPath);
1271+
public Path resolveCustomLocation(String customDataPath, final ShardId shardId) {
1272+
return resolveCustomLocation(customDataPath, shardId, sharedDataPath);
12771273
}
12781274

1279-
public static Path resolveCustomLocation(IndexSettings indexSettings, final ShardId shardId, Path sharedDataPath) {
1280-
return resolveIndexCustomLocation(indexSettings, sharedDataPath).resolve(Integer.toString(shardId.id()));
1275+
public static Path resolveCustomLocation(String customDataPath, final ShardId shardId, Path sharedDataPath) {
1276+
return resolveIndexCustomLocation(customDataPath, shardId.getIndex().getUUID(),
1277+
sharedDataPath).resolve(Integer.toString(shardId.id()));
12811278
}
12821279

12831280
/**

server/src/main/java/org/elasticsearch/gateway/AsyncShardFetch.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.Iterator;
4343
import java.util.List;
4444
import java.util.Map;
45+
import java.util.Objects;
4546
import java.util.Set;
4647
import java.util.concurrent.atomic.AtomicLong;
4748

@@ -61,22 +62,25 @@ public abstract class AsyncShardFetch<T extends BaseNodeResponse> implements Rel
6162
* An action that lists the relevant shard data that needs to be fetched.
6263
*/
6364
public interface Lister<NodesResponse extends BaseNodesResponse<NodeResponse>, NodeResponse extends BaseNodeResponse> {
64-
void list(ShardId shardId, DiscoveryNode[] nodes, ActionListener<NodesResponse> listener);
65+
void list(ShardId shardId, @Nullable String customDataPath, DiscoveryNode[] nodes, ActionListener<NodesResponse> listener);
6566
}
6667

6768
protected final Logger logger;
6869
protected final String type;
6970
protected final ShardId shardId;
71+
protected final String customDataPath;
7072
private final Lister<BaseNodesResponse<T>, T> action;
7173
private final Map<String, NodeEntry<T>> cache = new HashMap<>();
7274
private final Set<String> nodesToIgnore = new HashSet<>();
7375
private final AtomicLong round = new AtomicLong();
7476
private boolean closed;
7577

76-
protected AsyncShardFetch(Logger logger, String type, ShardId shardId, Lister<? extends BaseNodesResponse<T>, T> action) {
78+
protected AsyncShardFetch(Logger logger, String type, ShardId shardId, String customDataPath,
79+
Lister<? extends BaseNodesResponse<T>, T> action) {
7780
this.logger = logger;
7881
this.type = type;
79-
this.shardId = shardId;
82+
this.shardId = Objects.requireNonNull(shardId);
83+
this.customDataPath = Objects.requireNonNull(customDataPath);
8084
this.action = (Lister<BaseNodesResponse<T>, T>) action;
8185
}
8286

@@ -285,7 +289,7 @@ private boolean hasAnyNodeFetching(Map<String, NodeEntry<T>> shardCache) {
285289
// visible for testing
286290
void asyncFetch(final DiscoveryNode[] nodes, long fetchingRound) {
287291
logger.trace("{} fetching [{}] from {}", shardId, type, nodes);
288-
action.list(shardId, nodes, new ActionListener<BaseNodesResponse<T>>() {
292+
action.list(shardId, customDataPath, nodes, new ActionListener<BaseNodesResponse<T>>() {
289293
@Override
290294
public void onResponse(BaseNodesResponse<T> response) {
291295
processAsyncFetch(response.getNodes(), response.failures(), fetchingRound);

server/src/main/java/org/elasticsearch/gateway/GatewayAllocator.java

+14-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
2828
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
2929
import org.elasticsearch.client.node.NodeClient;
30+
import org.elasticsearch.cluster.metadata.IndexMetaData;
3031
import org.elasticsearch.cluster.node.DiscoveryNode;
3132
import org.elasticsearch.cluster.node.DiscoveryNodes;
3233
import org.elasticsearch.cluster.routing.RerouteService;
@@ -189,8 +190,9 @@ private boolean hasNewNodes(DiscoveryNodes nodes) {
189190

190191
class InternalAsyncFetch<T extends BaseNodeResponse> extends AsyncShardFetch<T> {
191192

192-
InternalAsyncFetch(Logger logger, String type, ShardId shardId, Lister<? extends BaseNodesResponse<T>, T> action) {
193-
super(logger, type, shardId, action);
193+
InternalAsyncFetch(Logger logger, String type, ShardId shardId, String customDataPath,
194+
Lister<? extends BaseNodesResponse<T>, T> action) {
195+
super(logger, type, shardId, customDataPath, action);
194196
}
195197

196198
@Override
@@ -217,7 +219,9 @@ protected AsyncShardFetch.FetchResult<NodeGatewayStartedShards> fetchData(ShardR
217219
Lister<BaseNodesResponse<NodeGatewayStartedShards>, NodeGatewayStartedShards> lister = this::listStartedShards;
218220
AsyncShardFetch<NodeGatewayStartedShards> fetch =
219221
asyncFetchStarted.computeIfAbsent(shard.shardId(),
220-
shardId -> new InternalAsyncFetch<>(logger, "shard_started", shardId, lister));
222+
shardId -> new InternalAsyncFetch<>(logger, "shard_started", shardId,
223+
IndexMetaData.INDEX_DATA_PATH_SETTING.get(allocation.metaData().index(shard.index()).getSettings()),
224+
lister));
221225
AsyncShardFetch.FetchResult<NodeGatewayStartedShards> shardState =
222226
fetch.fetchData(allocation.nodes(), allocation.getIgnoreNodes(shard.shardId()));
223227

@@ -227,9 +231,9 @@ protected AsyncShardFetch.FetchResult<NodeGatewayStartedShards> fetchData(ShardR
227231
return shardState;
228232
}
229233

230-
private void listStartedShards(ShardId shardId, DiscoveryNode[] nodes,
234+
private void listStartedShards(ShardId shardId, String customDataPath, DiscoveryNode[] nodes,
231235
ActionListener<BaseNodesResponse<NodeGatewayStartedShards>> listener) {
232-
var request = new TransportNodesListGatewayStartedShards.Request(shardId, nodes);
236+
var request = new TransportNodesListGatewayStartedShards.Request(shardId, customDataPath, nodes);
233237
client.executeLocally(TransportNodesListGatewayStartedShards.TYPE, request,
234238
ActionListener.wrap(listener::onResponse, listener::onFailure));
235239
}
@@ -244,12 +248,12 @@ class InternalReplicaShardAllocator extends ReplicaShardAllocator {
244248
}
245249

246250
@Override
247-
protected AsyncShardFetch.FetchResult<NodeStoreFilesMetaData>
248-
fetchData(ShardRouting shard, RoutingAllocation allocation) {
251+
protected AsyncShardFetch.FetchResult<NodeStoreFilesMetaData> fetchData(ShardRouting shard, RoutingAllocation allocation) {
249252
// explicitely type lister, some IDEs (Eclipse) are not able to correctly infer the function type
250253
Lister<BaseNodesResponse<NodeStoreFilesMetaData>, NodeStoreFilesMetaData> lister = this::listStoreFilesMetaData;
251254
AsyncShardFetch<NodeStoreFilesMetaData> fetch = asyncFetchStore.computeIfAbsent(shard.shardId(),
252-
shardId -> new InternalAsyncFetch<>(logger, "shard_store", shard.shardId(), lister));
255+
shardId -> new InternalAsyncFetch<>(logger, "shard_store", shard.shardId(),
256+
IndexMetaData.INDEX_DATA_PATH_SETTING.get(allocation.metaData().index(shard.index()).getSettings()), lister));
253257
AsyncShardFetch.FetchResult<NodeStoreFilesMetaData> shardStores =
254258
fetch.fetchData(allocation.nodes(), allocation.getIgnoreNodes(shard.shardId()));
255259
if (shardStores.hasData()) {
@@ -258,9 +262,9 @@ class InternalReplicaShardAllocator extends ReplicaShardAllocator {
258262
return shardStores;
259263
}
260264

261-
private void listStoreFilesMetaData(ShardId shardId, DiscoveryNode[] nodes,
265+
private void listStoreFilesMetaData(ShardId shardId, String customDataPath, DiscoveryNode[] nodes,
262266
ActionListener<BaseNodesResponse<NodeStoreFilesMetaData>> listener) {
263-
var request = new TransportNodesListShardStoreMetaData.Request(shardId, nodes);
267+
var request = new TransportNodesListShardStoreMetaData.Request(shardId, customDataPath, nodes);
264268
client.executeLocally(TransportNodesListShardStoreMetaData.TYPE, request,
265269
ActionListener.wrap(listener::onResponse, listener::onFailure));
266270
}

0 commit comments

Comments
 (0)