@wrenautomation/aerie 0.8.5 → 0.8.6
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/bin.js +157 -10
- package/dist/bin.js.map +4 -4
- package/dist/cli-run.d.ts +2 -0
- package/dist/cli-run.js +35 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/coord/audit.d.ts +45 -0
- package/dist/coord/audit.js +64 -0
- package/dist/coord/audit.js.map +1 -0
- package/dist/coord/port.d.ts +19 -0
- package/dist/coord/postgres.d.ts +5 -1
- package/dist/coord/postgres.js +30 -8
- package/dist/coord/postgres.js.map +1 -1
- package/dist/peers-coord.js +66 -10
- package/dist/peers-coord.js.map +2 -2
- package/dist/remote.js +6 -0
- package/dist/remote.js.map +1 -1
- package/dist/schema.d.ts +181 -0
- package/dist/schema.js +21 -0
- package/dist/schema.js.map +1 -1
- package/drizzle/0013_coord_lease_event.sql +16 -0
- package/drizzle/meta/0013_snapshot.json +2049 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -30266,7 +30266,7 @@ function oneOf(name, col, values) {
|
|
|
30266
30266
|
const list2 = values.map((v) => `'${v.replaceAll("'", "''")}'`).join(", ");
|
|
30267
30267
|
return check2(name, sql.raw(`"${col.name}" IN (${list2})`));
|
|
30268
30268
|
}
|
|
30269
|
-
var aerie, ACTOR_KINDS, AREA_ROLES, TASK_ROLES, LAYOUTS, PLACES, at, area, actor, areaRole, task, taskArea, taskActor, taskHolder, view, tsvector, taskEvent, milestone, mirrorFile, setting, coordGen, coordLease, coordClosing, coordTask, coordQueue, decision, routeRule, intake2, remoteCall;
|
|
30269
|
+
var aerie, ACTOR_KINDS, AREA_ROLES, TASK_ROLES, LAYOUTS, PLACES, at, area, actor, areaRole, task, taskArea, taskActor, taskHolder, view, tsvector, taskEvent, milestone, mirrorFile, setting, coordGen, coordLease, coordClosing, coordTask, coordLeaseEvent, coordQueue, decision, routeRule, intake2, remoteCall;
|
|
30270
30270
|
var init_schema2 = __esm({
|
|
30271
30271
|
"src/schema.ts"() {
|
|
30272
30272
|
"use strict";
|
|
@@ -30565,6 +30565,27 @@ var init_schema2 = __esm({
|
|
|
30565
30565
|
},
|
|
30566
30566
|
(t) => [primaryKey({ columns: [t.ns, t.taskId], name: "pk_coord_task" })]
|
|
30567
30567
|
);
|
|
30568
|
+
coordLeaseEvent = aerie.table(
|
|
30569
|
+
"coord_lease_event",
|
|
30570
|
+
{
|
|
30571
|
+
id: bigserial({ mode: "number" }).notNull(),
|
|
30572
|
+
ns: text2().notNull(),
|
|
30573
|
+
taskId: text2("task_id").notNull(),
|
|
30574
|
+
holder: text2().notNull(),
|
|
30575
|
+
gen: bigint4({ mode: "number" }).notNull(),
|
|
30576
|
+
/** claim | take (from the queue) | reclaim | release | done | expire (freed by `by`) | lost (lapsed, seen at renew) | reap (lapsed run lane) */
|
|
30577
|
+
kind: text2().notNull(),
|
|
30578
|
+
queued: boolean4().notNull(),
|
|
30579
|
+
expires: at("expires").notNull(),
|
|
30580
|
+
by: text2().notNull(),
|
|
30581
|
+
at: at("at").notNull()
|
|
30582
|
+
},
|
|
30583
|
+
(t) => [
|
|
30584
|
+
primaryKey({ columns: [t.id], name: "pk_coord_lease_event" }),
|
|
30585
|
+
index("coord_lease_event_task_idx").on(t.ns, t.taskId),
|
|
30586
|
+
index("coord_lease_event_at_idx").on(t.ns, t.at)
|
|
30587
|
+
]
|
|
30588
|
+
);
|
|
30568
30589
|
coordQueue = aerie.table(
|
|
30569
30590
|
"coord_queue",
|
|
30570
30591
|
{
|
|
@@ -33024,6 +33045,7 @@ function pgCoord(o) {
|
|
|
33024
33045
|
const C = coordClosing;
|
|
33025
33046
|
const T = coordTask;
|
|
33026
33047
|
const Q = coordQueue;
|
|
33048
|
+
const E = coordLeaseEvent;
|
|
33027
33049
|
const lockQueue = (tx) => tx.execute(
|
|
33028
33050
|
sql`select pg_advisory_xact_lock(hashtextextended(${`aerie.coord:${ns}:queue`}, 0))`
|
|
33029
33051
|
);
|
|
@@ -33043,7 +33065,7 @@ function pgCoord(o) {
|
|
|
33043
33065
|
if (!hit[0] || hit[0].status === name) return;
|
|
33044
33066
|
await aerieStore({ db: tx }).edit(id, { status: name }, by);
|
|
33045
33067
|
};
|
|
33046
|
-
const lease = async (tx, id, holder, leaseMs, queued, key) => {
|
|
33068
|
+
const lease = async (tx, id, holder, leaseMs, queued, key, how = "claim") => {
|
|
33047
33069
|
const t = clock();
|
|
33048
33070
|
const others = await rows2(
|
|
33049
33071
|
tx,
|
|
@@ -33056,13 +33078,17 @@ function pgCoord(o) {
|
|
|
33056
33078
|
);
|
|
33057
33079
|
const gen = num(g?.gen);
|
|
33058
33080
|
await tx.execute(sql`
|
|
33059
|
-
insert into ${L} (ns, task_id, holder, gen, expires, queued, lane, pin, take_key)
|
|
33081
|
+
with up as (insert into ${L} (ns, task_id, holder, gen, expires, queued, lane, pin, take_key)
|
|
33060
33082
|
values (${ns}, ${id}, ${holder}, ${gen}, ${plus(t, leaseMs)}, ${!!queued}, ${queued?.lane ?? null}, ${queued?.pin ?? null}, ${key ?? null})
|
|
33061
33083
|
on conflict (ns, task_id, holder) do update set gen = excluded.gen, expires = excluded.expires,
|
|
33062
33084
|
take_key = coalesce(excluded.take_key, ${L}.take_key),
|
|
33063
33085
|
queued = excluded.queued or ${L}.queued,
|
|
33064
33086
|
lane = case when excluded.queued then excluded.lane else ${L}.lane end,
|
|
33065
|
-
pin = case when excluded.queued then excluded.pin else ${L}.pin end
|
|
33087
|
+
pin = case when excluded.queued then excluded.pin else ${L}.pin end
|
|
33088
|
+
returning task_id, holder, gen, queued, expires, (xmax = 0) as fresh)
|
|
33089
|
+
insert into ${E} (ns, task_id, holder, gen, kind, queued, expires, by, at)
|
|
33090
|
+
select ${ns}, task_id, holder, gen, case when fresh then ${how} else 'reclaim' end, queued, expires, holder, ${t}
|
|
33091
|
+
from up`);
|
|
33066
33092
|
await touch(tx, id, t);
|
|
33067
33093
|
await setStatus(tx, id, "doing", holder);
|
|
33068
33094
|
return { gen, others: others.map((r) => r.holder) };
|
|
@@ -33075,8 +33101,13 @@ function pgCoord(o) {
|
|
|
33075
33101
|
if (!has.length) return { last: true, closeNow: false, status: "ready" };
|
|
33076
33102
|
const gone = await rows2(
|
|
33077
33103
|
tx,
|
|
33078
|
-
sql`delete from ${L} where ns = ${ns} and task_id = ${id} and holder = any(${textArr(holders)})
|
|
33079
|
-
|
|
33104
|
+
sql`with gone as (delete from ${L} where ns = ${ns} and task_id = ${id} and holder = any(${textArr(holders)})
|
|
33105
|
+
returning holder, gen, queued, lane, pin, expires),
|
|
33106
|
+
logged as (insert into ${E} (ns, task_id, holder, gen, kind, queued, expires, by, at)
|
|
33107
|
+
select ${ns}, ${id}, holder, gen,
|
|
33108
|
+
case when holder <> ${x.by} then 'expire' else ${x.done ? "done" : x.why ?? "release"} end,
|
|
33109
|
+
queued, expires, ${x.by}, ${t} from gone)
|
|
33110
|
+
select holder, queued, lane, pin from gone`
|
|
33080
33111
|
);
|
|
33081
33112
|
if (x.done && gone.some((l) => l.holder === x.by))
|
|
33082
33113
|
await tx.execute(sql`insert into ${C} (ns, task_id, holder, reason, at)
|
|
@@ -33156,7 +33187,7 @@ function pgCoord(o) {
|
|
|
33156
33187
|
);
|
|
33157
33188
|
if (!still.length) continue;
|
|
33158
33189
|
lost.push({ id: l.id, holder: l.holder });
|
|
33159
|
-
await drop(tx, l.id, [l.holder], { queue: l.queued, by: l.holder });
|
|
33190
|
+
await drop(tx, l.id, [l.holder], { queue: l.queued, by: l.holder, why: "lost" });
|
|
33160
33191
|
}
|
|
33161
33192
|
}
|
|
33162
33193
|
await tx.execute(sql`update ${L} set expires = ${plus(t, leaseMs)}
|
|
@@ -33284,7 +33315,8 @@ function pgCoord(o) {
|
|
|
33284
33315
|
sql`select 1 from ${L} where ns = ${ns} and task_id = ${l.id} and holder = ${l.holder}
|
|
33285
33316
|
and expires < ${t}`
|
|
33286
33317
|
);
|
|
33287
|
-
if (still.length)
|
|
33318
|
+
if (still.length)
|
|
33319
|
+
await drop(tx, l.id, [l.holder], { queue: l.queued, by: l.holder, why: "reap" });
|
|
33288
33320
|
}
|
|
33289
33321
|
}
|
|
33290
33322
|
if (x.cap !== void 0) {
|
|
@@ -33314,10 +33346,28 @@ function pgCoord(o) {
|
|
|
33314
33346
|
holder,
|
|
33315
33347
|
leaseMs,
|
|
33316
33348
|
{ lane: item.lane, pin: item.pin },
|
|
33317
|
-
x.key
|
|
33349
|
+
x.key,
|
|
33350
|
+
"take"
|
|
33318
33351
|
);
|
|
33319
33352
|
return { id: item.id, gen: r.gen };
|
|
33320
33353
|
}),
|
|
33354
|
+
leaseEvents: async ({ since, limit = 1e4 }) => (await rows2(
|
|
33355
|
+
db,
|
|
33356
|
+
sql`select id::float8 as id, task_id, holder, gen::float8 as gen, kind, queued, ${ms(sql`expires`)} as expires,
|
|
33357
|
+
by, ${ms(sql`at`)} as at from ${E} where ns = ${ns} and at >= ${at3(since)} order by id limit ${limit}`
|
|
33358
|
+
)).map(
|
|
33359
|
+
(r) => ({
|
|
33360
|
+
id: num(r.id),
|
|
33361
|
+
task: String(r.task_id),
|
|
33362
|
+
holder: String(r.holder),
|
|
33363
|
+
gen: num(r.gen),
|
|
33364
|
+
kind: r.kind,
|
|
33365
|
+
queued: !!r.queued,
|
|
33366
|
+
expires: num(r.expires),
|
|
33367
|
+
by: String(r.by),
|
|
33368
|
+
at: num(r.at)
|
|
33369
|
+
})
|
|
33370
|
+
),
|
|
33321
33371
|
fence: async (id, holder, gen) => (await rows2(
|
|
33322
33372
|
db,
|
|
33323
33373
|
sql`select 1 from ${L} where ns = ${ns} and task_id = ${id} and holder = ${holder}
|
|
@@ -33876,7 +33926,13 @@ var init_remote = __esm({
|
|
|
33876
33926
|
HOLDER,
|
|
33877
33927
|
MS
|
|
33878
33928
|
]),
|
|
33879
|
-
fence: read([ID2, HOLDER, external_exports.int().nonnegative()])
|
|
33929
|
+
fence: read([ID2, HOLDER, external_exports.int().nonnegative()]),
|
|
33930
|
+
leaseEvents: read([
|
|
33931
|
+
external_exports.strictObject({
|
|
33932
|
+
since: external_exports.number().nonnegative(),
|
|
33933
|
+
limit: external_exports.int().positive().max(1e4).optional()
|
|
33934
|
+
})
|
|
33935
|
+
])
|
|
33880
33936
|
};
|
|
33881
33937
|
OPS = { store: STORE_OPS, coord: COORD_OPS };
|
|
33882
33938
|
CALL = external_exports.object({
|
|
@@ -40739,6 +40795,69 @@ function registerIntakeCommands(program3, ctx) {
|
|
|
40739
40795
|
);
|
|
40740
40796
|
}
|
|
40741
40797
|
|
|
40798
|
+
// src/coord/audit.ts
|
|
40799
|
+
var START = /* @__PURE__ */ new Set(["claim", "take", "reclaim"]);
|
|
40800
|
+
function leaseAudit(events, held, o) {
|
|
40801
|
+
const starts = {};
|
|
40802
|
+
const ends = {};
|
|
40803
|
+
const doubleDelivery = [];
|
|
40804
|
+
const orphans = [];
|
|
40805
|
+
const mins = (ms2) => Math.round(ms2 / 6e3) / 10;
|
|
40806
|
+
const open3 = /* @__PURE__ */ new Map();
|
|
40807
|
+
for (const e of [...events].sort((a, b) => a.id - b.id)) {
|
|
40808
|
+
const byTask = open3.get(e.task) ?? /* @__PURE__ */ new Map();
|
|
40809
|
+
open3.set(e.task, byTask);
|
|
40810
|
+
if (START.has(e.kind)) {
|
|
40811
|
+
starts[e.kind] = (starts[e.kind] ?? 0) + 1;
|
|
40812
|
+
if (!e.queued) continue;
|
|
40813
|
+
const had = byTask.get(e.holder);
|
|
40814
|
+
if (e.kind !== "reclaim" || !had) {
|
|
40815
|
+
const others = [...byTask].filter(([h]) => h !== e.holder);
|
|
40816
|
+
if (others.length)
|
|
40817
|
+
doubleDelivery.push({
|
|
40818
|
+
task: e.task,
|
|
40819
|
+
holder: e.holder,
|
|
40820
|
+
at: e.at,
|
|
40821
|
+
open: others.map(([h, l]) => ({ holder: h, since: l.since }))
|
|
40822
|
+
});
|
|
40823
|
+
}
|
|
40824
|
+
byTask.set(e.holder, { since: had?.since ?? e.at });
|
|
40825
|
+
continue;
|
|
40826
|
+
}
|
|
40827
|
+
ends[e.kind] = (ends[e.kind] ?? 0) + 1;
|
|
40828
|
+
byTask.delete(e.holder);
|
|
40829
|
+
if ((e.kind === "expire" || e.kind === "reap") && e.at - e.expires > o.graceMs)
|
|
40830
|
+
orphans.push({
|
|
40831
|
+
task: e.task,
|
|
40832
|
+
holder: e.holder,
|
|
40833
|
+
expires: e.expires,
|
|
40834
|
+
lapsedMin: mins(e.at - e.expires),
|
|
40835
|
+
freed: { kind: e.kind, by: e.by, at: e.at }
|
|
40836
|
+
});
|
|
40837
|
+
}
|
|
40838
|
+
for (const t of held)
|
|
40839
|
+
for (const l of t.holders)
|
|
40840
|
+
if (o.now - l.expires > o.graceMs)
|
|
40841
|
+
orphans.push({
|
|
40842
|
+
task: t.id,
|
|
40843
|
+
holder: l.holder,
|
|
40844
|
+
expires: l.expires,
|
|
40845
|
+
lapsedMin: mins(o.now - l.expires),
|
|
40846
|
+
freed: null
|
|
40847
|
+
});
|
|
40848
|
+
return {
|
|
40849
|
+
since: o.since,
|
|
40850
|
+
until: o.now,
|
|
40851
|
+
graceMin: mins(o.graceMs),
|
|
40852
|
+
events: events.length,
|
|
40853
|
+
starts,
|
|
40854
|
+
ends,
|
|
40855
|
+
doubleDelivery,
|
|
40856
|
+
orphans,
|
|
40857
|
+
ok: !doubleDelivery.length && !orphans.length
|
|
40858
|
+
};
|
|
40859
|
+
}
|
|
40860
|
+
|
|
40742
40861
|
// src/cli-run.ts
|
|
40743
40862
|
init_core3();
|
|
40744
40863
|
|
|
@@ -41496,6 +41615,13 @@ async function work(o) {
|
|
|
41496
41615
|
|
|
41497
41616
|
// src/cli-run.ts
|
|
41498
41617
|
var each = (v, prev = []) => [...prev, v];
|
|
41618
|
+
function sinceOf(v, now) {
|
|
41619
|
+
const m = /^(\d+(?:\.\d+)?)([dh])$/.exec(v);
|
|
41620
|
+
if (m) return now - Number(m[1]) * (m[2] === "d" ? 864e5 : 36e5);
|
|
41621
|
+
const t = Date.parse(v);
|
|
41622
|
+
if (Number.isNaN(t)) throw new Error(`aerie: --since ${v}: give an ISO time or Nd / Nh`);
|
|
41623
|
+
return t;
|
|
41624
|
+
}
|
|
41499
41625
|
var print2 = (x) => void process.stdout.write(typeof x === "string" ? x : `${JSON.stringify(x, null, 2)}
|
|
41500
41626
|
`);
|
|
41501
41627
|
async function pickerOf(a) {
|
|
@@ -41727,6 +41853,27 @@ function registerRunCommands(program3, ctx) {
|
|
|
41727
41853
|
` : "No pool sessions.\n"
|
|
41728
41854
|
);
|
|
41729
41855
|
});
|
|
41856
|
+
program3.command("coord").description("the coord store").command("audit").description("double delivery and orphan holders from the lease log (WA-1364.3 AC #5)").option("--since <when>", "ISO time, or Nd / Nh back", "3d").option("--grace <min>", "minutes past expiry before a lease counts as orphaned", "60").option("--json", "JSON out").action(
|
|
41857
|
+
(o) => board(async (_a3, c) => {
|
|
41858
|
+
if (!c.leaseEvents) throw new Error(`aerie: coord store ${c.name} keeps no lease log`);
|
|
41859
|
+
const now = Date.now();
|
|
41860
|
+
const since = sinceOf(String(o.since), now);
|
|
41861
|
+
const [events, held] = await Promise.all([c.leaseEvents({ since }), c.held()]);
|
|
41862
|
+
const r = leaseAudit(events, held, { since, now, graceMs: Number(o.grace) * 6e4 });
|
|
41863
|
+
print2(
|
|
41864
|
+
o.json ? r : `${r.ok ? "ok" : "FAIL"}: ${r.events} lease events since ${new Date(since).toISOString()}; starts ${JSON.stringify(r.starts)}, ends ${JSON.stringify(r.ends)}; ${r.doubleDelivery.length} double deliveries, ${r.orphans.length} orphans
|
|
41865
|
+
${[
|
|
41866
|
+
...r.doubleDelivery.map(
|
|
41867
|
+
(d) => `double ${d.task} ${d.holder} at ${new Date(d.at).toISOString()}; open: ${d.open.map((x) => x.holder).join(", ")}`
|
|
41868
|
+
),
|
|
41869
|
+
...r.orphans.map(
|
|
41870
|
+
(x) => `orphan ${x.task} ${x.holder} ${x.lapsedMin} min past expiry${x.freed ? `, ${x.freed.kind} by ${x.freed.by}` : ", still held"}`
|
|
41871
|
+
)
|
|
41872
|
+
].map((l) => `${l}
|
|
41873
|
+
`).join("")}`
|
|
41874
|
+
);
|
|
41875
|
+
})
|
|
41876
|
+
);
|
|
41730
41877
|
program3.command("events").description("the event log across tasks").command("search").argument("<words>", 'web-search words: "a phrase", -not, or').option("--task <id>", "one task").option("--kind <kind>", "one kind, e.g. run.tool or routed").option("--limit <n>", "at most n", "50").option("--json", "JSON out").action(
|
|
41731
41878
|
(words, o) => ctx.withStore(async (a) => {
|
|
41732
41879
|
const rs = await a.searchEvents(words, {
|