-
Notifications
You must be signed in to change notification settings - Fork 26.1k
Avoid loading shard metadata while closing #29140
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
Changes from 26 commits
4d3550c
fb684fb
487e785
fb5f8c9
f8a4c0d
358a94c
fed85bf
0ce8021
b654d9d
e9ef547
7641ac2
62ae05c
ed37174
be76bb0
d534ffd
f89d9b1
e9e8ad5
be42cca
819d274
fd9dc31
032be06
3eff6c9
7f835cc
48f6d46
7e58bc6
91c101a
903ef15
1d4e044
61b4e4e
8f1a5e2
ac8902b
78c0526
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,7 +41,9 @@ | |
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.common.xcontent.NamedXContentRegistry; | ||
| import org.elasticsearch.env.NodeEnvironment; | ||
| import org.elasticsearch.env.ShardLock; | ||
| import org.elasticsearch.index.IndexSettings; | ||
| import org.elasticsearch.index.shard.IndexShard; | ||
| import org.elasticsearch.index.shard.ShardId; | ||
| import org.elasticsearch.index.shard.ShardPath; | ||
| import org.elasticsearch.index.shard.ShardStateMetaData; | ||
|
|
@@ -52,6 +54,7 @@ | |
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| /** | ||
| * This transport action is used to fetch the shard version from each node during primary allocation in {@link GatewayAllocator}. | ||
|
|
@@ -116,55 +119,71 @@ protected NodeGatewayStartedShards nodeOperation(NodeRequest request) { | |
| try { | ||
| final ShardId shardId = request.getShardId(); | ||
| logger.trace("{} loading local shard state info", shardId); | ||
| ShardStateMetaData shardStateMetaData = ShardStateMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, | ||
| nodeEnv.availableShardPaths(request.shardId)); | ||
| if (shardStateMetaData != null) { | ||
| IndexMetaData metaData = clusterService.state().metaData().index(shardId.getIndex()); | ||
| if (metaData == null) { | ||
| // we may send this requests while processing the cluster state that recovered the index | ||
| // sometimes the request comes in before the local node processed that cluster state | ||
| // in such cases we can load it from disk | ||
| metaData = IndexMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, | ||
| nodeEnv.indexPaths(shardId.getIndex())); | ||
| } | ||
| if (metaData == null) { | ||
| ElasticsearchException e = new ElasticsearchException("failed to find local IndexMetaData"); | ||
| e.setShard(request.shardId); | ||
| throw e; | ||
| } | ||
|
|
||
| if (indicesService.getShardOrNull(shardId) == null) { | ||
| // we don't have an open shard on the store, validate the files on disk are openable | ||
| ShardPath shardPath = null; | ||
| try { | ||
| IndexSettings indexSettings = new IndexSettings(metaData, settings); | ||
| shardPath = ShardPath.loadShardPath(logger, nodeEnv, shardId, indexSettings); | ||
| if (shardPath == null) { | ||
| throw new IllegalStateException(shardId + " no shard path found"); | ||
| } | ||
| Store.tryOpenIndex(shardPath.resolveIndex(), shardId, nodeEnv::shardLock, logger); | ||
| } catch (Exception exception) { | ||
| final ShardPath finalShardPath = shardPath; | ||
| logger.trace(() -> new ParameterizedMessage( | ||
| "{} can't open index for shard [{}] in path [{}]", | ||
| shardId, | ||
| shardStateMetaData, | ||
| (finalShardPath != null) ? finalShardPath.resolveIndex() : ""), | ||
| exception); | ||
| String allocationId = shardStateMetaData.allocationId != null ? | ||
| shardStateMetaData.allocationId.getId() : null; | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), allocationId, shardStateMetaData.primary, | ||
| exception); | ||
| } | ||
| } | ||
| final IndexShard indexShard = indicesService.getShardOrNull(shardId); | ||
| if (indexShard != null) { | ||
| final ShardStateMetaData shardStateMetaData = indexShard.getShardStateMetaData(); | ||
| final String allocationId = shardStateMetaData.allocationId != null ? | ||
| shardStateMetaData.allocationId.getId() : null; | ||
| logger.trace("{} shard state info found: [{}]", shardId, shardStateMetaData); | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), allocationId, shardStateMetaData.primary); | ||
| } | ||
|
|
||
| final ShardStateMetaData shardStateMetaData; | ||
| try (ShardLock ignored = nodeEnv.shardLock(shardId, TimeUnit.SECONDS.toMillis(5))) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I just spotted this - there are still two calls to |
||
| shardStateMetaData = ShardStateMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, | ||
| nodeEnv.availableShardPaths(shardId)); | ||
| } | ||
|
|
||
| if (shardStateMetaData == null) { | ||
| logger.trace("{} no local shard info found", shardId); | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), null, false); | ||
| } | ||
|
|
||
| logger.debug("{} shard state info found: [{}]", shardId, shardStateMetaData); | ||
| IndexMetaData metaData = clusterService.state().metaData().index(shardId.getIndex()); | ||
| if (metaData == null) { | ||
| // we may send this requests while processing the cluster state that recovered the index | ||
| // sometimes the request comes in before the local node processed that cluster state | ||
| // in such cases we can load it from disk | ||
| metaData = IndexMetaData.FORMAT.loadLatestState(logger, NamedXContentRegistry.EMPTY, | ||
| nodeEnv.indexPaths(shardId.getIndex())); | ||
| } | ||
| if (metaData == null) { | ||
| ElasticsearchException e = new ElasticsearchException("failed to find local IndexMetaData"); | ||
| e.setShard(request.shardId); | ||
| throw e; | ||
| } | ||
|
|
||
| // we don't have an open shard on the store, validate the files on disk are openable | ||
| ShardPath shardPath = null; | ||
| try { | ||
| IndexSettings indexSettings = new IndexSettings(metaData, settings); | ||
| try (ShardLock ignored = nodeEnv.shardLock(shardId, TimeUnit.SECONDS.toMillis(5))) { | ||
| shardPath = ShardPath.loadShardPath(logger, nodeEnv, shardId, indexSettings); | ||
| } | ||
| if (shardPath == null) { | ||
| throw new IllegalStateException(shardId + " no shard path found"); | ||
| } | ||
| Store.tryOpenIndex(shardPath.resolveIndex(), shardId, nodeEnv::shardLock, logger); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of acquiring the shard lock for a second time, I would prefer if we would do it once, and move this call under that lock and just rename Same thing for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } catch (Exception exception) { | ||
| final ShardPath finalShardPath = shardPath; | ||
| logger.trace(() -> new ParameterizedMessage( | ||
| "{} can't open index for shard [{}] in path [{}]", | ||
| shardId, | ||
| shardStateMetaData, | ||
| (finalShardPath != null) ? finalShardPath.resolveIndex() : ""), | ||
| exception); | ||
| String allocationId = shardStateMetaData.allocationId != null ? | ||
| shardStateMetaData.allocationId.getId() : null; | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), allocationId, shardStateMetaData.primary); | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), allocationId, shardStateMetaData.primary, | ||
| exception); | ||
| } | ||
| logger.trace("{} no local shard info found", shardId); | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), null, false); | ||
|
|
||
| logger.debug("{} shard state info found: [{}]", shardId, shardStateMetaData); | ||
| String allocationId = shardStateMetaData.allocationId != null ? | ||
| shardStateMetaData.allocationId.getId() : null; | ||
| return new NodeGatewayStartedShards(clusterService.localNode(), allocationId, shardStateMetaData.primary); | ||
|
|
||
| } catch (Exception e) { | ||
| throw new ElasticsearchException("failed to load started shards", e); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2065,6 +2065,12 @@ public void startRecovery(RecoveryState recoveryState, PeerRecoveryTargetService | |
| } | ||
| } | ||
|
|
||
| public ShardStateMetaData getShardStateMetaData() { | ||
| synchronized (mutex) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can avoid the mutex here. just do a one-time volatile read of shardrouting (which is an immutable object).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I pushed 1d4e044 |
||
| return new ShardStateMetaData(shardRouting.primary(), indexSettings.getUUID(), shardRouting.allocationId()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the shard is in primary mode, i.e., in charge of replicating changes (see {@link ReplicationTracker}). | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,7 @@ | |
| import org.elasticsearch.common.unit.TimeValue; | ||
| import org.elasticsearch.common.xcontent.NamedXContentRegistry; | ||
| import org.elasticsearch.env.NodeEnvironment; | ||
| import org.elasticsearch.env.ShardLock; | ||
| import org.elasticsearch.gateway.AsyncShardFetch; | ||
| import org.elasticsearch.index.IndexService; | ||
| import org.elasticsearch.index.IndexSettings; | ||
|
|
@@ -139,7 +140,10 @@ private StoreFilesMetaData listStoreMetaData(ShardId shardId) throws IOException | |
| return new StoreFilesMetaData(shardId, Store.MetadataSnapshot.EMPTY); | ||
| } | ||
| final IndexSettings indexSettings = indexService != null ? indexService.getIndexSettings() : new IndexSettings(metaData, settings); | ||
| final ShardPath shardPath = ShardPath.loadShardPath(logger, nodeEnv, shardId, indexSettings); | ||
| final ShardPath shardPath; | ||
| try (ShardLock ignored = nodeEnv.shardLock(shardId, TimeUnit.SECONDS.toMillis(5))) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you double check what the effect is of failing to get the lock?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could potentially mean infinite shard fetching / reroute retry loop if the shard lock is unavailable for an extended time.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked at how we could be in a situation in which the shard lock is unavailable for a long time. This'd be the case if the shard was open, but that means there's an All the other usages of the shard lock seem short-lived. They protect some IO (e.g. deleting the shards, etc) so may take some time, but not infinitely long. Also, we obtain the same shard lock a few lines down, in Could you clarify, @ywelsch?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In here so as not to mess with existing behavior. |
||
| shardPath = ShardPath.loadShardPath(logger, nodeEnv, shardId, indexSettings); | ||
| } | ||
| if (shardPath == null) { | ||
| return new StoreFilesMetaData(shardId, Store.MetadataSnapshot.EMPTY); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allocationIds have been around since I don't know how long. When can this be null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its declaration says this:
elasticsearch/server/src/main/java/org/elasticsearch/index/shard/ShardStateMetaData.java
Lines 44 to 45 in 6538542
There are lots of other null checks too. Maybe worth addressing separately?