Skip to content

Introduce TransportGetFromTranslogAction #95998

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

Conversation

pxsalehi
Copy link
Member

@pxsalehi pxsalehi commented May 10, 2023

This is a change broken off from the ongoing real-time GET PR (#93976).
It is just an action that can be used to invoke the new ShardGetService.getFromTranslog
in #95736. It will be used on the search shards as a first step to handle a real-time get.

Relates #93976, ES-5537

@pxsalehi pxsalehi added >non-issue :Distributed Indexing/CRUD A catch all label for issues around indexing, updating and getting a doc by id. Not search. labels May 10, 2023
@elasticsearchmachine elasticsearchmachine added v8.9.0 Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. labels May 10, 2023
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-distributed (Team:Distributed)

@pxsalehi
Copy link
Member Author

@pxsalehi pxsalehi requested review from tlrx, Tim-Brooks and kingherc May 10, 2023 13:58
Comment on lines 62 to 66
Engine engine = indexShard.getEngineOrNull();
if (engine == null) {
listener.onFailure(new AlreadyClosedException("engine closed"));
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is really needed because ShardGetService.getFromTranslog() uses getEngine() at some point and that will throw in case of a closed engine. I think we can do this only if the result of the getFromTranslog is null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's not needed there. Slightly outdated code! I've moved it to when result is null.

private final GetRequest getRequest;
private final ShardId shardId;

public Request(GetRequest getRequest, ShardId shardId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetRequest is a SingleShardRequest and can contain a ShardId, should we check that they are the same?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed if it's the same shardId, then we can re-use the GetRequest as the request type of the TransportGetFromTranslogAction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that in an initial version. See this comment. The code in SingleShardRequest is a bit old school. It keeps an internalShardId which is mutable and is only used internally. Essentially to reuse it, I'd have to make some internal mutable state of SingleShardRequest public which is not really worth it I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing to the comment. I do agree SingleShardRequest did not aged well.

If I understand the code correctly, the internal shard id of the GetRequest is resolved when the request is executed through the TransportGetAction/TransportSingleShardAction, and TransportGetAction will be modified for realtime gets on stateless to use the new TransportGetFromTranslogAction and the resolved shard id will be passed over the the TransportGetFromTranslogAction.Request.

So we should be fine.

segmentGeneration = in.readZLong();
if (in.readBoolean()) {
getResult = new GetResult(in);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert that segmentGeneration > -1L when getResult is null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null and -1 is a valid state. I have mention that in the comment above segmentGeneration(), when we don't find anything and there hasn't been any unsafe->safe switches.

assertThat(response.getResult().isExists(), equalTo(true));
assertThat(response.segmentGeneration(), equalTo(-1L));
// After a refresh we should not be able to get from translog
client().admin().indices().refresh(new RefreshRequest("test")).get();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: refresh("test")

Copy link
Contributor

@kingherc kingherc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Just some minor stuff I believe

private final GetRequest getRequest;
private final ShardId shardId;

public Request(GetRequest getRequest, ShardId shardId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed if it's the same shardId, then we can re-use the GetRequest as the request type of the TransportGetFromTranslogAction.

public void writeTo(StreamOutput out) throws IOException {
out.writeZLong(segmentGeneration);
if (getResult == null) {
out.writeBoolean(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to write a boolean? If segmentGeneration != -1, then the result is null and you simply do not need to write anything else.

client().prepareIndex("test").setId("1").setSource("field1", "value1").setRefreshPolicy(RefreshPolicy.NONE).get();
response = getFromTranslog(indexOrAlias(), "1");
assertNotNull(response.getResult());
assertThat(response.getResult().isExists(), equalTo(true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also assert the real getResult's value? That e.g. it's equal to a document with field1&value1? Similar comment for the similar points below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why that would matter for this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth verifying that we are getting the right version of the document from the translog

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member Author

@pxsalehi pxsalehi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reviews. It's ready now again.

Comment on lines 62 to 66
Engine engine = indexShard.getEngineOrNull();
if (engine == null) {
listener.onFailure(new AlreadyClosedException("engine closed"));
return;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that's not needed there. Slightly outdated code! I've moved it to when result is null.

private final GetRequest getRequest;
private final ShardId shardId;

public Request(GetRequest getRequest, ShardId shardId) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that in an initial version. See this comment. The code in SingleShardRequest is a bit old school. It keeps an internalShardId which is mutable and is only used internally. Essentially to reuse it, I'd have to make some internal mutable state of SingleShardRequest public which is not really worth it I think.

segmentGeneration = in.readZLong();
if (in.readBoolean()) {
getResult = new GetResult(in);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null and -1 is a valid state. I have mention that in the comment above segmentGeneration(), when we don't find anything and there hasn't been any unsafe->safe switches.

client().prepareIndex("test").setId("1").setSource("field1", "value1").setRefreshPolicy(RefreshPolicy.NONE).get();
response = getFromTranslog(indexOrAlias(), "1");
assertNotNull(response.getResult());
assertThat(response.getResult().isExists(), equalTo(true));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why that would matter for this case.

@pxsalehi pxsalehi requested review from kingherc and tlrx May 11, 2023 16:07
Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - I only left minor comments, feel free to address them

client().prepareIndex("test").setId("1").setSource("field1", "value1").setRefreshPolicy(RefreshPolicy.NONE).get();
response = getFromTranslog(indexOrAlias(), "1");
assertNotNull(response.getResult());
assertThat(response.getResult().isExists(), equalTo(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth verifying that we are getting the right version of the document from the translog

assertNotNull(response.getResult());
assertThat(response.getResult().isExists(), equalTo(true));
assertThat(response.segmentGeneration(), equalTo(-1L));
// Get followed by a delete should still return a result
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a side note and for future tests, I think that comment like this are very helpful to understand test failures and can be backed into the assertion methods themselves, ie this could be:

assertThat("Get followed by a delete should still return a result", response.getResult(), nonNullValue());

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea!

private final GetRequest getRequest;
private final ShardId shardId;

public Request(GetRequest getRequest, ShardId shardId) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing to the comment. I do agree SingleShardRequest did not aged well.

If I understand the code correctly, the internal shard id of the GetRequest is resolved when the request is executed through the TransportGetAction/TransportSingleShardAction, and TransportGetAction will be modified for realtime gets on stateless to use the new TransportGetFromTranslogAction and the resolved shard id will be passed over the the TransportGetFromTranslogAction.Request.

So we should be fine.


@Override
public String toString() {
return "GetFromTranslogRequest{" + "getRequest=" + getRequest + ", shardId=" + shardId + "}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just

Suggested change
return "GetFromTranslogRequest{" + "getRequest=" + getRequest + ", shardId=" + shardId + "}";
return "translog " + getRequest;

as GetRequest.toString already provides useful information?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't seem to include the shardID though.

Copy link
Contributor

@kingherc kingherc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM2. Feel free to consider the remaining unresolved comments.

@pxsalehi pxsalehi added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label May 16, 2023
@pxsalehi
Copy link
Member Author

@elasticmachine run elasticsearch-ci/part-2

@pxsalehi
Copy link
Member Author

Failure was #96162

@pxsalehi
Copy link
Member Author

@elasticmachine update branch

@pxsalehi
Copy link
Member Author

@elasticmachine run elasticsearch-ci/part-3

Failure is #95983

@elasticsearchmachine elasticsearchmachine merged commit fa264fb into elastic:main May 16, 2023
@pxsalehi pxsalehi deleted the ps230509-transportGetFromTranslogAction branch May 16, 2023 17:07
pxsalehi added a commit that referenced this pull request Jun 9, 2023
The multi-get counterpart of #95998.

Relates ES-5677
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) :Distributed Indexing/CRUD A catch all label for issues around indexing, updating and getting a doc by id. Not search. >non-issue Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. v8.9.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants