Skip to content

Commit eff14ae

Browse files
committed
refactor(webapp): extract and simplify the delivery-id createdAt bounds calc
getDeliveriesByFriendlyIds computed the createdAt prune window in three passes (map to timestamps, filter, then map again inside Math.min(...) / Math.max(...)). Extract it to a single-pass deliveryIdsCreatedAtBounds that decodes each id once and tracks min/max in one loop, with no Math.min(...spread), which builds the whole argument list and overflows the stack on large inputs. Same result, plus a unit test covering the span, single-id, empty, and legacy-fallback cases.
1 parent e36405c commit eff14ae

3 files changed

Lines changed: 87 additions & 13 deletions

File tree

apps/webapp/app/services/webhookDeliveriesRepository/clickhouseWebhookDeliveriesRepository.server.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type ClickhouseQueryBuilder } from "@internal/clickhouse";
22
import { WebhookDeliveryId } from "@trigger.dev/core/v3/isomorphic";
33
import { boundedIn } from "@trigger.dev/database";
4+
import { deliveryIdsCreatedAtBounds } from "./deliveryIdBounds";
45
import { decodeRunsCursor, encodeRunsCursor } from "../runsRepository/runsCursor.server";
56
import {
67
type CountDeliveriesByEndpointOptions,
@@ -316,24 +317,13 @@ export class ClickHouseWebhookDeliveriesRepository implements IWebhookDeliveries
316317
const ids = options.friendlyIds.map((friendlyId) => WebhookDeliveryId.toId(friendlyId));
317318
if (ids.length === 0) return [];
318319

319-
const timestamps = options.friendlyIds
320-
.map((friendlyId) => WebhookDeliveryId.parseTimestamp(friendlyId))
321-
.filter((value): value is Date => value != null);
322-
const createdAtBound =
323-
timestamps.length === options.friendlyIds.length
324-
? {
325-
createdAt: {
326-
gte: new Date(Math.min(...timestamps.map((value) => value.getTime()))),
327-
lte: new Date(Math.max(...timestamps.map((value) => value.getTime()))),
328-
},
329-
}
330-
: {};
320+
const bounds = deliveryIdsCreatedAtBounds(options.friendlyIds);
331321

332322
return this.options.prisma.webhookDelivery.findMany({
333323
where: {
334324
id: { in: boundedIn(ids) },
335325
runtimeEnvironmentId: options.environmentId,
336-
...createdAtBound,
326+
...(bounds ? { createdAt: bounds } : {}),
337327
},
338328
select: DELIVERY_LIST_SELECT,
339329
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { WebhookDeliveryId } from "@trigger.dev/core/v3/isomorphic";
2+
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.
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.
12+
*
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).
15+
*/
16+
export function deliveryIdsCreatedAtBounds(
17+
friendlyIds: string[]
18+
): { gte: Date; lte: Date } | undefined {
19+
let min = Number.POSITIVE_INFINITY;
20+
let max = Number.NEGATIVE_INFINITY;
21+
22+
for (const friendlyId of friendlyIds) {
23+
const timestamp = WebhookDeliveryId.parseTimestamp(friendlyId);
24+
if (!timestamp) return undefined;
25+
const ms = timestamp.getTime();
26+
if (ms < min) min = ms;
27+
if (ms > max) max = ms;
28+
}
29+
30+
if (min === Number.POSITIVE_INFINITY) return undefined;
31+
return { gte: new Date(min), lte: new Date(max) };
32+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
import { WebhookDeliveryId } from "@trigger.dev/core/v3/isomorphic";
3+
import { deliveryIdsCreatedAtBounds } from "../app/services/webhookDeliveriesRepository/deliveryIdBounds";
4+
5+
function idAt(iso: string): string {
6+
vi.setSystemTime(new Date(iso));
7+
return WebhookDeliveryId.generate().friendlyId;
8+
}
9+
10+
describe("deliveryIdsCreatedAtBounds", () => {
11+
it("returns undefined for an empty set", () => {
12+
expect(deliveryIdsCreatedAtBounds([])).toBeUndefined();
13+
});
14+
15+
it("returns a zero-width span for a single id (gte == lte == its mint time)", () => {
16+
vi.useFakeTimers();
17+
try {
18+
const at = "2026-08-11T10:00:00.000Z";
19+
const friendlyId = idAt(at);
20+
const bounds = deliveryIdsCreatedAtBounds([friendlyId]);
21+
expect(bounds?.gte.toISOString()).toBe(at);
22+
expect(bounds?.lte.toISOString()).toBe(at);
23+
} finally {
24+
vi.useRealTimers();
25+
}
26+
});
27+
28+
it("spans the earliest and latest mint times across ids, regardless of input order", () => {
29+
vi.useFakeTimers();
30+
try {
31+
const early = idAt("2026-08-09T00:00:00.000Z");
32+
const mid = idAt("2026-08-10T12:00:00.000Z");
33+
const late = idAt("2026-08-11T23:59:59.000Z");
34+
const bounds = deliveryIdsCreatedAtBounds([mid, late, early]);
35+
expect(bounds?.gte.toISOString()).toBe("2026-08-09T00:00:00.000Z");
36+
expect(bounds?.lte.toISOString()).toBe("2026-08-11T23:59:59.000Z");
37+
} finally {
38+
vi.useRealTimers();
39+
}
40+
});
41+
42+
it("returns undefined when any id is legacy (non-time-encoded), so the caller skips pruning", () => {
43+
vi.useFakeTimers();
44+
try {
45+
const v1 = idAt("2026-08-11T10:00:00.000Z");
46+
expect(deliveryIdsCreatedAtBounds([v1, "whd_legacycuidstyleid"])).toBeUndefined();
47+
expect(deliveryIdsCreatedAtBounds(["whd_legacycuidstyleid"])).toBeUndefined();
48+
} finally {
49+
vi.useRealTimers();
50+
}
51+
});
52+
});

0 commit comments

Comments
 (0)