-
Notifications
You must be signed in to change notification settings - Fork 25.8k
Stateless real-time GET #93976
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
Stateless real-time GET #93976
Changes from 1 commit
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 |
|---|---|---|
|
|
@@ -9,21 +9,32 @@ | |
| package org.elasticsearch.action.get; | ||
|
|
||
| import org.elasticsearch.action.ActionListener; | ||
| import org.elasticsearch.action.ActionListenerResponseHandler; | ||
| import org.elasticsearch.action.ActionRunnable; | ||
| import org.elasticsearch.action.NoShardAvailableActionException; | ||
| import org.elasticsearch.action.admin.indices.refresh.TransportShardRefreshAction; | ||
| import org.elasticsearch.action.support.ActionFilters; | ||
| import org.elasticsearch.action.support.replication.BasicReplicationRequest; | ||
| import org.elasticsearch.action.support.single.shard.TransportSingleShardAction; | ||
| import org.elasticsearch.client.internal.node.NodeClient; | ||
| import org.elasticsearch.cluster.ClusterState; | ||
| import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; | ||
| import org.elasticsearch.cluster.node.DiscoveryNode; | ||
| import org.elasticsearch.cluster.routing.PlainShardIterator; | ||
| import org.elasticsearch.cluster.routing.ShardIterator; | ||
| import org.elasticsearch.cluster.routing.ShardRouting; | ||
| import org.elasticsearch.cluster.service.ClusterService; | ||
| import org.elasticsearch.common.inject.Inject; | ||
| import org.elasticsearch.common.io.stream.Writeable; | ||
| import org.elasticsearch.index.IndexService; | ||
| import org.elasticsearch.index.get.GetResult; | ||
| import org.elasticsearch.index.shard.IndexShard; | ||
| import org.elasticsearch.index.shard.ShardId; | ||
| import org.elasticsearch.index.shard.ShardNotFoundException; | ||
| import org.elasticsearch.indices.ExecutorSelector; | ||
| import org.elasticsearch.indices.IndicesService; | ||
| import org.elasticsearch.logging.LogManager; | ||
| import org.elasticsearch.logging.Logger; | ||
| import org.elasticsearch.threadpool.ThreadPool; | ||
| import org.elasticsearch.transport.TransportService; | ||
|
|
||
|
|
@@ -34,8 +45,11 @@ | |
| */ | ||
| public class TransportGetAction extends TransportSingleShardAction<GetRequest, GetResponse> { | ||
|
|
||
| private static final Logger logger = LogManager.getLogger(TransportGetAction.class); | ||
|
|
||
| private final IndicesService indicesService; | ||
| private final ExecutorSelector executorSelector; | ||
| private final NodeClient client; | ||
|
|
||
| @Inject | ||
| public TransportGetAction( | ||
|
|
@@ -45,7 +59,8 @@ public TransportGetAction( | |
| ThreadPool threadPool, | ||
| ActionFilters actionFilters, | ||
| IndexNameExpressionResolver indexNameExpressionResolver, | ||
| ExecutorSelector executorSelector | ||
| ExecutorSelector executorSelector, | ||
| NodeClient client | ||
| ) { | ||
| super( | ||
| GetAction.NAME, | ||
|
|
@@ -59,6 +74,7 @@ public TransportGetAction( | |
| ); | ||
| this.indicesService = indicesService; | ||
| this.executorSelector = executorSelector; | ||
| this.client = client; | ||
| // register the internal TransportGetFromTranslogAction | ||
| new TransportGetFromTranslogAction(transportService, indicesService, actionFilters); | ||
| } | ||
|
|
@@ -78,7 +94,10 @@ protected ShardIterator shards(ClusterState state, InternalRequest request) { | |
| request.request().routing(), | ||
| request.request().preference() | ||
| ); | ||
| return clusterService.operationRouting().useOnlyPromotableShardsForStateless(iterator); | ||
| if (iterator == null) { | ||
| return null; | ||
| } | ||
| return new PlainShardIterator(iterator.shardId(), iterator.getShardRoutings().stream().filter(ShardRouting::isSearchable).toList()); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -91,6 +110,16 @@ protected void resolveRequest(ClusterState state, InternalRequest request) { | |
| protected void asyncShardOperation(GetRequest request, ShardId shardId, ActionListener<GetResponse> listener) throws IOException { | ||
| IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); | ||
| IndexShard indexShard = indexService.getShard(shardId.id()); | ||
| if (indexShard.routingEntry() == null) { | ||
|
||
| listener.onFailure(new ShardNotFoundException(shardId)); | ||
| return; | ||
| } | ||
| if (indexShard.routingEntry().isPromotableToPrimary() == false) { | ||
| handleGetOnUnpromotableShard(request, indexShard, listener); | ||
| return; | ||
| } | ||
| assert DiscoveryNode.isStateless(clusterService.getSettings()) == false | ||
| : "A TransportGetAction should always be handled by a search shard in Stateless"; | ||
| if (request.realtime()) { // we are not tied to a refresh cycle here anyway | ||
| asyncGet(request, shardId, listener); | ||
| } else { | ||
|
|
@@ -148,6 +177,67 @@ private void asyncGet(GetRequest request, ShardId shardId, ActionListener<GetRes | |
| } | ||
| } | ||
|
|
||
| private void handleGetOnUnpromotableShard(GetRequest request, IndexShard indexShard, ActionListener<GetResponse> listener) | ||
| throws IOException { | ||
| ShardId shardId = indexShard.shardId(); | ||
| DiscoveryNode node = getCurrentNodeOfPrimary(shardId); | ||
| if (request.refresh()) { | ||
| logger.trace("send refresh action for shard {} to node {}", shardId, node.getId()); | ||
| var refreshRequest = new BasicReplicationRequest(shardId); | ||
| refreshRequest.setParentTask(request.getParentTask()); | ||
| client.executeLocally( | ||
| TransportShardRefreshAction.TYPE, | ||
| refreshRequest, | ||
| ActionListener.wrap(replicationResponse -> super.asyncShardOperation(request, shardId, listener), listener::onFailure) | ||
| ); | ||
| } else if (request.realtime()) { | ||
| TransportGetFromTranslogAction.Request getFromTranslogRequest = new TransportGetFromTranslogAction.Request(request, shardId); | ||
| getFromTranslogRequest.setParentTask(request.getParentTask()); | ||
| transportService.sendRequest( | ||
| node, | ||
| TransportGetFromTranslogAction.NAME, | ||
| getFromTranslogRequest, | ||
| new ActionListenerResponseHandler<>(listener.delegateFailure((l, r) -> { | ||
| if (r.getResult() != null) { | ||
| logger.debug("received result for real-time get for id '{}' from promotable shard", request.id()); | ||
| l.onResponse(new GetResponse(r.getResult())); | ||
| } else { | ||
| logger.debug( | ||
| "no result for real-time get for id '{}' from promotable shard (segment generation to wait for: {})", | ||
| request.id(), | ||
| r.segmentGeneration() | ||
| ); | ||
| if (r.segmentGeneration() == -1) { | ||
| // Nothing to wait for (no previous unsafe generation), just handle the Get locally. | ||
| ActionRunnable.supply(listener, () -> shardOperation(request, shardId)).run(); | ||
| } else { | ||
| assert r.segmentGeneration() > -1L; | ||
| indexShard.waitForSegmentGeneration( | ||
| r.segmentGeneration(), | ||
| ActionListener.wrap(aLong -> super.asyncShardOperation(request, shardId, listener), listener::onFailure) | ||
| ); | ||
| } | ||
| } | ||
| }), TransportGetFromTranslogAction.Response::new, getExecutor(request, shardId)) | ||
| ); | ||
| } else { | ||
| // A non-real-time get with no explicit refresh requested. | ||
| super.asyncShardOperation(request, shardId, listener); | ||
| } | ||
| } | ||
|
|
||
| private DiscoveryNode getCurrentNodeOfPrimary(ShardId shardId) { | ||
| var shardRoutingTable = clusterService.state().routingTable().shardRoutingTable(shardId); | ||
| if (shardRoutingTable.primaryShard() == null || shardRoutingTable.primaryShard().active() == false) { | ||
| throw new NoShardAvailableActionException(shardId, "primary shard is not active"); | ||
| } | ||
| DiscoveryNode node = clusterService.state().nodes().get(shardRoutingTable.primaryShard().currentNodeId()); | ||
|
||
| if (node == null) { | ||
| throw new NoShardAvailableActionException(shardId); | ||
| } | ||
| return node; | ||
| } | ||
|
|
||
| private IndexShard getIndexShard(ShardId shardId) { | ||
| IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); | ||
| return indexService.getShard(shardId.id()); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.