Skip to content

Commit 694312c

Browse files
kathiekiwiTrigger.dev RepoOps
authored andcommitted
feat(alerts): evaluate alerts every minute
Alerts are now checked every minute instead of every 5–10 minutes, so they raise and recover sooner. Mono-RevId: f147b6afe8616cecb53c38a6da5d3a6758e7caf8
1 parent 8491915 commit 694312c

4 files changed

Lines changed: 26 additions & 4 deletions

File tree

‎.server-changes/alerts-cadence.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Alerts are now checked every minute instead of every 5–10 minutes, so they raise and recover sooner.

‎apps/webapp/app/env.server.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ const EnvironmentSchema = z
10921092

10931093
/** These enable the alerts feature in v3 */
10941094
ALERT_EMAIL_TRANSPORT: z.enum(["resend", "smtp", "aws-ses"]).optional(),
1095-
ALERT_EVALUATION_CADENCE_CAP_SEC: z.coerce.number().int().positive().optional(),
1095+
ALERT_EVALUATION_CADENCE_CAP_SEC: z.coerce.number().int().positive().default(60),
10961096
ALERT_FROM_EMAIL: z.string().optional(),
10971097
ALERT_REPLY_TO_EMAIL: z.string().optional(),
10981098
ALERT_RESEND_API_KEY: z.string().optional(),

‎apps/webapp/app/v3/services/alerts/engine/evaluateAlert.server.test.ts‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ describe("evaluateAlert", () => {
154154
expect(deliveries.every((d) => d.status === "pending")).toBe(true);
155155
});
156156

157+
it("schedules the next evaluation within a minute for a 600s capability", async () => {
158+
await createAlert(db, {
159+
...BASE,
160+
capabilityKey: "queue.oldest_age",
161+
condition: { kind: "threshold", operator: ">", value: 100, windowSec: 600 },
162+
});
163+
const now = new Date();
164+
const claimed = await claimOne(now);
165+
166+
await evaluateAlert(claimed, { db, evaluateBinding: bindingFor(notMetResult()) });
167+
168+
const next = (await getAlert(db, { id: claimed.id }))!.nextEvaluationAt!.getTime();
169+
expect(next).toBeGreaterThan(now.getTime());
170+
expect(next - now.getTime()).toBeLessThanOrEqual(60_000);
171+
});
172+
157173
it("stateful met->met writes nothing (no duplicate raise)", async () => {
158174
await createAlert(db, { ...BASE, attention: "needs_attention", observedCondition: "met" });
159175
const now = new Date();
@@ -1057,7 +1073,7 @@ describe("unavailable reason tracking", () => {
10571073

10581074
it("successful evaluation schedules the next grid slot, not now + cadence", async () => {
10591075
const alert = await createAlert(db, { ...BASE });
1060-
const cadenceMs = 300_000;
1076+
const cadenceMs = 60_000;
10611077
const phase = alertPhaseOffsetMs(alert.id);
10621078
const slot = Math.floor((Date.now() - phase) / cadenceMs) * cadenceMs + phase;
10631079
const due = new Date(slot - cadenceMs);
@@ -1089,7 +1105,7 @@ describe("unavailable reason tracking", () => {
10891105

10901106
const after = (await getAlert(db, { id: alert.id }))!;
10911107
const phase = alertPhaseOffsetMs(alert.id);
1092-
const expected = (Math.floor((now.getTime() - phase) / 300_000) + 1) * 300_000 + phase;
1108+
const expected = (Math.floor((now.getTime() - phase) / 60_000) + 1) * 60_000 + phase;
10931109
expect(after.nextEvaluationAt!.getTime()).toBe(expected);
10941110
});
10951111

‎apps/webapp/app/v3/services/alerts/engine/evaluateAlertJob.server.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ describe("runEvaluateAlertJob", () => {
231231
expect.objectContaining({ alertId: alert.id, capabilityKey: alert.capabilityKey }),
232232
]);
233233
const nextAt = (await job(worker, alert.id))!.timestamp.getTime();
234-
const cadenceMs = getCapability(alert.capabilityKey)!.minCadenceSec * 1000;
234+
const cadenceMs = Math.min(getCapability(alert.capabilityKey)!.minCadenceSec, 60) * 1000;
235235
expect(nextAt).toBeGreaterThan(startedAt);
236236
expect((nextAt - alertPhaseOffsetMs(alert.id)) % cadenceMs).toBe(0);
237237
expect((await getAlert(db, { id: alert.id }))!.nextEvaluationAt!.getTime()).toBe(nextAt);

0 commit comments

Comments
 (0)