@voyantjs/workflows-orchestrator-node 0.30.1 → 0.30.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node-standalone-driver.d.ts.map +1 -1
- package/dist/node-standalone-driver.js +1 -0
- package/dist/postgres-schema.d.ts +17 -0
- package/dist/postgres-schema.d.ts.map +1 -1
- package/dist/postgres-schema.js +2 -1
- package/dist/postgres-wakeup-store.d.ts +1 -0
- package/dist/postgres-wakeup-store.d.ts.map +1 -1
- package/dist/postgres-wakeup-store.js +11 -2
- package/dist/scheduler.d.ts +2 -12
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +2 -98
- package/dist/wakeup-store.d.ts +1 -0
- package/dist/wakeup-store.d.ts.map +1 -1
- package/dist/wakeup-store.js +8 -1
- package/drizzle/0005_wakeup_priority.sql +3 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +3 -3
- package/src/node-standalone-driver.ts +1 -0
- package/src/postgres-schema.ts +2 -1
- package/src/postgres-wakeup-store.ts +12 -2
- package/src/scheduler.ts +10 -106
- package/src/wakeup-store.ts +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-standalone-driver.d.ts","sourceRoot":"","sources":["../src/node-standalone-driver.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EACV,aAAa,EAOd,MAAM,4BAA4B,CAAA;AAInC,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,WAAW,EACjB,MAAM,kCAAkC,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAWxD,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAIpC,MAAM,WAAW,2BAA2B;IAC1C,4EAA4E;IAC5E,EAAE,EAAE,EAAE,CAAA;IACN,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;IAC7D,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,2BAA2B,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"node-standalone-driver.d.ts","sourceRoot":"","sources":["../src/node-standalone-driver.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EACV,aAAa,EAOd,MAAM,4BAA4B,CAAA;AAInC,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,WAAW,EACjB,MAAM,kCAAkC,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAWxD,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAIpC,MAAM,WAAW,2BAA2B;IAC1C,4EAA4E;IAC5E,EAAE,EAAE,EAAE,CAAA;IACN,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;IAC7D,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,2BAA2B,GAAG,aAAa,CA+R3F"}
|
|
@@ -138,6 +138,7 @@ export function createNodeStandaloneDriver(opts) {
|
|
|
138
138
|
tags: triggerOpts?.tags,
|
|
139
139
|
idempotencyKey: triggerOpts?.idempotencyKey,
|
|
140
140
|
delay: triggerOpts?.delay,
|
|
141
|
+
priority: triggerOpts?.priority,
|
|
141
142
|
}, { store: runStore, handler, now });
|
|
142
143
|
// Sync wakeup row so the time-wheel can resume DATETIME-parked runs.
|
|
143
144
|
// No-op if the run completed inline (status !== "waiting").
|
|
@@ -272,6 +272,23 @@ export declare const wakeupsTable: import("drizzle-orm/pg-core").PgTableWithColu
|
|
|
272
272
|
identity: undefined;
|
|
273
273
|
generated: undefined;
|
|
274
274
|
}, {}, {}>;
|
|
275
|
+
priority: import("drizzle-orm/pg-core").PgColumn<{
|
|
276
|
+
name: "priority";
|
|
277
|
+
tableName: "voyant_wakeups";
|
|
278
|
+
dataType: "number";
|
|
279
|
+
columnType: "PgInteger";
|
|
280
|
+
data: number;
|
|
281
|
+
driverParam: string | number;
|
|
282
|
+
notNull: true;
|
|
283
|
+
hasDefault: true;
|
|
284
|
+
isPrimaryKey: false;
|
|
285
|
+
isAutoincrement: false;
|
|
286
|
+
hasRuntimeDefault: false;
|
|
287
|
+
enumValues: undefined;
|
|
288
|
+
baseColumn: never;
|
|
289
|
+
identity: undefined;
|
|
290
|
+
generated: undefined;
|
|
291
|
+
}, {}, {}>;
|
|
275
292
|
leaseOwner: import("drizzle-orm/pg-core").PgColumn<{
|
|
276
293
|
name: "lease_owner";
|
|
277
294
|
tableName: "voyant_wakeups";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-schema.d.ts","sourceRoot":"","sources":["../src/postgres-schema.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0C7B,CAAA;AAED,eAAO,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"postgres-schema.d.ts","sourceRoot":"","sources":["../src/postgres-schema.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0C7B,CAAA;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAelC,CAAA"}
|
package/dist/postgres-schema.js
CHANGED
|
@@ -36,11 +36,12 @@ export const snapshotRunsTable = pgTable("voyant_snapshot_runs", {
|
|
|
36
36
|
export const wakeupsTable = pgTable("voyant_wakeups", {
|
|
37
37
|
runId: text("run_id").primaryKey(),
|
|
38
38
|
wakeAt: bigint("wake_at", { mode: "number" }).notNull(),
|
|
39
|
+
priority: integer("priority").notNull().default(0),
|
|
39
40
|
leaseOwner: text("lease_owner"),
|
|
40
41
|
leaseExpiresAt: bigint("lease_expires_at", { mode: "number" }),
|
|
41
42
|
updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
|
|
42
43
|
}, (table) => ({
|
|
43
|
-
dueIdx: index("voyant_wakeups_due_idx").on(table.wakeAt),
|
|
44
|
+
dueIdx: index("voyant_wakeups_due_idx").on(table.priority.desc(), table.wakeAt.asc()),
|
|
44
45
|
leaseIdx: index("voyant_wakeups_lease_idx").on(table.leaseExpiresAt),
|
|
45
46
|
}));
|
|
46
47
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-wakeup-store.d.ts","sourceRoot":"","sources":["../src/postgres-wakeup-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAElE,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAE1C,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,QAAQ,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"postgres-wakeup-store.d.ts","sourceRoot":"","sources":["../src/postgres-wakeup-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAElE,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAE1C,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,QAAQ,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,WAAW,CAkHvF;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC/D,SAAS,EAAE,MAAM;;;;;;;EAUlB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,YAAY,CAAC,YAAY,GAAG,YAAY,CASrF"}
|
|
@@ -49,7 +49,7 @@ export function createPostgresWakeupStore(opts) {
|
|
|
49
49
|
OR lease_expires_at <= ${at}
|
|
50
50
|
OR lease_owner = ${owner}
|
|
51
51
|
)
|
|
52
|
-
ORDER BY wake_at ASC
|
|
52
|
+
ORDER BY priority DESC, wake_at ASC
|
|
53
53
|
LIMIT ${limit}
|
|
54
54
|
FOR UPDATE SKIP LOCKED
|
|
55
55
|
)
|
|
@@ -59,12 +59,19 @@ export function createPostgresWakeupStore(opts) {
|
|
|
59
59
|
updated_at = ${at}
|
|
60
60
|
FROM due
|
|
61
61
|
WHERE wakeups.run_id = due.run_id
|
|
62
|
-
RETURNING wakeups.run_id, wakeups.wake_at, wakeups.lease_owner, wakeups.lease_expires_at, wakeups.updated_at
|
|
62
|
+
RETURNING wakeups.run_id, wakeups.wake_at, wakeups.priority, wakeups.lease_owner, wakeups.lease_expires_at, wakeups.updated_at
|
|
63
63
|
`);
|
|
64
64
|
const rows = result.rows;
|
|
65
|
+
rows.sort((a, b) => {
|
|
66
|
+
const priorityDelta = toNumber(b.priority) - toNumber(a.priority);
|
|
67
|
+
if (priorityDelta !== 0)
|
|
68
|
+
return priorityDelta;
|
|
69
|
+
return toNumber(a.wake_at) - toNumber(b.wake_at);
|
|
70
|
+
});
|
|
65
71
|
return rows.map((row) => ({
|
|
66
72
|
runId: row.run_id,
|
|
67
73
|
wakeAt: toNumber(row.wake_at),
|
|
74
|
+
priority: toNumber(row.priority),
|
|
68
75
|
leaseOwner: row.lease_owner ?? undefined,
|
|
69
76
|
leaseExpiresAt: row.lease_expires_at === null ? undefined : toNumber(row.lease_expires_at),
|
|
70
77
|
updatedAt: toNumber(row.updated_at),
|
|
@@ -90,6 +97,7 @@ export function wakeupToRow(record, updatedAt) {
|
|
|
90
97
|
return {
|
|
91
98
|
runId: record.runId,
|
|
92
99
|
wakeAt: record.wakeAt,
|
|
100
|
+
priority: record.priority ?? 0,
|
|
93
101
|
leaseOwner: record.leaseOwner ?? null,
|
|
94
102
|
leaseExpiresAt: record.leaseExpiresAt ?? null,
|
|
95
103
|
updatedAt,
|
|
@@ -99,6 +107,7 @@ export function rowToWakeupRecord(row) {
|
|
|
99
107
|
return {
|
|
100
108
|
runId: row.runId,
|
|
101
109
|
wakeAt: row.wakeAt,
|
|
110
|
+
priority: row.priority,
|
|
102
111
|
leaseOwner: row.leaseOwner ?? undefined,
|
|
103
112
|
leaseExpiresAt: row.leaseExpiresAt ?? undefined,
|
|
104
113
|
updatedAt: row.updatedAt,
|
package/dist/scheduler.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EnvironmentName, ScheduleDeclaration } from "@voyantjs/workflows";
|
|
2
|
+
export { type CronSpec, computeNextFire, nextCronFire, parseCron, toMs, } from "@voyantjs/workflows-orchestrator";
|
|
2
3
|
export interface ScheduleSource {
|
|
3
4
|
workflowId: string;
|
|
4
5
|
decl: ScheduleDeclaration;
|
|
@@ -30,15 +31,4 @@ export interface SchedulerHandle {
|
|
|
30
31
|
sourceCount: () => number;
|
|
31
32
|
}
|
|
32
33
|
export declare function createScheduler(deps: SchedulerDeps): SchedulerHandle;
|
|
33
|
-
export declare function computeNextFire(decl: ScheduleDeclaration, fromMs: number): number;
|
|
34
|
-
export interface CronSpec {
|
|
35
|
-
minute: number[];
|
|
36
|
-
hour: number[];
|
|
37
|
-
day: number[];
|
|
38
|
-
month: number[];
|
|
39
|
-
dow: number[];
|
|
40
|
-
}
|
|
41
|
-
export declare function parseCron(expr: string): CronSpec;
|
|
42
|
-
export declare function nextCronFire(spec: CronSpec, fromMs: number): number;
|
|
43
|
-
export declare function toMs(duration: Duration): number;
|
|
44
34
|
//# sourceMappingURL=scheduler.d.ts.map
|
package/dist/scheduler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAG/E,OAAO,EACL,KAAK,QAAQ,EACb,eAAe,EACf,YAAY,EACZ,SAAS,EACT,IAAI,GACL,MAAM,kCAAkC,CAAA;AAEzC,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,mBAAmB,CAAA;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;IAClC,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9F,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,WAAW,CAAA;IAChC,aAAa,CAAC,EAAE,OAAO,aAAa,CAAA;IACpC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAChF;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,WAAW,EAAE,MAAM;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IACzF,WAAW,EAAE,MAAM,MAAM,CAAA;CAC1B;AASD,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,CAsGpE"}
|
package/dist/scheduler.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { computeNextFire } from "@voyantjs/workflows-orchestrator";
|
|
2
|
+
export { computeNextFire, nextCronFire, parseCron, toMs, } from "@voyantjs/workflows-orchestrator";
|
|
1
3
|
export function createScheduler(deps) {
|
|
2
4
|
const now = deps.now ?? (() => Date.now());
|
|
3
5
|
const tickMs = deps.tickMs ?? 1_000;
|
|
@@ -107,101 +109,3 @@ async function resolveInput(input) {
|
|
|
107
109
|
}
|
|
108
110
|
return input;
|
|
109
111
|
}
|
|
110
|
-
export function computeNextFire(decl, fromMs) {
|
|
111
|
-
if ("cron" in decl)
|
|
112
|
-
return nextCronFire(parseCron(decl.cron), fromMs);
|
|
113
|
-
if ("every" in decl)
|
|
114
|
-
return fromMs + toMs(decl.every);
|
|
115
|
-
if ("at" in decl) {
|
|
116
|
-
const at = typeof decl.at === "string" ? Date.parse(decl.at) : decl.at.getTime();
|
|
117
|
-
if (!Number.isFinite(at))
|
|
118
|
-
throw new Error(`invalid "at" value: ${String(decl.at)}`);
|
|
119
|
-
return at < fromMs ? Number.POSITIVE_INFINITY : at;
|
|
120
|
-
}
|
|
121
|
-
throw new Error(`schedule declaration missing one of cron/every/at`);
|
|
122
|
-
}
|
|
123
|
-
export function parseCron(expr) {
|
|
124
|
-
const parts = expr.trim().split(/\s+/);
|
|
125
|
-
if (parts.length !== 5) {
|
|
126
|
-
throw new Error(`invalid cron "${expr}" — expected 5 fields (minute hour day month dow)`);
|
|
127
|
-
}
|
|
128
|
-
return {
|
|
129
|
-
minute: parseField(parts[0], 0, 59, "minute"),
|
|
130
|
-
hour: parseField(parts[1], 0, 23, "hour"),
|
|
131
|
-
day: parseField(parts[2], 1, 31, "day"),
|
|
132
|
-
month: parseField(parts[3], 1, 12, "month"),
|
|
133
|
-
dow: parseField(parts[4], 0, 6, "dow"),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
function parseField(f, min, max, label) {
|
|
137
|
-
const out = new Set();
|
|
138
|
-
for (const part of f.split(",")) {
|
|
139
|
-
const stepMatch = /^(.+)\/(\d+)$/.exec(part);
|
|
140
|
-
const body = stepMatch ? stepMatch[1] : part;
|
|
141
|
-
const step = stepMatch ? Number(stepMatch[2]) : 1;
|
|
142
|
-
if (!(step >= 1))
|
|
143
|
-
throw new Error(`cron ${label} step must be >=1 in "${f}"`);
|
|
144
|
-
let lo;
|
|
145
|
-
let hi;
|
|
146
|
-
if (body === "*") {
|
|
147
|
-
lo = min;
|
|
148
|
-
hi = max;
|
|
149
|
-
}
|
|
150
|
-
else if (body.includes("-")) {
|
|
151
|
-
const [a, b] = body.split("-");
|
|
152
|
-
lo = Number(a);
|
|
153
|
-
hi = Number(b);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
lo = Number(body);
|
|
157
|
-
hi = lo;
|
|
158
|
-
}
|
|
159
|
-
if (!Number.isFinite(lo) || !Number.isFinite(hi) || lo < min || hi > max || lo > hi) {
|
|
160
|
-
throw new Error(`cron ${label} out of range [${min}..${max}] in "${f}"`);
|
|
161
|
-
}
|
|
162
|
-
for (let i = lo; i <= hi; i += step)
|
|
163
|
-
out.add(i);
|
|
164
|
-
}
|
|
165
|
-
return [...out].sort((a, b) => a - b);
|
|
166
|
-
}
|
|
167
|
-
export function nextCronFire(spec, fromMs) {
|
|
168
|
-
const date = new Date(fromMs);
|
|
169
|
-
date.setUTCSeconds(0, 0);
|
|
170
|
-
date.setUTCMinutes(date.getUTCMinutes() + 1);
|
|
171
|
-
const maxIterations = 60 * 24 * 366 * 5;
|
|
172
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
173
|
-
if (spec.minute.includes(date.getUTCMinutes()) &&
|
|
174
|
-
spec.hour.includes(date.getUTCHours()) &&
|
|
175
|
-
spec.day.includes(date.getUTCDate()) &&
|
|
176
|
-
spec.month.includes(date.getUTCMonth() + 1) &&
|
|
177
|
-
spec.dow.includes(date.getUTCDay())) {
|
|
178
|
-
return date.getTime();
|
|
179
|
-
}
|
|
180
|
-
date.setUTCMinutes(date.getUTCMinutes() + 1);
|
|
181
|
-
}
|
|
182
|
-
throw new Error("cron search exceeded 5 years without finding a match");
|
|
183
|
-
}
|
|
184
|
-
export function toMs(duration) {
|
|
185
|
-
if (typeof duration === "number")
|
|
186
|
-
return duration;
|
|
187
|
-
const m = /^(\d+)(ms|s|m|h|d|w)$/.exec(duration);
|
|
188
|
-
if (!m)
|
|
189
|
-
throw new Error(`invalid duration "${duration}"`);
|
|
190
|
-
const n = Number(m[1]);
|
|
191
|
-
switch (m[2]) {
|
|
192
|
-
case "ms":
|
|
193
|
-
return n;
|
|
194
|
-
case "s":
|
|
195
|
-
return n * 1_000;
|
|
196
|
-
case "m":
|
|
197
|
-
return n * 60_000;
|
|
198
|
-
case "h":
|
|
199
|
-
return n * 3_600_000;
|
|
200
|
-
case "d":
|
|
201
|
-
return n * 86_400_000;
|
|
202
|
-
case "w":
|
|
203
|
-
return n * 604_800_000;
|
|
204
|
-
default:
|
|
205
|
-
throw new Error(`invalid duration "${duration}"`);
|
|
206
|
-
}
|
|
207
|
-
}
|
package/dist/wakeup-store.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wakeup-store.d.ts","sourceRoot":"","sources":["../src/wakeup-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAGjE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAA;IACzD,MAAM,EAAE,CACN,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAC7D,OAAO,CAAC,YAAY,CAAC,CAAA;IAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,KAAK,EAAE,MAAM,CAAA;QACb,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC7B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,WAAW,CA4EhF;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"wakeup-store.d.ts","sourceRoot":"","sources":["../src/wakeup-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAGjE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAA;IACzD,MAAM,EAAE,CACN,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAC7D,OAAO,CAAC,YAAY,CAAC,CAAA;IAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,KAAK,EAAE,MAAM,CAAA;QACb,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC7B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,oBAAyB,GAAG,WAAW,CA4EhF;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/F"}
|
package/dist/wakeup-store.js
CHANGED
|
@@ -41,7 +41,7 @@ export function createFsWakeupStore(opts = {}) {
|
|
|
41
41
|
return wakeups;
|
|
42
42
|
},
|
|
43
43
|
async leaseDue({ owner, now: at = now(), leaseMs, limit = 25 }) {
|
|
44
|
-
const wakeups = await this.list();
|
|
44
|
+
const wakeups = (await this.list()).sort(compareWakeupClaimOrder);
|
|
45
45
|
const leased = [];
|
|
46
46
|
for (const wakeup of wakeups) {
|
|
47
47
|
if (leased.length >= limit)
|
|
@@ -86,10 +86,17 @@ export async function syncWakeupFromRecord(store, record) {
|
|
|
86
86
|
await store.upsert({
|
|
87
87
|
runId: record.id,
|
|
88
88
|
wakeAt,
|
|
89
|
+
priority: record.priority,
|
|
89
90
|
leaseOwner: undefined,
|
|
90
91
|
leaseExpiresAt: undefined,
|
|
91
92
|
});
|
|
92
93
|
}
|
|
94
|
+
function compareWakeupClaimOrder(a, b) {
|
|
95
|
+
const priorityDelta = (b.priority ?? 0) - (a.priority ?? 0);
|
|
96
|
+
if (priorityDelta !== 0)
|
|
97
|
+
return priorityDelta;
|
|
98
|
+
return a.wakeAt - b.wakeAt;
|
|
99
|
+
}
|
|
93
100
|
async function safeReaddir(dir) {
|
|
94
101
|
try {
|
|
95
102
|
const entries = await readdir(dir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/workflows-orchestrator-node",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.3",
|
|
4
4
|
"description": "Node/Docker runtime primitives for @voyantjs/workflows-orchestrator, including a file-backed run store and local scheduler.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"drizzle-orm": "^0.45.2",
|
|
31
31
|
"pg": "^8.20.0",
|
|
32
|
-
"@voyantjs/workflows-orchestrator": "0.30.
|
|
33
|
-
"@voyantjs/workflows": "0.30.
|
|
32
|
+
"@voyantjs/workflows-orchestrator": "0.30.3",
|
|
33
|
+
"@voyantjs/workflows": "0.30.3"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^20.12.0",
|
|
@@ -244,6 +244,7 @@ export function createNodeStandaloneDriver(opts: NodeStandaloneDriverOptions): D
|
|
|
244
244
|
tags: triggerOpts?.tags,
|
|
245
245
|
idempotencyKey: triggerOpts?.idempotencyKey,
|
|
246
246
|
delay: triggerOpts?.delay,
|
|
247
|
+
priority: triggerOpts?.priority,
|
|
247
248
|
},
|
|
248
249
|
{ store: runStore, handler, now },
|
|
249
250
|
)
|
package/src/postgres-schema.ts
CHANGED
|
@@ -61,12 +61,13 @@ export const wakeupsTable = pgTable(
|
|
|
61
61
|
{
|
|
62
62
|
runId: text("run_id").primaryKey(),
|
|
63
63
|
wakeAt: bigint("wake_at", { mode: "number" }).notNull(),
|
|
64
|
+
priority: integer("priority").notNull().default(0),
|
|
64
65
|
leaseOwner: text("lease_owner"),
|
|
65
66
|
leaseExpiresAt: bigint("lease_expires_at", { mode: "number" }),
|
|
66
67
|
updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
|
|
67
68
|
},
|
|
68
69
|
(table) => ({
|
|
69
|
-
dueIdx: index("voyant_wakeups_due_idx").on(table.wakeAt),
|
|
70
|
+
dueIdx: index("voyant_wakeups_due_idx").on(table.priority.desc(), table.wakeAt.asc()),
|
|
70
71
|
leaseIdx: index("voyant_wakeups_lease_idx").on(table.leaseExpiresAt),
|
|
71
72
|
}),
|
|
72
73
|
)
|
|
@@ -62,6 +62,7 @@ export function createPostgresWakeupStore(opts: PostgresWakeupStoreOptions): Wak
|
|
|
62
62
|
wake_at: number
|
|
63
63
|
lease_owner: string | null
|
|
64
64
|
lease_expires_at: number | null
|
|
65
|
+
priority: number
|
|
65
66
|
updated_at: number
|
|
66
67
|
}>(sql`
|
|
67
68
|
WITH due AS (
|
|
@@ -73,7 +74,7 @@ export function createPostgresWakeupStore(opts: PostgresWakeupStoreOptions): Wak
|
|
|
73
74
|
OR lease_expires_at <= ${at}
|
|
74
75
|
OR lease_owner = ${owner}
|
|
75
76
|
)
|
|
76
|
-
ORDER BY wake_at ASC
|
|
77
|
+
ORDER BY priority DESC, wake_at ASC
|
|
77
78
|
LIMIT ${limit}
|
|
78
79
|
FOR UPDATE SKIP LOCKED
|
|
79
80
|
)
|
|
@@ -83,18 +84,25 @@ export function createPostgresWakeupStore(opts: PostgresWakeupStoreOptions): Wak
|
|
|
83
84
|
updated_at = ${at}
|
|
84
85
|
FROM due
|
|
85
86
|
WHERE wakeups.run_id = due.run_id
|
|
86
|
-
RETURNING wakeups.run_id, wakeups.wake_at, wakeups.lease_owner, wakeups.lease_expires_at, wakeups.updated_at
|
|
87
|
+
RETURNING wakeups.run_id, wakeups.wake_at, wakeups.priority, wakeups.lease_owner, wakeups.lease_expires_at, wakeups.updated_at
|
|
87
88
|
`)
|
|
88
89
|
const rows = result.rows as Array<{
|
|
89
90
|
run_id: string
|
|
90
91
|
wake_at: number | string
|
|
92
|
+
priority: number | string
|
|
91
93
|
lease_owner: string | null
|
|
92
94
|
lease_expires_at: number | string | null
|
|
93
95
|
updated_at: number | string
|
|
94
96
|
}>
|
|
97
|
+
rows.sort((a, b) => {
|
|
98
|
+
const priorityDelta = toNumber(b.priority) - toNumber(a.priority)
|
|
99
|
+
if (priorityDelta !== 0) return priorityDelta
|
|
100
|
+
return toNumber(a.wake_at) - toNumber(b.wake_at)
|
|
101
|
+
})
|
|
95
102
|
return rows.map((row) => ({
|
|
96
103
|
runId: row.run_id,
|
|
97
104
|
wakeAt: toNumber(row.wake_at),
|
|
105
|
+
priority: toNumber(row.priority),
|
|
98
106
|
leaseOwner: row.lease_owner ?? undefined,
|
|
99
107
|
leaseExpiresAt: row.lease_expires_at === null ? undefined : toNumber(row.lease_expires_at),
|
|
100
108
|
updatedAt: toNumber(row.updated_at),
|
|
@@ -125,6 +133,7 @@ export function wakeupToRow(
|
|
|
125
133
|
return {
|
|
126
134
|
runId: record.runId,
|
|
127
135
|
wakeAt: record.wakeAt,
|
|
136
|
+
priority: record.priority ?? 0,
|
|
128
137
|
leaseOwner: record.leaseOwner ?? null,
|
|
129
138
|
leaseExpiresAt: record.leaseExpiresAt ?? null,
|
|
130
139
|
updatedAt,
|
|
@@ -135,6 +144,7 @@ export function rowToWakeupRecord(row: typeof wakeupsTable.$inferSelect): Wakeup
|
|
|
135
144
|
return {
|
|
136
145
|
runId: row.runId,
|
|
137
146
|
wakeAt: row.wakeAt,
|
|
147
|
+
priority: row.priority,
|
|
138
148
|
leaseOwner: row.leaseOwner ?? undefined,
|
|
139
149
|
leaseExpiresAt: row.leaseExpiresAt ?? undefined,
|
|
140
150
|
updatedAt: row.updatedAt,
|
package/src/scheduler.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EnvironmentName, ScheduleDeclaration } from "@voyantjs/workflows"
|
|
2
|
+
import { computeNextFire } from "@voyantjs/workflows-orchestrator"
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
type CronSpec,
|
|
6
|
+
computeNextFire,
|
|
7
|
+
nextCronFire,
|
|
8
|
+
parseCron,
|
|
9
|
+
toMs,
|
|
10
|
+
} from "@voyantjs/workflows-orchestrator"
|
|
2
11
|
|
|
3
12
|
export interface ScheduleSource {
|
|
4
13
|
workflowId: string
|
|
@@ -143,108 +152,3 @@ async function resolveInput(
|
|
|
143
152
|
}
|
|
144
153
|
return input
|
|
145
154
|
}
|
|
146
|
-
|
|
147
|
-
export function computeNextFire(decl: ScheduleDeclaration, fromMs: number): number {
|
|
148
|
-
if ("cron" in decl) return nextCronFire(parseCron(decl.cron), fromMs)
|
|
149
|
-
if ("every" in decl) return fromMs + toMs(decl.every)
|
|
150
|
-
if ("at" in decl) {
|
|
151
|
-
const at = typeof decl.at === "string" ? Date.parse(decl.at) : decl.at.getTime()
|
|
152
|
-
if (!Number.isFinite(at)) throw new Error(`invalid "at" value: ${String(decl.at)}`)
|
|
153
|
-
return at < fromMs ? Number.POSITIVE_INFINITY : at
|
|
154
|
-
}
|
|
155
|
-
throw new Error(`schedule declaration missing one of cron/every/at`)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface CronSpec {
|
|
159
|
-
minute: number[]
|
|
160
|
-
hour: number[]
|
|
161
|
-
day: number[]
|
|
162
|
-
month: number[]
|
|
163
|
-
dow: number[]
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function parseCron(expr: string): CronSpec {
|
|
167
|
-
const parts = expr.trim().split(/\s+/)
|
|
168
|
-
if (parts.length !== 5) {
|
|
169
|
-
throw new Error(`invalid cron "${expr}" — expected 5 fields (minute hour day month dow)`)
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
minute: parseField(parts[0]!, 0, 59, "minute"),
|
|
173
|
-
hour: parseField(parts[1]!, 0, 23, "hour"),
|
|
174
|
-
day: parseField(parts[2]!, 1, 31, "day"),
|
|
175
|
-
month: parseField(parts[3]!, 1, 12, "month"),
|
|
176
|
-
dow: parseField(parts[4]!, 0, 6, "dow"),
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function parseField(f: string, min: number, max: number, label: string): number[] {
|
|
181
|
-
const out = new Set<number>()
|
|
182
|
-
for (const part of f.split(",")) {
|
|
183
|
-
const stepMatch = /^(.+)\/(\d+)$/.exec(part)
|
|
184
|
-
const body = stepMatch ? stepMatch[1]! : part
|
|
185
|
-
const step = stepMatch ? Number(stepMatch[2]) : 1
|
|
186
|
-
if (!(step >= 1)) throw new Error(`cron ${label} step must be >=1 in "${f}"`)
|
|
187
|
-
let lo: number
|
|
188
|
-
let hi: number
|
|
189
|
-
if (body === "*") {
|
|
190
|
-
lo = min
|
|
191
|
-
hi = max
|
|
192
|
-
} else if (body.includes("-")) {
|
|
193
|
-
const [a, b] = body.split("-")
|
|
194
|
-
lo = Number(a)
|
|
195
|
-
hi = Number(b)
|
|
196
|
-
} else {
|
|
197
|
-
lo = Number(body)
|
|
198
|
-
hi = lo
|
|
199
|
-
}
|
|
200
|
-
if (!Number.isFinite(lo) || !Number.isFinite(hi) || lo < min || hi > max || lo > hi) {
|
|
201
|
-
throw new Error(`cron ${label} out of range [${min}..${max}] in "${f}"`)
|
|
202
|
-
}
|
|
203
|
-
for (let i = lo; i <= hi; i += step) out.add(i)
|
|
204
|
-
}
|
|
205
|
-
return [...out].sort((a, b) => a - b)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function nextCronFire(spec: CronSpec, fromMs: number): number {
|
|
209
|
-
const date = new Date(fromMs)
|
|
210
|
-
date.setUTCSeconds(0, 0)
|
|
211
|
-
date.setUTCMinutes(date.getUTCMinutes() + 1)
|
|
212
|
-
|
|
213
|
-
const maxIterations = 60 * 24 * 366 * 5
|
|
214
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
215
|
-
if (
|
|
216
|
-
spec.minute.includes(date.getUTCMinutes()) &&
|
|
217
|
-
spec.hour.includes(date.getUTCHours()) &&
|
|
218
|
-
spec.day.includes(date.getUTCDate()) &&
|
|
219
|
-
spec.month.includes(date.getUTCMonth() + 1) &&
|
|
220
|
-
spec.dow.includes(date.getUTCDay())
|
|
221
|
-
) {
|
|
222
|
-
return date.getTime()
|
|
223
|
-
}
|
|
224
|
-
date.setUTCMinutes(date.getUTCMinutes() + 1)
|
|
225
|
-
}
|
|
226
|
-
throw new Error("cron search exceeded 5 years without finding a match")
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function toMs(duration: Duration): number {
|
|
230
|
-
if (typeof duration === "number") return duration
|
|
231
|
-
const m = /^(\d+)(ms|s|m|h|d|w)$/.exec(duration)
|
|
232
|
-
if (!m) throw new Error(`invalid duration "${duration}"`)
|
|
233
|
-
const n = Number(m[1])
|
|
234
|
-
switch (m[2]) {
|
|
235
|
-
case "ms":
|
|
236
|
-
return n
|
|
237
|
-
case "s":
|
|
238
|
-
return n * 1_000
|
|
239
|
-
case "m":
|
|
240
|
-
return n * 60_000
|
|
241
|
-
case "h":
|
|
242
|
-
return n * 3_600_000
|
|
243
|
-
case "d":
|
|
244
|
-
return n * 86_400_000
|
|
245
|
-
case "w":
|
|
246
|
-
return n * 604_800_000
|
|
247
|
-
default:
|
|
248
|
-
throw new Error(`invalid duration "${duration}"`)
|
|
249
|
-
}
|
|
250
|
-
}
|
package/src/wakeup-store.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { findEarliestWakeAt } from "./sleep-alarm-manager.js"
|
|
|
6
6
|
export interface WakeupRecord {
|
|
7
7
|
runId: string
|
|
8
8
|
wakeAt: number
|
|
9
|
+
priority?: number
|
|
9
10
|
leaseOwner?: string
|
|
10
11
|
leaseExpiresAt?: number
|
|
11
12
|
updatedAt: number
|
|
@@ -74,7 +75,7 @@ export function createFsWakeupStore(opts: FsWakeupStoreOptions = {}): WakeupStor
|
|
|
74
75
|
},
|
|
75
76
|
|
|
76
77
|
async leaseDue({ owner, now: at = now(), leaseMs, limit = 25 }) {
|
|
77
|
-
const wakeups = await this.list()
|
|
78
|
+
const wakeups = (await this.list()).sort(compareWakeupClaimOrder)
|
|
78
79
|
const leased: WakeupRecord[] = []
|
|
79
80
|
for (const wakeup of wakeups) {
|
|
80
81
|
if (leased.length >= limit) break
|
|
@@ -119,11 +120,18 @@ export async function syncWakeupFromRecord(store: WakeupStore, record: RunRecord
|
|
|
119
120
|
await store.upsert({
|
|
120
121
|
runId: record.id,
|
|
121
122
|
wakeAt,
|
|
123
|
+
priority: record.priority,
|
|
122
124
|
leaseOwner: undefined,
|
|
123
125
|
leaseExpiresAt: undefined,
|
|
124
126
|
})
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
function compareWakeupClaimOrder(a: WakeupRecord, b: WakeupRecord): number {
|
|
130
|
+
const priorityDelta = (b.priority ?? 0) - (a.priority ?? 0)
|
|
131
|
+
if (priorityDelta !== 0) return priorityDelta
|
|
132
|
+
return a.wakeAt - b.wakeAt
|
|
133
|
+
}
|
|
134
|
+
|
|
127
135
|
async function safeReaddir(dir: string): Promise<string[]> {
|
|
128
136
|
try {
|
|
129
137
|
const entries = await readdir(dir)
|