Repository navigation
fix: v2 search drops rows after a sub-second startTime; make maxResults cap configurable #3374
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
Changes from all commits
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 |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@hyperdx/api': patch | ||
| --- | ||
|
|
||
| feat: Make the external API v2 search `maxResults` cap configurable via `EXTERNAL_API_SEARCH_MAX_RESULTS`. Defaults to 2000, matching the previous hardcoded value. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@hyperdx/common-utils': patch | ||
| --- | ||
|
|
||
| fix: Floor the time filter start on second-precision DateTime columns so a sub-second start no longer drops the rest of that second |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -992,13 +992,21 @@ export async function timeFilterExpr({ | |
| ); | ||
| } | ||
|
|
||
| const columnType = stripTypeWrappers(columnMeta?.type ?? ''); | ||
| // DateTime truncates to the second, so a sub-second or exclusive start | ||
| // compared as-is drops the rest of that second. | ||
| const isSecondPrecision = /^DateTime(?:\(.*\))?$/i.test(columnType); | ||
| const columnStartTime = isSecondPrecision | ||
| ? Math.floor(startTime / 1000) * 1000 | ||
| : startTime; | ||
|
|
||
| const rawStartBound = isRenderingRawSqlTemplate | ||
| ? includedDataInterval | ||
| ? chSql`toStartOfInterval($__fromTime_ms, INTERVAL $__interval_s second) - INTERVAL $__interval_s second` | ||
| : chSql`$__fromTime_ms` | ||
| : includedDataInterval | ||
| ? chSql`toStartOfInterval(fromUnixTimestamp64Milli(${{ Int64: startTime }}), INTERVAL ${includedDataInterval}) - INTERVAL ${includedDataInterval}` | ||
| : chSql`fromUnixTimestamp64Milli(${{ Int64: startTime }})`; | ||
| ? chSql`toStartOfInterval(fromUnixTimestamp64Milli(${{ Int64: columnStartTime }}), INTERVAL ${includedDataInterval}) - INTERVAL ${includedDataInterval}` | ||
| : chSql`fromUnixTimestamp64Milli(${{ Int64: columnStartTime }})`; | ||
|
Comment on lines
+1008
to
+1009
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new rounding only applies to queries with concrete dates. With Apply the same rounding in the generated template and cover the conversion with a test.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 979ab720c: in template mode a DateTime column now compares against |
||
|
|
||
| const rawEndBound = isRenderingRawSqlTemplate | ||
| ? includedDataInterval | ||
|
|
@@ -1016,13 +1024,13 @@ export async function timeFilterExpr({ | |
| ? chSql`${toStartOf.function}(${rawEndBound}${toStartOf.formattedRemainingArgs})` | ||
| : rawEndBound; | ||
|
|
||
| const isDateType = | ||
| /^Date(?:32)?$/i.test(stripTypeWrappers(columnMeta?.type ?? '')) || | ||
| isToDateExpr; | ||
| const isDateType = /^Date(?:32)?$/i.test(columnType) || isToDateExpr; | ||
|
|
||
| // toStartOf* and Date filters must stay inclusive — strict < on a rounded value drops a whole interval | ||
| const startOp = | ||
| dateRangeStartInclusive || toStartOf || isDateType ? '>=' : '>'; | ||
| dateRangeStartInclusive || toStartOf || isDateType || isSecondPrecision | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 major — Exclusive start becomes inclusive on DateTime-only sources, so adjacent windows return the same rows twice The forced Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 979ab720c. The forced |
||
| ? '>=' | ||
| : '>'; | ||
|
Comment on lines
+1031
to
+1033
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a source has only one Keep the exact comparison for a standalone timestamp. Only relax a coarse filter when another column enforces the requested boundary.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same fix as the thread above (979ab720c):
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks — I agree that the multi-column However, the current condition does not actually make To make the stated invariant true, the code needs to detect that another finer timestamp column exists and only relax the DateTime predicate in that case; otherwise the standalone DateTime path should retain
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That review ran on ca7d9b2; the PR was closed before the fix push, so it doesn't show 979ab720c. On the branch, |
||
| const endOp = | ||
| dateRangeEndInclusive || toStartOf || isDateType ? '<=' : '<'; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
EXTERNAL_API_SEARCH_MAX_RESULTSis below 100, requests that omitmaxResultsfail. The configuration accepts values such as 50, but Zod inserts the default of 100 and checks it against the configured maximum. Clients relying on the documented default receive a 400 response.Use
Math.min(100, EXTERNAL_API_SEARCH_MAX_RESULTS)as the default and add a request test for a cap below 100.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 979ab720c: the default is now
Math.min(100, EXTERNAL_API_SEARCH_MAX_RESULTS). I didn't add a request test. The cap is read when the module loads, so a test would need to reload the router with the env var set, and the schema isn't exported. Happy to add one if you prefer a particular pattern.