Skip to content
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

#12117 Support for Server & Controller API to check for Segments reload of a table in servers #13789

Merged
merged 45 commits into from
Aug 28, 2024

Conversation

deepthi912
Copy link
Collaborator

@deepthi912 deepthi912 commented Aug 9, 2024

Context:
#12117
This PR adds two APIs:
Server API

  1. An API on the server which returns server id and boolean result(to check if any segments need to be reloaded for a given server).
    Response for the server API will be something like :
    {
    "instanceId": "Server_10.0.0.215_7050",
    "needReload": false
    }

Controller API
2) Controller API which checks if the reload is needed on a table taking into verbose flag consideration:

  • For example if verbose is true, Response will be like :

{
"needReload": false,
"serverToSegmentsReloadList": {
"Server_10.0.0.215_7050": {
"needReload": false,
"instanceId": "Server_10.0.0.215_7050"
}
}
}

  • If verbose flag is set to false, serverToSegmentsReloadList map will be empty :

{
"needReload": false,
"serverToSegmentsReloadList": {}
}

Extension: This api can add extra details about the list of segments to be reloaded in the response in the future.

Responses from Controller API:

image

image

@deepthi912 deepthi912 changed the title #12117 Support for Server API to check for Segments reloaded for a particular table #12117 Support for Server API to check for Segments reload for a particular table in a server Aug 9, 2024
line characters checkstyle
@codecov-commenter
Copy link

codecov-commenter commented Aug 9, 2024

Codecov Report

Attention: Patch coverage is 11.65049% with 91 lines in your changes missing coverage. Please review.

Project coverage is 57.49%. Comparing base (59551e4) to head (aa3a722).
Report is 953 commits behind head on master.

Files with missing lines Patch % Lines
...t/controller/util/ServerSegmentMetadataReader.java 0.00% 24 Missing ⚠️
...ler/api/resources/PinotSegmentRestletResource.java 0.00% 22 Missing ⚠️
.../pinot/core/data/manager/BaseTableDataManager.java 0.00% 15 Missing ⚠️
...che/pinot/controller/util/TableMetadataReader.java 0.00% 13 Missing ⚠️
...che/pinot/server/api/resources/TablesResource.java 0.00% 9 Missing ⚠️
...inot/controller/helix/ControllerRequestClient.java 0.00% 5 Missing ⚠️
...spi/utils/builder/ControllerRequestURLBuilder.java 0.00% 2 Missing ⚠️
...l/indexsegment/immutable/ImmutableSegmentImpl.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #13789      +/-   ##
============================================
- Coverage     61.75%   57.49%   -4.27%     
+ Complexity      207      197      -10     
============================================
  Files          2436     2582     +146     
  Lines        133233   142544    +9311     
  Branches      20636    22128    +1492     
============================================
- Hits          82274    81949     -325     
- Misses        44911    54100    +9189     
- Partials       6048     6495     +447     
Flag Coverage Δ
custom-integration1 <0.01% <0.00%> (-0.01%) ⬇️
integration <0.01% <0.00%> (-0.01%) ⬇️
integration1 <0.01% <0.00%> (-0.01%) ⬇️
integration2 0.00% <0.00%> (ø)
java-11 57.45% <11.65%> (-4.26%) ⬇️
java-21 57.38% <11.65%> (-4.25%) ⬇️
skip-bytebuffers-false 57.46% <11.65%> (-4.28%) ⬇️
skip-bytebuffers-true 57.36% <11.65%> (+29.63%) ⬆️
temurin 57.49% <11.65%> (-4.27%) ⬇️
unittests 57.48% <11.65%> (-4.27%) ⬇️
unittests1 40.21% <40.00%> (-6.68%) ⬇️
unittests2 27.81% <0.00%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@deepthi912 deepthi912 changed the title #12117 Support for Server API to check for Segments reload for a particular table in a server #12117 Support for Server API to check for Segments reload of a table in a server Aug 9, 2024
boolean mismatchCheck = false;
for (SegmentDataManager segmentDataManager : segmentDataManagers) {
Set<String> segmentColumns = SegmentMetadataFetcher.getSegmentColumns(segmentDataManager, columns);
if (!segmentColumns.containsAll(schemaColumns)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

we would need to also check indexing changes. reload will be needed for indexing changes, dictionary changes, along with new column added

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried to use little detailed check from SegmentPreProcessor.needProcess() to compare the same. But I am doubtful of the amount of time it would take. Do you want me to just compare the indexes map corresponding to each column instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

which specific part about needPreprocess are you doubtful about?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@npawar We should be good. I was thinking there were some extra config checks happening in needProcess!

segments reload check including indexes metadata
checkstyle unused import removal
remove unused code to get the columns
Copy link
Contributor

@Jackie-Jiang Jackie-Jiang left a comment

Choose a reason for hiding this comment

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

The overall direction looks good!

public String checkMismatchedSegments(
@ApiParam(value = "Table Name with type", required = true) @PathParam("tableName") String tableName,
@ApiParam(value = "Column name", allowMultiple = true) @QueryParam("columns") @DefaultValue("")
List<String> columns, @Context HttpHeaders headers) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is columns needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not needed, removed

Comment on lines 975 to 977
Pair<TableConfig, Schema> tableConfigSchema = tableDataManager.fetchTableConfigAndSchema();
IndexLoadingConfig indexLoadingConfig =
tableDataManager.getIndexLoadingConfig(tableConfigSchema.getLeft(), tableConfigSchema.getRight());
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Pair<TableConfig, Schema> tableConfigSchema = tableDataManager.fetchTableConfigAndSchema();
IndexLoadingConfig indexLoadingConfig =
tableDataManager.getIndexLoadingConfig(tableConfigSchema.getLeft(), tableConfigSchema.getRight());
IndexLoadingConfig indexLoadingConfig = tableDataManager.fetchIndexLoadingConfig();

boolean mismatchCheck = false;
for (SegmentDataManager segmentDataManager : segmentDataManagers) {
SegmentZKMetadata segmentZKMetadata = tableDataManager.fetchZKMetadata(segmentDataManager.getSegmentName());
if (tableDataManager.checkReloadSegment(segmentZKMetadata,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest pushing down the logic completely into the TableDataManager so that it is easier to manage and extend in the future. Basically add an API needReload() to the TableDataManager

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@GET
@Path("/tables/{tableName}/segments/mismatch")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Checks if there is any mismatch of columns in a segment", notes =
Copy link
Contributor

Choose a reason for hiding this comment

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

The API path and doc is a little bit misleading. We want to check if reload is needed, instead of checking column mismatch

*
* It has details of server id and returns true/false if there are any segments to be reloaded or not.
*/
public class SegmentColumnMismatchResponse {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest renaming it to SegmentReloadCheckResponse and rename the fields accordingly

Comment on lines 31 to 32
boolean _isMismatch;
String _serverInstanceId;
Copy link
Contributor

Choose a reason for hiding this comment

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

They can be defined as private final


// If the segment doesn't exist on server or its CRC has changed, then we
// need to fall back to download the segment from deep store to load it.
if (segmentMetadata == null || !hasSameCRC(zkMetadata, segmentMetadata)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not read ZK metadata or do CRC check here because it can drastically increase the ZK load. needPreprocess() check should be enough

Schema schema = indexLoadingConfig.getSchema();
//if the reload of the segment is not needed then return false
if (!ImmutableSegmentLoader.needPreprocess(segmentDirectory, indexLoadingConfig, schema)) {
_logger.info("Segment: {} is consistent with latest table config and schema", segmentName);
Copy link
Contributor

Choose a reason for hiding this comment

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

This can flood the log (consider thousands of segments on each server)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed the log

private final String _serverInstanceId;

@JsonCreator
public SegmentsReloadCheckResponse(@JsonProperty("isReload") boolean isReload,
Copy link
Member

Choose a reason for hiding this comment

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

nit: needReload instead?

@Context HttpHeaders headers) {
tableName = DatabaseUtils.translateTableName(tableName, headers);
TableDataManager tableDataManager = ServerResourceUtils.checkGetTableDataManager(_serverInstance, tableName);
boolean isSegmentsReload = tableDataManager.needReloadSegments();
Copy link
Member

Choose a reason for hiding this comment

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

nit: rename the output to sth like needReload?


@JsonCreator
public SegmentsReloadCheckResponse(@JsonProperty("isReload") boolean isReload,
@JsonProperty("serverInstanceId") String serverInstanceId) {
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 why the serverInstanceId needs to be returned as part of the response payload here? Didn't we already know where the request was sent to?

//if re processing or reload is needed on a segment then return true
return ImmutableSegmentLoader.needPreprocess(segmentDirectory, indexLoadingConfig, schema);
} catch (Exception e) {
_logger.error("Failed to check if reload is needed for a segment {}", segmentName, e);
Copy link
Member

Choose a reason for hiding this comment

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

Can we emit a server metric to indicate that something is wrong when running the check?

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel that might be overkilling. IMO we can simply throw the exception out, and client can handle it accordingly.

public boolean needReloadSegments() {
IndexLoadingConfig indexLoadingConfig = fetchIndexLoadingConfig();
List<SegmentDataManager> segmentDataManagers = acquireAllSegments();
boolean segmentsReloadCheck = false;
Copy link
Member

Choose a reason for hiding this comment

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

nit: rename it to sth like needSegmentReload

* Checks for a particular segment, reload is needed or not
* @return true if the reload is needed on the segment
*/
boolean checkReloadSegment(SegmentZKMetadata zkMetadata, IndexLoadingConfig indexLoadingConfig);
Copy link
Member

Choose a reason for hiding this comment

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

nit: unify the method name to sth like needReloadSegment(...)?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this API anymore

} catch (Exception e) {
_logger.error("Failed to check if reload is needed for a segment {}", segmentName, e);
closeSegmentDirectoryQuietly(segmentDirectory);
return false;
Copy link
Member

Choose a reason for hiding this comment

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

I saw you stated in the API description that 500 will be returned if there is any server side error. Shouldn't we return 500 here in this case?

return _serverInstanceId;
}

public boolean getReload() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method name might not work for JSON serialization. I think it will be mistakenly serialized as reload. Can you add a test to verify? We might need to rename it to isNeedReload(), or explicitly annotate it with @JsonProperty("needReload"). If we explicitly annotate, a better name could be isReloadNeeded()

*/
public class SegmentsReloadCheckResponse {
private final boolean _needReload;
private final String _serverInstanceId;
Copy link
Contributor

Choose a reason for hiding this comment

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

(minor) instanceId might be more concise because this is always returned from server.

Do we really need to return it? Controller should already have this info when reading the address of the server

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We have it, it's just little complex to separate out server instance when doing multi get request which is why just returning it as well in the response to make it simpler. I will look into this later. For now, we can include instanceId as well I think

throws Exception {
String segmentName = zkMetadata.getSegmentName();
SegmentDirectory segmentDirectory =
tryInitSegmentDirectory(segmentName, String.valueOf(zkMetadata.getCrc()), indexLoadingConfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

This can cause exception for consuming segments. We should only check immutable segments. We can read the SegmentDirectory out from ImmutableSegmentImpl. We can consider adding a test in BaseClusterIntegrationTestSet.testReload() to verify if the API works. You may also add the test after the controller side integration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Checked for ImmutableSegmentImpl instance before passing it through needReload where SegmentDirectory is generated. I think this should avoid any exception cases of consuming segments. Adding more tests along with controller side code and to combine with the same PR.

boolean needReload = false;
try {
for (SegmentDataManager segmentDataManager : segmentDataManagers) {
SegmentZKMetadata segmentZKMetadata = fetchZKMetadata(segmentDataManager.getSegmentName());
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not read ZK metadata because it needs to access ZK, and we might end up reading it for thousands of segments.

@@ -1013,6 +1013,22 @@ public boolean tryLoadExistingSegment(SegmentZKMetadata zkMetadata, IndexLoading
}
}

boolean needReloadSegment(SegmentZKMetadata zkMetadata, IndexLoadingConfig indexLoadingConfig)
Copy link
Contributor

Choose a reason for hiding this comment

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

Make it private

Schema schema = indexLoadingConfig.getSchema();
//if re processing or reload is needed on a segment then return true
return ImmutableSegmentLoader.needPreprocess(segmentDirectory, indexLoadingConfig, schema);
} catch (Exception e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can probably directly throw the exception out without catch

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

closeSegmentDirectoryQuietly(segmentDirectory); There is this closure being handled in exception cases( so wrote a catch block). Do you want me to handle this in finally?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we can use the SegmentDirectory within ImmutableSegmentImpl, we don't need to close it here. Ideally we don't want to create new SegmentDirectory in this check

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

Add Controller Side API and test in integration
@deepthi912 deepthi912 changed the title #12117 Support for Server API to check for Segments reload of a table in a server #12117 Support for Server & Controller API to check for Segments reload of a table in servers Aug 16, 2024
Copy link
Contributor

@Jackie-Jiang Jackie-Jiang left a comment

Choose a reason for hiding this comment

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

Mostly good

public Map<String, JsonNode> getServerCheckSegmentsReloadMetadata(String tableName, TableType tableType,
int timeoutMs)
throws InvalidConfigException, IOException {
String tableNameWithType =
Copy link
Contributor

Choose a reason for hiding this comment

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

This check should be performed in the RestletResource class before calling this method

@@ -721,6 +721,11 @@ public String reloadOfflineTable(String tableName, boolean forceDownload)
return getControllerRequestClient().reloadTable(tableName, TableType.OFFLINE, forceDownload);
}

public String needReloadOfflineTable(String tableNameWithType)
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename it to checkIfReloadIsNeeded. Does it only apply to offline table?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It applies to both realtime and offline tables, changing the name

@@ -239,6 +239,11 @@ public String forTableReload(String tableName, TableType tableType, boolean forc
return StringUtil.join("/", _baseUrl, "segments", tableName, query);
}

public String forTableNeedReload(String tableNameWithType) {
String query = String.format("needReload?verbose=%s", "false");
Copy link
Contributor

Choose a reason for hiding this comment

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

(minor) No need to format

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Tested for false and true verbose flags. Changed this method

Copy link
Contributor

@Jackie-Jiang Jackie-Jiang left a comment

Choose a reason for hiding this comment

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

LGTM

@Jackie-Jiang Jackie-Jiang merged commit ae97265 into apache:master Aug 28, 2024
21 checks passed
rajagopr pushed a commit to rajagopr/pinot that referenced this pull request Sep 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants