|
1 | 1 | import { WebhookDeliveryId } from "@trigger.dev/core/v3/isomorphic"; |
2 | 2 |
|
3 | 3 | /** |
4 | | - * Compute the `createdAt` span covering a set of webhook delivery friendlyIds, for partition-pruning a |
5 | | - * lookup by id on the RANGE-partitioned `WebhookDelivery` table. |
| 4 | + * Single-pass min/max over a set of `createdAt` timestamps (unix ms), returned as a Prisma |
| 5 | + * `{ gte, lte }` range for partition-pruning the RANGE-partitioned `WebhookDelivery` table. |
6 | 6 | * |
7 | | - * Each v1 id is time-encoded (see `WebhookDeliveryId`) with the same timestamp the engine stores as the |
8 | | - * row's `createdAt`, so the returned `[gte, lte]` covers every row in the set exactly. Returns |
9 | | - * `undefined` when the set is empty or contains any legacy (non-time-encoded) id: in that case the |
10 | | - * caller must not add a `createdAt` predicate, since a bound derived from only the decodable ids would |
11 | | - * wrongly exclude the legacy rows. |
| 7 | + * Avoids `Math.min(...spread)` / `Math.max(...spread)`: the spread builds an O(n) argument list and |
| 8 | + * throws "Maximum call stack size exceeded" once the array is large (~1e5+ elements). Returns |
| 9 | + * `undefined` for an empty set, so the caller adds no `createdAt` predicate. |
| 10 | + */ |
| 11 | +export function createdAtMsBounds(msValues: number[]): { gte: Date; lte: Date } | undefined { |
| 12 | + let min = Number.POSITIVE_INFINITY; |
| 13 | + let max = Number.NEGATIVE_INFINITY; |
| 14 | + |
| 15 | + for (const ms of msValues) { |
| 16 | + if (ms < min) min = ms; |
| 17 | + if (ms > max) max = ms; |
| 18 | + } |
| 19 | + |
| 20 | + if (min === Number.POSITIVE_INFINITY) return undefined; |
| 21 | + return { gte: new Date(min), lte: new Date(max) }; |
| 22 | +} |
| 23 | + |
| 24 | +/** |
| 25 | + * Compute the `createdAt` span covering a set of webhook delivery friendlyIds, for partition-pruning |
| 26 | + * a lookup by id on the RANGE-partitioned `WebhookDelivery` table. |
12 | 27 | * |
13 | | - * Single pass, no intermediate arrays and no `Math.min(...spread)` (which is O(n) to build the argument |
14 | | - * list and can overflow the call stack for large inputs). |
| 28 | + * Each v1 id is time-encoded (see `WebhookDeliveryId`) with the same timestamp the engine stores as |
| 29 | + * the row's `createdAt`, so the returned `[gte, lte]` covers every row in the set exactly. Returns |
| 30 | + * `undefined` when the set is empty or contains any legacy (non-time-encoded) id: in that case the |
| 31 | + * caller must not add a `createdAt` predicate, since a bound derived from only the decodable ids |
| 32 | + * would wrongly exclude the legacy rows. |
15 | 33 | */ |
16 | 34 | export function deliveryIdsCreatedAtBounds( |
17 | 35 | friendlyIds: string[] |
18 | 36 | ): { gte: Date; lte: Date } | undefined { |
19 | | - let min = Number.POSITIVE_INFINITY; |
20 | | - let max = Number.NEGATIVE_INFINITY; |
| 37 | + const msValues: number[] = []; |
21 | 38 |
|
22 | 39 | for (const friendlyId of friendlyIds) { |
23 | 40 | const timestamp = WebhookDeliveryId.parseTimestamp(friendlyId); |
24 | 41 | if (!timestamp) return undefined; |
25 | | - const ms = timestamp.getTime(); |
26 | | - if (ms < min) min = ms; |
27 | | - if (ms > max) max = ms; |
| 42 | + msValues.push(timestamp.getTime()); |
28 | 43 | } |
29 | 44 |
|
30 | | - if (min === Number.POSITIVE_INFINITY) return undefined; |
31 | | - return { gte: new Date(min), lte: new Date(max) }; |
| 45 | + return createdAtMsBounds(msValues); |
32 | 46 | } |
0 commit comments