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

Update SqlQueryGenerator to support client-side paging #3536

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

Conversation

almostchristian
Copy link

Description

Updates SqlQueryGenerator to support client-side paging by generating queries that are paged using OFFSET. If the configuration SupportsIndexedPaging is set to false, the old query generation will be used. If the ct parameter is numeric, indexed paging will be used regardless of the SupportsIndexedPaging configuration.

Related issues

Addresses #2454.

Testing

Added tests in ChainingSearchTests

FHIR Team Checklist

  • Update the title of the PR to be succinct and less than 65 characters
  • Add a milestone to the PR for the sprint that it is merged (i.e. add S47)
  • Tag the PR with the type of update: Bug, Build, Dependencies, Enhancement, New-Feature or Documentation
  • Tag the PR with Open source, Azure API for FHIR (CosmosDB or common code) or Azure Healthcare APIs (SQL or common code) to specify where this change is intended to be released.
  • Tag the PR with Schema Version backward compatible or Schema Version backward incompatible or Schema Version unchanged if this adds or updates Sql script which is/is not backward compatible with the code.
  • CI is green before merge Build Status
  • Review squash-merge requirements

Semver Change (docs)

Patch|Skip|Feature|Breaking (reason)

@almostchristian
Copy link
Author

@microsoft-github-policy-service agree

@almostchristian almostchristian marked this pull request as ready for review September 25, 2023 15:25
@almostchristian almostchristian requested a review from a team as a code owner September 25, 2023 15:25
@LTA-Thinking LTA-Thinking added this to the S126 milestone Oct 17, 2023
@LTA-Thinking LTA-Thinking added Enhancement Enhancement on existing functionality. Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs labels Oct 17, 2023
@LTA-Thinking
Copy link
Collaborator

/azp run

Copy link
Collaborator

@LTA-Thinking LTA-Thinking left a comment

Choose a reason for hiding this comment

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

Looks good on first pass, still running some tests on the behavior. If we include this it will need to default to off as it otherwise would be a breaking change.
Something I need to still test is how this handles updates compared with the old CTs.

@@ -118,7 +119,18 @@ public SearchOptions Create(string resourceType, IReadOnlyList<Tuple<string, str
string.Format(Core.Resources.MultipleQueryParametersNotAllowed, KnownQueryParameterNames.ContinuationToken));
}

continuationToken = ContinuationTokenConverter.Decode(query.Item2);
// check is continuation token is a numerical format which means it must use an offset
if (int.TryParse(query.Item2, out _))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check is being done both here and in the continuation token converter. Making a note to see if it is needed in both locations.

@@ -95,7 +108,8 @@ public override Expression VisitSqlRoot(SqlRootExpression expression, SqlSearchO
// for resources that have a value for the _sort parameter. So we will generate
// the appropriate Sort expression below.
}
else if (continuationToken != null && continuationToken.SortValue == null)
else if ((continuationToken != null && continuationToken.SortValue == null) ||
(continuationToken == null && context.Sort[0].sortOrder == SortOrder.Ascending))
{
// We have a ct for resourceid but not for the sort value.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you update the comment to fit the new logic?

Copy link
Author

Choose a reason for hiding this comment

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

I removed this change

@@ -773,7 +792,7 @@ void AppendOrderBy()

StringBuilder.Append("SELECT DISTINCT ");

if (includeExpression.Reversed)
if (includeExpression.Reversed && context.UseIndexedPaging)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this case only needed when using indexed paging? This is used to prevent too many results from being returned from a revinclude.

Copy link
Author

Choose a reason for hiding this comment

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

I reverted this change. This was an old change i did when I was messing around.

@LTA-Thinking
Copy link
Collaborator

A difference I found in behavior between the two CTs:

  • User A runs a search, gets a CT
  • User B executes an action that adds a resource that would have been on the first page of User A's results
  • User A runs a search with their CT
  • With the current CTs User A's sees the next resource they were looking for. With index based CTs User A's first result is the last results from the first page of results, because User B's action pushed it to now be on the second page of results.

This could cause issues for services with heavy usage as the user could get "more" results then they are expecting, or get duplicates if they aren't looking for this issue.

@LTA-Thinking
Copy link
Collaborator

@almostchristian do you have performance numbers comparing runtimes of your change to our default method?

@almostchristian
Copy link
Author

A difference I found in behavior between the two CTs:

* User A runs a search, gets a CT

* User B executes an action that adds a resource that would have been on the first page of User A's results

* User A runs a search with their CT

* With the current CTs User A's sees the next resource they were looking for. With index based CTs User A's first result is the last results from the first page of results, because User B's action pushed it to now be on the second page of results.

This could cause issues for services with heavy usage as the user could get "more" results then they are expecting, or get duplicates if they aren't looking for this issue.

Thanks for testing, I think this is an expected behavior with this indexed based paging. Indexed based list is suitable for UIs that have page indexes instead of an infinite scroll. Our UIs uses the traditional table with page numbers, and indexed based paging allows us to generate the links without having to walk through the entire list which would be required in the current ct.

@almostchristian
Copy link
Author

@almostchristian do you have performance numbers comparing runtimes of your change to our default method?

I don't have the comparison of the performance of the generated sql, but I was able to measure the e2e performance below.

Test setup: From a fresh db, create patient with same data 1000 times, then search by patient.identifier.
I use nbomber and run with 100 threads for 2 minutes with a 5 second warmup.

I get the first page and second page, with _count of 2.
Numbers below are the average of 3 runs.

paging mode page1 mean page1 p95 page1 p99 page2 mean page2 p95 page2 p99
default 33.88 59.35 68.02 41.47 53.02 59.69
indexed 32.41 58.68 68.69 39.59 53.02 60.36

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Enhancement Enhancement on existing functionality.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants