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

Pagination #17638

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Pagination #17638

wants to merge 10 commits into from

Conversation

Lindsay-00
Copy link

Description

[Describe what this change achieves]

Related Issues

Resolves #[Issue number to be closed when this PR is merged]

Check List

  • Functionality includes testing.
  • API changes companion pull request created, if applicable.
  • Public documentation issue/PR created, if applicable.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Copy link
Contributor

❌ Gradle check result for ad612da: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

Copy link
Contributor

❌ Gradle check result for baa30b0: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

private final int pageSize;
private final String nextToken;
private final String sortBy;
private final String sortOrder;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should make sortBy and sortOrder an Enum since this is going to be a limited set of values and move the switch case logic inside the enum.

enum SortBy {
    case QUERY_GROUP, NODE_ID;
    Comparator<WlmStats> comparator;
....
    public Comparator<WlmStats> getComparator() {
            return comparator;
    }
}

enum SortOrder {
      case ASC, DESC;
      Function<Comparator<WlmStats>, Comparator<WlmStats>> transformer;
....
      public Comparator<WlmStats> transform(Comparator<WlmStats> comp) {
              return transformer.apply(comp);
      }
}

return Collections.emptyList();
}

List<WlmStats> allQueryGroups = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

This var name is misleading as this holds the stats not the queryGroups.

}
}

allQueryGroups = sortStats(allQueryGroups, sortBy, sortOrder);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed given that we already have the sorted list of WLM stats.


int startIndex = 0;
if (requestedToken != null) {
OptionalInt foundIndex = findIndex(allQueryGroups, requestedToken.lastQueryGroupId);
Copy link
Contributor

Choose a reason for hiding this comment

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

Filtering based on repetitive filed can result into circular loops of results. for example the same set of query group id will be present on all WlmStats entries and if your pageSize and querygroup count are equal then it will always return first two pages worth of results.

@@ -245,6 +245,11 @@ public PageToken getPageToken() {
return pageToken;
}

public void setPageToken(PageToken pageToken) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is not needed, We can use the constructor to inject the token.

QueryGroupStats.ResourceStats cpuStats = statsHolder.getResourceStats().get(ResourceType.CPU);
QueryGroupStats.ResourceStats memoryStats = statsHolder.getResourceStats().get(ResourceType.MEMORY);

table.addCell(cpuStats != null ? cpuStats.getCurrentUsage() : 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think 0 would be a misnomer here instead put a placeholder like _ or NA.

if (paginationStrategy.getNextToken() == null) {
table.startRow();
table.addCell("No more pages available");
for (int i = 1; i < 13; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should make the literals as named constants

Comment on lines 159 to 164
table.startRow();
table.addCell("No more pages available");
for (int i = 1; i < 13; i++) {
table.addCell("-");
}
table.endRow();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be conditional ? Additionally lets refactor a method for this footer .

Comment on lines 133 to 154
String queryGroupId = entry.getKey();
QueryGroupStats.QueryGroupStatsHolder statsHolder = entry.getValue();

table.startRow();
table.addCell(wlmStats.getNode().getId());
table.addCell("|");
table.addCell(queryGroupId);
table.addCell("|");
table.addCell(statsHolder.getCompletions());
table.addCell("|");
table.addCell(statsHolder.getRejections());
table.addCell("|");
table.addCell(statsHolder.getCancellations());
table.addCell("|");

QueryGroupStats.ResourceStats cpuStats = statsHolder.getResourceStats().get(ResourceType.CPU);
QueryGroupStats.ResourceStats memoryStats = statsHolder.getResourceStats().get(ResourceType.MEMORY);

table.addCell(cpuStats != null ? cpuStats.getCurrentUsage() : 0);
table.addCell("|");
table.addCell(memoryStats != null ? memoryStats.getCurrentUsage() : 0);
table.endRow();
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 refactor these lines into a method named addRow(table);

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this file is not needed as we can directly instantiate the DiscoverNode instance with mock transport.

Copy link
Contributor

❌ Gradle check result for 0dd053d: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@@ -245,6 +245,7 @@ public PageToken getPageToken() {
return pageToken;
}


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 remove this blank line

Objects.requireNonNull(token, "Token cannot be null");
String decrypted = PaginationStrategy.decryptStringToken(token);
final String[] parts = decrypted.split(SPLIT_REGEX);
if (parts.length != 4 || parts[0].isEmpty() || parts[1].isEmpty() || parts[3].isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I saw you defined

        private static final int NODE_ID_POS = 0;
        private static final int QUERY_GROUP_ID_POS = 1;
        private static final int QUERY_GROUP_COUNT_POS = 2;
        private static final int HASH_POS = 3;

We should replace the numbers here with these static fields.

}
}

private List<WlmStats> applyPagination(List<WlmStats> rawStats, WlmStrategyToken requestedToken, String currentHash) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is too long. Let's break it into several shorter functions. Also add a javadoc to explain how the pagination works for better readability

return paginatedStats;
}

public PageToken getNextToken() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplication. The function getResponseToken() returns responseToken as well.

builder.field("details", e.getMessage());
builder.endObject();

channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, builder));
Copy link
Contributor

Choose a reason for hiding this comment

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

Should add logging information (using logger.error) before throwing an exception to provide better debugging information for clients.

Comment on lines +104 to +126
try {
WlmPaginationStrategy paginationStrategy = new WlmPaginationStrategy(pageSize, nextToken, sortBy, sortOrder, response);
List<WlmStats> paginatedStats = paginationStrategy.getPaginatedStats();
PageToken nextPageToken = paginationStrategy.getNextToken();

// Use constructor to create Table with token
Table paginatedTable = createTableWithHeaders(nextPageToken);
buildTable(paginatedTable, paginatedStats, paginationStrategy);

request.params().put("v", "true");
return RestTable.buildResponse(paginatedTable, channel);
} catch (OpenSearchParseException e) {
String userMessage = "Pagination state has changed (e.g., new query groups added or removed). "
+ "Please restart pagination from the beginning by omitting the 'next_token' parameter.";

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
builder.field("error", userMessage);
builder.field("details", e.getMessage());
builder.endObject();

channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, builder));
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Extract these lines in a new method to improve the readability of functionprepareRequest

Boolean breach = request.hasParam("breach") ? Boolean.parseBoolean(request.param("breach")) : null;
WlmStatsRequest wlmStatsRequest = new WlmStatsRequest(nodeIds, queryGroupIds, breach);

int pageSize = request.paramAsInt("size", 10);
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 have validation logic for pageSize? eg. greater than 0 and less than a certain value to ensure performance.

Comment on lines +42 to +49
switch (input.toLowerCase()) {
case "query_group":
return QUERY_GROUP;
case "node_id":
return NODE_ID;
default:
throw new IllegalArgumentException("Invalid sort field: " + input);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use this to avoid using switch? Same for SortOrder

try {
            return SortBy.valueOf(input.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid sort field: " + input + ". Allowed values: query_group, node_id");
        }

@ruai0511
Copy link
Contributor

Can we provide a better name for this PR and add some description? A single word title is not descriptive

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants