@usine/engine 0.3.0 → 0.5.0
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/LICENSE +661 -21
- package/README.md +112 -38
- package/dist/board-moves/board-moves.d.ts +20 -0
- package/dist/board-moves/board-moves.d.ts.map +1 -0
- package/dist/board-moves/board-moves.js +74 -0
- package/dist/board-moves/board-moves.js.map +1 -0
- package/dist/board-moves/model.d.ts +21 -0
- package/dist/board-moves/model.d.ts.map +1 -0
- package/dist/board-moves/model.js +22 -0
- package/dist/board-moves/model.js.map +1 -0
- package/dist/cursor/cursor.d.ts +2 -6
- package/dist/cursor/cursor.d.ts.map +1 -1
- package/dist/cursor/cursor.js +2 -6
- package/dist/cursor/cursor.js.map +1 -1
- package/dist/cycle-budget/cycle-budget.d.ts +14 -8
- package/dist/cycle-budget/cycle-budget.d.ts.map +1 -1
- package/dist/cycle-budget/cycle-budget.js +51 -10
- package/dist/cycle-budget/cycle-budget.js.map +1 -1
- package/dist/cycle-budget/model.d.ts +6 -2
- package/dist/cycle-budget/model.d.ts.map +1 -1
- package/dist/cycle-budget/model.js +5 -2
- package/dist/cycle-budget/model.js.map +1 -1
- package/dist/cycle-budget/queries.d.ts +1 -1
- package/dist/cycle-budget/queries.d.ts.map +1 -1
- package/dist/cycle-budget/queries.js +3 -3
- package/dist/cycle-budget/queries.js.map +1 -1
- package/dist/database/database.d.ts +1 -1
- package/dist/database/database.d.ts.map +1 -1
- package/dist/database/local-sqlite.d.ts +13 -10
- package/dist/database/local-sqlite.d.ts.map +1 -1
- package/dist/database/local-sqlite.js +22 -17
- package/dist/database/local-sqlite.js.map +1 -1
- package/dist/database/schema.d.ts +668 -36
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +123 -23
- package/dist/database/schema.js.map +1 -1
- package/dist/{workflow → engine}/admission.d.ts +2 -2
- package/dist/engine/admission.d.ts.map +1 -0
- package/dist/{workflow → engine}/admission.js +4 -4
- package/dist/engine/admission.js.map +1 -0
- package/dist/engine/checkout-recovery.d.ts +79 -0
- package/dist/engine/checkout-recovery.d.ts.map +1 -0
- package/dist/engine/checkout-recovery.js +238 -0
- package/dist/engine/checkout-recovery.js.map +1 -0
- package/dist/engine/engine.d.ts +67 -26
- package/dist/engine/engine.d.ts.map +1 -1
- package/dist/engine/engine.js +136 -129
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/error.d.ts +29 -10
- package/dist/engine/error.d.ts.map +1 -1
- package/dist/engine/error.js +28 -8
- package/dist/engine/error.js.map +1 -1
- package/dist/engine/lifecycle.d.ts +83 -28
- package/dist/engine/lifecycle.d.ts.map +1 -1
- package/dist/engine/lifecycle.js +94 -45
- package/dist/engine/lifecycle.js.map +1 -1
- package/dist/engine/retry.d.ts +16 -0
- package/dist/engine/retry.d.ts.map +1 -0
- package/dist/engine/retry.js +18 -0
- package/dist/engine/retry.js.map +1 -0
- package/dist/engine/run-again.d.ts +45 -0
- package/dist/engine/run-again.d.ts.map +1 -0
- package/dist/engine/run-again.js +56 -0
- package/dist/engine/run-again.js.map +1 -0
- package/dist/engine/run-driver.d.ts +68 -0
- package/dist/engine/run-driver.d.ts.map +1 -0
- package/dist/engine/run-driver.js +448 -0
- package/dist/engine/run-driver.js.map +1 -0
- package/dist/engine/shutdown.d.ts +3 -4
- package/dist/engine/shutdown.d.ts.map +1 -1
- package/dist/engine/shutdown.js +3 -4
- package/dist/engine/shutdown.js.map +1 -1
- package/dist/engine/submission.d.ts +31 -11
- package/dist/engine/submission.d.ts.map +1 -1
- package/dist/engine/submission.js +43 -12
- package/dist/engine/submission.js.map +1 -1
- package/dist/engine/unconfirmed-stops.d.ts +27 -0
- package/dist/engine/unconfirmed-stops.d.ts.map +1 -0
- package/dist/engine/unconfirmed-stops.js +26 -0
- package/dist/engine/unconfirmed-stops.js.map +1 -0
- package/dist/item-projection/item-projection.d.ts +14 -4
- package/dist/item-projection/item-projection.d.ts.map +1 -1
- package/dist/item-projection/item-projection.js +60 -14
- package/dist/item-projection/item-projection.js.map +1 -1
- package/dist/item-projection/model.d.ts +5 -1
- package/dist/item-projection/model.d.ts.map +1 -1
- package/dist/item-projection/model.js +6 -2
- package/dist/item-projection/model.js.map +1 -1
- package/dist/loop-control/loop-control.d.ts +5 -5
- package/dist/loop-control/loop-control.d.ts.map +1 -1
- package/dist/loop-control/loop-control.js +4 -4
- package/dist/loop-control/loop-control.js.map +1 -1
- package/dist/loop-control/model.d.ts +1 -1
- package/dist/loop-control/model.d.ts.map +1 -1
- package/dist/loop-control/model.js +1 -1
- package/dist/loop-control/model.js.map +1 -1
- package/dist/repository-label-projection/model.d.ts +2 -2
- package/dist/repository-label-projection/model.d.ts.map +1 -1
- package/dist/repository-label-projection/model.js +2 -2
- package/dist/repository-label-projection/model.js.map +1 -1
- package/dist/repository-label-projection/repository-label-projection.d.ts +1 -1
- package/dist/repository-label-projection/repository-label-projection.d.ts.map +1 -1
- package/dist/repository-label-projection/repository-label-projection.js +1 -1
- package/dist/repository-label-projection/repository-label-projection.js.map +1 -1
- package/dist/run-reader/model.d.ts +62 -4
- package/dist/run-reader/model.d.ts.map +1 -1
- package/dist/run-reader/model.js +66 -7
- package/dist/run-reader/model.js.map +1 -1
- package/dist/run-reader/run-reader.d.ts +25 -10
- package/dist/run-reader/run-reader.d.ts.map +1 -1
- package/dist/run-reader/run-reader.js +25 -7
- package/dist/run-reader/run-reader.js.map +1 -1
- package/dist/step-executor.d.ts +86 -13
- package/dist/step-executor.d.ts.map +1 -1
- package/dist/step-executor.js +10 -2
- package/dist/step-executor.js.map +1 -1
- package/dist/store/attempt-windows.d.ts +8 -0
- package/dist/store/attempt-windows.d.ts.map +1 -0
- package/dist/store/attempt-windows.js +53 -0
- package/dist/store/attempt-windows.js.map +1 -0
- package/dist/store/codec.d.ts +242 -43
- package/dist/store/codec.d.ts.map +1 -1
- package/dist/store/codec.js +61 -16
- package/dist/store/codec.js.map +1 -1
- package/dist/store/leftovers.d.ts +10 -0
- package/dist/store/leftovers.d.ts.map +1 -0
- package/dist/store/leftovers.js +43 -0
- package/dist/store/leftovers.js.map +1 -0
- package/dist/store/loop-items.d.ts +16 -0
- package/dist/store/loop-items.d.ts.map +1 -0
- package/dist/store/loop-items.js +185 -0
- package/dist/store/loop-items.js.map +1 -0
- package/dist/store/queries.d.ts +385 -36
- package/dist/store/queries.d.ts.map +1 -1
- package/dist/store/queries.js +355 -107
- package/dist/store/queries.js.map +1 -1
- package/dist/store/store.d.ts +106 -54
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +497 -190
- package/dist/store/store.js.map +1 -1
- package/dist/store/transcript.d.ts +3 -3
- package/dist/store/transcript.d.ts.map +1 -1
- package/dist/store/transcript.js +25 -6
- package/dist/store/transcript.js.map +1 -1
- package/dist/transcript.d.ts +19 -20
- package/dist/transcript.d.ts.map +1 -1
- package/dist/transcript.js +23 -21
- package/dist/transcript.js.map +1 -1
- package/dist/trigger-condition.d.ts +23 -0
- package/dist/trigger-condition.d.ts.map +1 -0
- package/dist/trigger-condition.js +8 -0
- package/dist/trigger-condition.js.map +1 -0
- package/drizzle/20260917071139_resolve_run_failure/migration.sql +1 -0
- package/drizzle/20260917071139_resolve_run_failure/snapshot.json +1471 -0
- package/drizzle/20260918032643_attempt_salvage/migration.sql +1 -0
- package/drizzle/20260918032643_attempt_salvage/snapshot.json +1481 -0
- package/drizzle/20261011153856_drop_workflow_tables/migration.sql +4 -0
- package/drizzle/20261011153856_drop_workflow_tables/snapshot.json +1433 -0
- package/drizzle/20261017155043_attempt_salvage_check/migration.sql +41 -0
- package/drizzle/20261017155043_attempt_salvage_check/snapshot.json +1421 -0
- package/drizzle/20261018162700_discard_attempt_leftovers/migration.sql +1 -0
- package/drizzle/20261018162700_discard_attempt_leftovers/snapshot.json +1431 -0
- package/drizzle/20261018172605_loop_items_and_reopenings/migration.sql +15 -0
- package/drizzle/20261018172605_loop_items_and_reopenings/snapshot.json +1574 -0
- package/drizzle/20261018180339_attempt_ended_at/migration.sql +1 -0
- package/drizzle/20261018180339_attempt_ended_at/snapshot.json +1584 -0
- package/drizzle/20261019104043_run_approval_source/migration.sql +15 -0
- package/drizzle/20261019104043_run_approval_source/snapshot.json +1610 -0
- package/drizzle/20261019133538_board_moves/migration.sql +14 -0
- package/drizzle/20261019133538_board_moves/snapshot.json +1731 -0
- package/drizzle/20261019170538_item_projection_gone/migration.sql +1 -0
- package/drizzle/20261019170538_item_projection_gone/snapshot.json +1741 -0
- package/drizzle/20261019180649_run_again_events/migration.sql +7 -0
- package/drizzle/20261019180649_run_again_events/snapshot.json +1804 -0
- package/drizzle/20261019185008_trigger_event_permissions/migration.sql +1 -0
- package/drizzle/20261019185008_trigger_event_permissions/snapshot.json +1814 -0
- package/drizzle/20261020103746_attempt_start/migration.sql +5 -0
- package/drizzle/20261020103746_attempt_start/snapshot.json +1844 -0
- package/drizzle/20261020125211_attempt_comments/migration.sql +1 -0
- package/drizzle/20261020125211_attempt_comments/snapshot.json +1854 -0
- package/drizzle/20261020191320_run_closed_at/migration.sql +6 -0
- package/drizzle/20261020191320_run_closed_at/snapshot.json +1864 -0
- package/drizzle/20261023091428_rename_loop_item_hold_to_suppression/migration.sql +2 -0
- package/drizzle/20261023091428_rename_loop_item_hold_to_suppression/snapshot.json +1867 -0
- package/drizzle/20261024162954_cycle_ask_closures/migration.sql +10 -0
- package/drizzle/20261024162954_cycle_ask_closures/snapshot.json +1948 -0
- package/drizzle/20261027104206_attempt_out_of_memory/migration.sql +1 -0
- package/drizzle/20261027104206_attempt_out_of_memory/snapshot.json +1958 -0
- package/drizzle/20261027191944_failure_resolution/migration.sql +1 -0
- package/drizzle/20261027191944_failure_resolution/snapshot.json +1968 -0
- package/package.json +22 -12
- package/dist/workflow/admission.d.ts.map +0 -1
- package/dist/workflow/admission.js.map +0 -1
- package/dist/workflow/command.d.ts +0 -13
- package/dist/workflow/command.d.ts.map +0 -1
- package/dist/workflow/command.js +0 -17
- package/dist/workflow/command.js.map +0 -1
- package/dist/workflow/workflow.d.ts +0 -18
- package/dist/workflow/workflow.d.ts.map +0 -1
- package/dist/workflow/workflow.js +0 -108
- package/dist/workflow/workflow.js.map +0 -1
package/dist/store/queries.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
2
|
+
import { WorkerName } from "@usine/core/worker";
|
|
3
|
+
import { Attempt, AttemptId, AttemptContext, AttemptNumber, totalOutOfMemoryKills, } from "@usine/core/attempt";
|
|
2
4
|
import { AttemptOutcome } from "@usine/core/attempt/outcome";
|
|
5
|
+
import { AttemptSalvage, salvageRefFor } from "@usine/core/attempt/salvage";
|
|
3
6
|
import { Event, DedupKey, EventId } from "@usine/core/event";
|
|
4
7
|
import { ItemKey } from "@usine/core/item";
|
|
5
|
-
import { LoopName } from "@usine/core/loop";
|
|
8
|
+
import { LoopName, LoopPermissions } from "@usine/core/loop";
|
|
6
9
|
import { RunId } from "@usine/core/run";
|
|
7
10
|
import { StepName } from "@usine/core/step";
|
|
8
11
|
import * as RunState from "@usine/core/run/state";
|
|
9
12
|
import { StepResult } from "@usine/core/step/result";
|
|
10
|
-
import { and, asc, desc, eq, inArray, isNull, ne, notInArray, sql } from "drizzle-orm";
|
|
13
|
+
import { and, asc, desc, eq, gte, inArray, isNull, ne, notInArray, sql, } from "drizzle-orm";
|
|
11
14
|
import { alias } from "drizzle-orm/sqlite-core";
|
|
12
15
|
import { Array as EffectArray, DateTime, Effect, Option, Schema } from "effect";
|
|
13
|
-
import { approvals, attempts, events, ignoredEvents, projectionRevision, runs, steps, triggerEvents,
|
|
16
|
+
import { approvals, attempts, events, ignoredEvents, itemCycleBudgets, loopItems, projectionRevision, runAgainEvents, runs, steps, triggerEvents, } from "../database/schema.js";
|
|
14
17
|
import { RunNotFoundError } from "../engine/error.js";
|
|
15
18
|
import { RunSnapshot, RunSummary } from "../run-reader/model.js";
|
|
16
19
|
import { Submission } from "../engine/submission.js";
|
|
17
|
-
import { WORKFLOW_COMMAND_PRIORITY } from "../workflow/command.js";
|
|
18
20
|
import { cycleQueries } from "../cycle-budget/queries.js";
|
|
19
|
-
import { attemptFromRow, decodeAttemptRows, decodeDiscards,
|
|
21
|
+
import { approvalFromRow, attemptFromRow, decodeAttemptRows, decodeDiscards, decodeNextStarts, decodeOutOfMemory, decodeEventRoutes, decodeProjectionRevision, decodeReopenedEvents, decodeReopenings, decodeRunEventRows, decodeRunIds, decodeRunRows, encodeAgent, encodeAttemptStart, encodeFailureResolution, encodeBrainSpec, encodeDateTime, encodeOutcome, encodeComments, encodeOutOfMemory, encodeSalvage, encodePayload, encodeRunState, encodeStepResult, eventFromRow, runAgainFromRow, runEventFromRow, runFromRow, runSummaryFromRow, stepFromRow, submissionFromRow, } from "./codec.js";
|
|
22
|
+
import { loopItemQueries } from "./loop-items.js";
|
|
20
23
|
import { transcriptQueries } from "./transcript.js";
|
|
21
24
|
const newestRunFirst = (left, right) => DateTime.toEpochMillis(right.openedAt) - DateTime.toEpochMillis(left.openedAt) ||
|
|
22
25
|
right.runId.localeCompare(left.runId);
|
|
@@ -29,6 +32,7 @@ const eventRouteSelection = {
|
|
|
29
32
|
loop: triggerEvents.loopName,
|
|
30
33
|
step: triggerEvents.stepName,
|
|
31
34
|
approval: triggerEvents.approval,
|
|
35
|
+
permissions: triggerEvents.permissions,
|
|
32
36
|
cycleLimit: triggerEvents.cycleLimit,
|
|
33
37
|
cycleGeneration: triggerEvents.cycleGeneration,
|
|
34
38
|
repository: triggerEvents.repository,
|
|
@@ -37,13 +41,16 @@ const eventRouteSelection = {
|
|
|
37
41
|
agent: triggerEvents.agentJson,
|
|
38
42
|
parentModel: triggerEvents.parentModel,
|
|
39
43
|
};
|
|
44
|
+
const approvalEvents = alias(events, "approval_event");
|
|
40
45
|
const runSelection = {
|
|
41
46
|
itemKey: triggerEvents.itemKey,
|
|
42
47
|
runId: runs.runId,
|
|
43
48
|
eventId: runs.eventId,
|
|
44
49
|
loop: runs.loopName,
|
|
45
50
|
state: runs.stateJson,
|
|
51
|
+
resolvedFailureAttemptId: runs.resolvedFailureAttemptId,
|
|
46
52
|
openedAt: runs.openedAt,
|
|
53
|
+
closedAt: runs.closedAt,
|
|
47
54
|
repository: runs.repository,
|
|
48
55
|
revision: runs.revision,
|
|
49
56
|
task: runs.task,
|
|
@@ -51,8 +58,13 @@ const runSelection = {
|
|
|
51
58
|
parentModel: runs.parentModel,
|
|
52
59
|
stepName: steps.stepName,
|
|
53
60
|
result: steps.result,
|
|
61
|
+
approvedAt: approvalEvents.receivedAt,
|
|
62
|
+
approvalChannel: approvals.channel,
|
|
63
|
+
approver: approvals.approver,
|
|
64
|
+
againFrom: runAgainEvents.fromRunId,
|
|
65
|
+
againRequestedBy: runAgainEvents.requestedBy,
|
|
54
66
|
};
|
|
55
|
-
const attemptSelection = {
|
|
67
|
+
export const attemptSelection = {
|
|
56
68
|
attemptId: attempts.attemptId,
|
|
57
69
|
runId: attempts.runId,
|
|
58
70
|
stepName: attempts.stepName,
|
|
@@ -65,8 +77,14 @@ const attemptSelection = {
|
|
|
65
77
|
parentModel: attempts.parentModel,
|
|
66
78
|
startedAt: attempts.startedAt,
|
|
67
79
|
outcome: attempts.outcomeJson,
|
|
80
|
+
endedAt: attempts.endedAt,
|
|
81
|
+
salvage: attempts.salvageJson,
|
|
82
|
+
start: attempts.startJson,
|
|
83
|
+
failureResolution: attempts.failureResolutionJson,
|
|
84
|
+
reattachedAt: attempts.reattachedAt,
|
|
85
|
+
comments: attempts.commentsJson,
|
|
86
|
+
outOfMemory: attempts.outOfMemoryJson,
|
|
68
87
|
};
|
|
69
|
-
// The state partitions are owned by @usine/core; these predicates only borrow their tags.
|
|
70
88
|
const storedStateTag = sql `json_extract(${runs.stateJson}, '$._tag')`;
|
|
71
89
|
const openState = inArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]);
|
|
72
90
|
const unstartedState = inArray(storedStateTag, [...RunState.UNSTARTED_STATE_TAGS]);
|
|
@@ -74,8 +92,7 @@ const unstartedState = inArray(storedStateTag, [...RunState.UNSTARTED_STATE_TAGS
|
|
|
74
92
|
// cannot emit the declaration (TS2883).
|
|
75
93
|
const storedAgent = (agent) => Schema.decodeUnknownSync(Schema.Json)(encodeAgent(agent));
|
|
76
94
|
export const queriesFor = (database) => {
|
|
77
|
-
//
|
|
78
|
-
// Workflow suspension before its final mutation is observable (ADR 0020).
|
|
95
|
+
// `.execute()` stays explicit (ADR 0020).
|
|
79
96
|
const eventRouteByDedup = Effect.fn("RunStoreQueries.eventRouteByDedup")(function* (dedupKey) {
|
|
80
97
|
const rows = yield* database
|
|
81
98
|
.select(eventRouteSelection)
|
|
@@ -95,15 +112,64 @@ export const queriesFor = (database) => {
|
|
|
95
112
|
.execute();
|
|
96
113
|
return yield* decodeEventRoutes(rows);
|
|
97
114
|
});
|
|
115
|
+
const selectRuns = () => database
|
|
116
|
+
.select(runSelection)
|
|
117
|
+
.from(runs)
|
|
118
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
119
|
+
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
120
|
+
.leftJoin(approvals, eq(approvals.runId, runs.runId))
|
|
121
|
+
.leftJoin(approvalEvents, eq(approvalEvents.eventId, approvals.eventId))
|
|
122
|
+
.leftJoin(runAgainEvents, eq(runAgainEvents.eventId, runs.eventId));
|
|
98
123
|
const runRows = Effect.fn("RunStoreQueries.runRows")(function* (runId) {
|
|
124
|
+
const rows = yield* selectRuns().where(eq(runs.runId, runId)).execute();
|
|
125
|
+
return yield* decodeRunRows(rows);
|
|
126
|
+
});
|
|
127
|
+
/** Returns the Attempts that match `filters` and whose leftovers weren't discarded. */
|
|
128
|
+
const recoveries = (...filters) => Effect.gen(function* () {
|
|
99
129
|
const rows = yield* database
|
|
100
|
-
.select(
|
|
101
|
-
.
|
|
130
|
+
.select({
|
|
131
|
+
attemptId: attempts.attemptId,
|
|
132
|
+
worker: attempts.workerName,
|
|
133
|
+
repository: runs.repository,
|
|
134
|
+
runId: attempts.runId,
|
|
135
|
+
loop: triggerEvents.loopName,
|
|
136
|
+
itemKey: triggerEvents.itemKey,
|
|
137
|
+
number: attempts.attemptNumber,
|
|
138
|
+
permissions: triggerEvents.permissions,
|
|
139
|
+
outcome: sql `coalesce(json_extract(${attempts.outcomeJson}, '$._tag'), '')`,
|
|
140
|
+
salvage: attempts.salvageJson,
|
|
141
|
+
})
|
|
142
|
+
.from(attempts)
|
|
143
|
+
.innerJoin(runs, eq(runs.runId, attempts.runId))
|
|
102
144
|
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
103
|
-
.
|
|
104
|
-
.where(eq(runs.runId, runId))
|
|
145
|
+
.where(and(isNull(attempts.leftoversDiscardedAt), ...filters))
|
|
105
146
|
.execute();
|
|
106
|
-
|
|
147
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({
|
|
148
|
+
attemptId: AttemptId,
|
|
149
|
+
worker: WorkerName,
|
|
150
|
+
repository: RepositoryName,
|
|
151
|
+
runId: RunId,
|
|
152
|
+
loop: LoopName,
|
|
153
|
+
itemKey: ItemKey,
|
|
154
|
+
number: AttemptNumber,
|
|
155
|
+
permissions: LoopPermissions,
|
|
156
|
+
outcome: Schema.String,
|
|
157
|
+
salvage: Schema.NullOr(AttemptSalvage),
|
|
158
|
+
})))(rows);
|
|
159
|
+
return decoded.map(({ number, outcome, salvage, ...attempt }) => ({
|
|
160
|
+
...attempt,
|
|
161
|
+
ref: salvageRefFor(attempt.runId, number),
|
|
162
|
+
succeeded: outcome === "Succeeded",
|
|
163
|
+
open: outcome === "",
|
|
164
|
+
rejected: salvage?._tag === "Rejected" ? Option.some(salvage) : Option.none(),
|
|
165
|
+
}));
|
|
166
|
+
});
|
|
167
|
+
const salvageCandidates = Effect.fn("RunStoreQueries.salvageCandidates")(function* () {
|
|
168
|
+
return yield* recoveries();
|
|
169
|
+
});
|
|
170
|
+
const salvageCandidate = Effect.fn("RunStoreQueries.salvageCandidate")(function* (attemptId) {
|
|
171
|
+
yield* Effect.annotateCurrentSpan({ attemptId });
|
|
172
|
+
return EffectArray.head(yield* recoveries(eq(attempts.attemptId, attemptId)));
|
|
107
173
|
});
|
|
108
174
|
const attemptRows = Effect.fn("RunStoreQueries.attemptRows")(function* (runId) {
|
|
109
175
|
const rows = yield* database
|
|
@@ -131,17 +197,21 @@ export const queriesFor = (database) => {
|
|
|
131
197
|
run: runFromRow(storedRun),
|
|
132
198
|
step: stepFromRow(storedRun),
|
|
133
199
|
attempts: storedAttempts.map(attemptFromRow),
|
|
200
|
+
resolvedFailureAttemptId: Option.fromNullishOr(storedRun.resolvedFailureAttemptId),
|
|
201
|
+
closedAt: Option.fromNullishOr(storedRun.closedAt),
|
|
202
|
+
approval: approvalFromRow(storedRun),
|
|
203
|
+
runAgain: runAgainFromRow(storedRun),
|
|
134
204
|
repository: storedRun.repository,
|
|
135
205
|
revision: storedRun.revision,
|
|
136
206
|
task: storedRun.task,
|
|
137
207
|
agent: storedRun.agent,
|
|
138
208
|
parentModel: Option.fromNullishOr(storedRun.parentModel),
|
|
209
|
+
permissions: route.permissions,
|
|
139
210
|
});
|
|
140
211
|
});
|
|
141
212
|
const summariesWithAttempts = Effect.fn("RunStoreQueries.summariesWithAttempts")(function* (storedRuns) {
|
|
142
213
|
const runIds = storedRuns.map(({ runId }) => runId);
|
|
143
|
-
//
|
|
144
|
-
// long-lived Task with many Runs.
|
|
214
|
+
// Chunks keep each read below the driver's bind-parameter limit.
|
|
145
215
|
const storedAttempts = (yield* Effect.forEach(EffectArray.chunksOf(runIds, 100), (ids) => database
|
|
146
216
|
.select(attemptSelection)
|
|
147
217
|
.from(attempts)
|
|
@@ -155,11 +225,7 @@ export const queriesFor = (database) => {
|
|
|
155
225
|
const itemRunSummaries = Effect.fn("RunStoreQueries.itemRunSummaries")(function* (itemKeys) {
|
|
156
226
|
if (itemKeys.length === 0)
|
|
157
227
|
return [];
|
|
158
|
-
const storedRuns = (yield* Effect.forEach(EffectArray.chunksOf([...new Set(itemKeys)], 100), (keys) =>
|
|
159
|
-
.select(runSelection)
|
|
160
|
-
.from(runs)
|
|
161
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
162
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
228
|
+
const storedRuns = (yield* Effect.forEach(EffectArray.chunksOf([...new Set(itemKeys)], 100), (keys) => selectRuns()
|
|
163
229
|
.where(inArray(triggerEvents.itemKey, keys))
|
|
164
230
|
.execute()
|
|
165
231
|
.pipe(Effect.flatMap(decodeRunRows))))
|
|
@@ -167,24 +233,15 @@ export const queriesFor = (database) => {
|
|
|
167
233
|
.sort(newestRunFirst);
|
|
168
234
|
return yield* summariesWithAttempts(storedRuns);
|
|
169
235
|
});
|
|
170
|
-
// Every open Run plus the newest `closedLimit`
|
|
171
|
-
// Waiting run never falls out of the operator's view while history stays bounded.
|
|
236
|
+
// Every open Run plus the newest `closedLimit` closed ones, so an old waiting Run stays listed.
|
|
172
237
|
const runSummaries = Effect.fn("RunStoreQueries.runSummaries")(function* (closedLimit) {
|
|
173
238
|
const [openRuns, closedRuns] = yield* Effect.all([
|
|
174
|
-
|
|
175
|
-
.select(runSelection)
|
|
176
|
-
.from(runs)
|
|
177
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
178
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
239
|
+
selectRuns()
|
|
179
240
|
.where(openState)
|
|
180
241
|
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
181
242
|
.execute()
|
|
182
243
|
.pipe(Effect.flatMap(decodeRunRows)),
|
|
183
|
-
|
|
184
|
-
.select(runSelection)
|
|
185
|
-
.from(runs)
|
|
186
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
187
|
-
.innerJoin(steps, eq(steps.runId, runs.runId))
|
|
244
|
+
selectRuns()
|
|
188
245
|
.where(notInArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]))
|
|
189
246
|
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
190
247
|
.limit(closedLimit)
|
|
@@ -194,6 +251,45 @@ export const queriesFor = (database) => {
|
|
|
194
251
|
const storedRuns = [...openRuns, ...closedRuns].sort(newestRunFirst);
|
|
195
252
|
return yield* summariesWithAttempts(storedRuns);
|
|
196
253
|
});
|
|
254
|
+
const runEvents = Effect.fn("RunStoreQueries.runEvents")(function* (runIds) {
|
|
255
|
+
// Chunks keep each read below the driver's bind-parameter limit.
|
|
256
|
+
const rows = yield* Effect.forEach(EffectArray.chunksOf([...new Set(runIds)], 100), (ids) => database
|
|
257
|
+
.select({
|
|
258
|
+
runId: runs.runId,
|
|
259
|
+
eventId: events.eventId,
|
|
260
|
+
dedupKey: events.dedupKey,
|
|
261
|
+
payload: events.payloadJson,
|
|
262
|
+
receivedAt: events.receivedAt,
|
|
263
|
+
})
|
|
264
|
+
.from(runs)
|
|
265
|
+
.innerJoin(events, eq(events.eventId, runs.eventId))
|
|
266
|
+
.where(inArray(runs.runId, ids))
|
|
267
|
+
.execute()
|
|
268
|
+
.pipe(Effect.flatMap(decodeRunEventRows)));
|
|
269
|
+
return rows.flat().map(runEventFromRow);
|
|
270
|
+
});
|
|
271
|
+
const closedRunSummaries = Effect.fn("RunStoreQueries.closedRunSummaries")(function* (limit) {
|
|
272
|
+
const closedRuns = yield* selectRuns()
|
|
273
|
+
.where(notInArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]))
|
|
274
|
+
.orderBy(desc(runs.closedAt), desc(runs.runId))
|
|
275
|
+
.limit(limit)
|
|
276
|
+
.execute()
|
|
277
|
+
.pipe(Effect.flatMap(decodeRunRows));
|
|
278
|
+
return yield* summariesWithAttempts(closedRuns);
|
|
279
|
+
});
|
|
280
|
+
// Every closed Run with `closedAt` at or after `since`, or every closed Run for `None`.
|
|
281
|
+
const closedRunSummariesSince = Effect.fn("RunStoreQueries.closedRunSummariesSince")(function* (since) {
|
|
282
|
+
const closed = notInArray(storedStateTag, [...RunState.OPEN_STATE_TAGS]);
|
|
283
|
+
const closedRuns = yield* selectRuns()
|
|
284
|
+
.where(Option.match(since, {
|
|
285
|
+
onNone: () => closed,
|
|
286
|
+
onSome: (from) => and(closed, gte(runs.closedAt, encodeDateTime(from))),
|
|
287
|
+
}))
|
|
288
|
+
.orderBy(desc(runs.closedAt), desc(runs.runId))
|
|
289
|
+
.execute()
|
|
290
|
+
.pipe(Effect.flatMap(decodeRunRows));
|
|
291
|
+
return yield* summariesWithAttempts(closedRuns);
|
|
292
|
+
});
|
|
197
293
|
const revision = Effect.fn("RunStoreQueries.projectionRevision")(function* () {
|
|
198
294
|
const rows = yield* decodeProjectionRevision(yield* database
|
|
199
295
|
.select({ revision: projectionRevision.revision })
|
|
@@ -203,10 +299,12 @@ export const queriesFor = (database) => {
|
|
|
203
299
|
.execute());
|
|
204
300
|
return Option.fromNullishOr(rows[0]?.revision);
|
|
205
301
|
});
|
|
206
|
-
|
|
302
|
+
// A closed state sets `closedAt` to `at` or now; an open state clears it.
|
|
303
|
+
const updateState = Effect.fn("RunStoreQueries.updateState")(function* (runId, state, at) {
|
|
304
|
+
const closedAt = RunState.isOpen(state) ? null : encodeDateTime(at ?? (yield* DateTime.now));
|
|
207
305
|
const updated = yield* database
|
|
208
306
|
.update(runs)
|
|
209
|
-
.set({ stateJson: encodeRunState(state) })
|
|
307
|
+
.set({ stateJson: encodeRunState(state), closedAt })
|
|
210
308
|
.where(eq(runs.runId, runId))
|
|
211
309
|
.returning({ runId: runs.runId })
|
|
212
310
|
.execute();
|
|
@@ -214,6 +312,19 @@ export const queriesFor = (database) => {
|
|
|
214
312
|
return yield* new RunNotFoundError({ runId });
|
|
215
313
|
}
|
|
216
314
|
});
|
|
315
|
+
const resolveFailure = Effect.fn("RunStoreQueries.resolveFailure")(function* (runId, attemptId, resolution) {
|
|
316
|
+
yield* Effect.annotateCurrentSpan({ runId, attemptId });
|
|
317
|
+
yield* database
|
|
318
|
+
.update(attempts)
|
|
319
|
+
.set({ failureResolutionJson: encodeFailureResolution(resolution) })
|
|
320
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
321
|
+
.execute();
|
|
322
|
+
yield* database
|
|
323
|
+
.update(runs)
|
|
324
|
+
.set({ resolvedFailureAttemptId: attemptId })
|
|
325
|
+
.where(eq(runs.runId, runId))
|
|
326
|
+
.execute();
|
|
327
|
+
});
|
|
217
328
|
const addAttempt = Effect.fn("RunStoreQueries.addAttempt")(function* (attempt) {
|
|
218
329
|
yield* database
|
|
219
330
|
.insert(attempts)
|
|
@@ -230,13 +341,82 @@ export const queriesFor = (database) => {
|
|
|
230
341
|
parentModel: Option.getOrNull(attempt.parentModel),
|
|
231
342
|
startedAt: encodeDateTime(attempt.startedAt),
|
|
232
343
|
outcomeJson: null,
|
|
344
|
+
endedAt: null,
|
|
345
|
+
salvageJson: null,
|
|
346
|
+
startJson: Option.match(attempt.start, { onNone: () => null, onSome: encodeAttemptStart }),
|
|
233
347
|
})
|
|
234
348
|
.execute();
|
|
235
349
|
});
|
|
236
|
-
const
|
|
350
|
+
const recordReattach = Effect.fn("RunStoreQueries.recordReattach")(function* (attemptId, reattachedAt) {
|
|
351
|
+
yield* Effect.annotateCurrentSpan({ attemptId });
|
|
237
352
|
yield* database
|
|
238
353
|
.update(attempts)
|
|
239
|
-
.set({
|
|
354
|
+
.set({ reattachedAt: encodeDateTime(reattachedAt) })
|
|
355
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
356
|
+
.execute();
|
|
357
|
+
});
|
|
358
|
+
/** Returns the operator retry for the Run's next Attempt, or `None`. */
|
|
359
|
+
const nextStart = Effect.fn("RunStoreQueries.nextStart")(function* (runId) {
|
|
360
|
+
const rows = yield* database
|
|
361
|
+
.select({ nextStart: runs.nextStartJson })
|
|
362
|
+
.from(runs)
|
|
363
|
+
.where(eq(runs.runId, runId))
|
|
364
|
+
.execute();
|
|
365
|
+
return Option.fromNullishOr((yield* decodeNextStarts(rows))[0]?.nextStart);
|
|
366
|
+
});
|
|
367
|
+
const setNextStart = Effect.fn("RunStoreQueries.setNextStart")(function* (runId, start) {
|
|
368
|
+
yield* database
|
|
369
|
+
.update(runs)
|
|
370
|
+
.set({
|
|
371
|
+
nextStartJson: Option.match(start, { onNone: () => null, onSome: encodeAttemptStart }),
|
|
372
|
+
})
|
|
373
|
+
.where(eq(runs.runId, runId))
|
|
374
|
+
.execute();
|
|
375
|
+
});
|
|
376
|
+
const recordSalvage = Effect.fn("RunStoreQueries.recordSalvage")(function* (attemptId, salvage) {
|
|
377
|
+
yield* Effect.annotateCurrentSpan({ attemptId, salvage: salvage._tag });
|
|
378
|
+
yield* database
|
|
379
|
+
.update(attempts)
|
|
380
|
+
.set({ salvageJson: encodeSalvage(salvage) })
|
|
381
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
382
|
+
.execute();
|
|
383
|
+
});
|
|
384
|
+
const recordComments = Effect.fn("RunStoreQueries.recordComments")(function* (attemptId, comments) {
|
|
385
|
+
yield* Effect.annotateCurrentSpan({ attemptId, comments: comments.length });
|
|
386
|
+
yield* database
|
|
387
|
+
.update(attempts)
|
|
388
|
+
.set({ commentsJson: encodeComments(comments) })
|
|
389
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
390
|
+
.execute();
|
|
391
|
+
});
|
|
392
|
+
/**
|
|
393
|
+
* Records the kills of one of the Attempt's containers, replacing an earlier record of the same
|
|
394
|
+
* container, and returns the kills of all its containers.
|
|
395
|
+
*/
|
|
396
|
+
const recordOutOfMemory = Effect.fn("RunStoreQueries.recordOutOfMemory")(function* (attemptId, kills) {
|
|
397
|
+
yield* Effect.annotateCurrentSpan({
|
|
398
|
+
attemptId,
|
|
399
|
+
container: kills.container,
|
|
400
|
+
kills: kills.kills,
|
|
401
|
+
});
|
|
402
|
+
const rows = yield* database
|
|
403
|
+
.select({ outOfMemory: attempts.outOfMemoryJson })
|
|
404
|
+
.from(attempts)
|
|
405
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
406
|
+
.execute();
|
|
407
|
+
const recorded = (yield* decodeOutOfMemory(rows))[0]?.outOfMemory ?? [];
|
|
408
|
+
const containers = EffectArray.append(recorded.filter(({ container }) => container !== kills.container), kills);
|
|
409
|
+
yield* database
|
|
410
|
+
.update(attempts)
|
|
411
|
+
.set({ outOfMemoryJson: encodeOutOfMemory(containers) })
|
|
412
|
+
.where(eq(attempts.attemptId, attemptId))
|
|
413
|
+
.execute();
|
|
414
|
+
return totalOutOfMemoryKills(containers);
|
|
415
|
+
});
|
|
416
|
+
const closeAttempt = Effect.fn("RunStoreQueries.closeAttempt")(function* (attemptId, outcome, endedAt) {
|
|
417
|
+
yield* database
|
|
418
|
+
.update(attempts)
|
|
419
|
+
.set({ outcomeJson: encodeOutcome(outcome), endedAt: encodeDateTime(endedAt) })
|
|
240
420
|
.where(and(eq(attempts.attemptId, attemptId), isNull(attempts.outcomeJson)))
|
|
241
421
|
.execute();
|
|
242
422
|
});
|
|
@@ -277,7 +457,7 @@ export const queriesFor = (database) => {
|
|
|
277
457
|
const approvalExists = Effect.fn("RunStoreQueries.approvalExists")(function* (runId) {
|
|
278
458
|
return (yield* database.$count(approvals, eq(approvals.runId, runId)).execute()) > 0;
|
|
279
459
|
});
|
|
280
|
-
const recordApproval = Effect.fn("RunStoreQueries.recordApproval")(function* (runId, approval) {
|
|
460
|
+
const recordApproval = Effect.fn("RunStoreQueries.recordApproval")(function* (runId, approval, source) {
|
|
281
461
|
yield* database
|
|
282
462
|
.insert(events)
|
|
283
463
|
.values({
|
|
@@ -288,30 +468,15 @@ export const queriesFor = (database) => {
|
|
|
288
468
|
})
|
|
289
469
|
.onConflictDoNothing()
|
|
290
470
|
.execute();
|
|
291
|
-
yield* database.insert(approvals).values({ runId, eventId: approval.id }).execute();
|
|
292
|
-
});
|
|
293
|
-
const requestWorkflowCommand = Effect.fn("RunStoreQueries.requestWorkflowCommand")(function* (runId, command) {
|
|
294
|
-
yield* database
|
|
295
|
-
.insert(workflowCommands)
|
|
296
|
-
.values({ runId, command })
|
|
297
|
-
.onConflictDoNothing()
|
|
298
|
-
.execute();
|
|
299
|
-
});
|
|
300
|
-
const requestInterrupt = Effect.fn("RunStoreQueries.requestInterrupt")(function* (runId) {
|
|
301
471
|
yield* database
|
|
302
|
-
.
|
|
303
|
-
.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const rows = yield* database
|
|
310
|
-
.select({ runId: workflowCommands.runId, command: workflowCommands.command })
|
|
311
|
-
.from(workflowCommands)
|
|
312
|
-
.where(eq(workflowCommands.runId, runId))
|
|
472
|
+
.insert(approvals)
|
|
473
|
+
.values({
|
|
474
|
+
runId,
|
|
475
|
+
eventId: approval.id,
|
|
476
|
+
channel: source.channel,
|
|
477
|
+
approver: Option.getOrNull(source.approver),
|
|
478
|
+
})
|
|
313
479
|
.execute();
|
|
314
|
-
return (yield* decodeWorkflowCommands(rows)).map(({ command }) => command);
|
|
315
480
|
});
|
|
316
481
|
const createRun = Effect.fn("RunStoreQueries.createRun")(function* (submission, eventId, runId) {
|
|
317
482
|
const candidate = alias(triggerEvents, "candidate");
|
|
@@ -376,7 +541,48 @@ export const queriesFor = (database) => {
|
|
|
376
541
|
.limit(1)
|
|
377
542
|
.execute());
|
|
378
543
|
});
|
|
379
|
-
|
|
544
|
+
/** Returns the newest reopening of `eventId` (#267), with its reopening number. */
|
|
545
|
+
const newestReopening = Effect.fn("RunStoreQueries.newestReopening")(function* (eventId) {
|
|
546
|
+
const rows = yield* decodeReopenings(yield* database
|
|
547
|
+
.select({ ...eventRouteSelection, reopening: triggerEvents.reopening })
|
|
548
|
+
.from(events)
|
|
549
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, events.eventId))
|
|
550
|
+
.where(eq(triggerEvents.reopensEventId, eventId))
|
|
551
|
+
.orderBy(desc(triggerEvents.reopening))
|
|
552
|
+
.limit(1)
|
|
553
|
+
.execute());
|
|
554
|
+
return Option.map(Option.fromNullishOr(rows[0]), (row) => ({
|
|
555
|
+
submission: submissionFromRow(row),
|
|
556
|
+
reopening: row.reopening,
|
|
557
|
+
}));
|
|
558
|
+
});
|
|
559
|
+
/** Returns the trigger Event that `eventId` reopens or runs again, or `None` for a trigger's own Event. */
|
|
560
|
+
const reopenedEvent = Effect.fn("RunStoreQueries.reopenedEvent")(function* (eventId) {
|
|
561
|
+
const reopened = alias(events, "reopened_event");
|
|
562
|
+
const rows = yield* decodeReopenedEvents(yield* database
|
|
563
|
+
.select({ eventId: reopened.eventId, dedupKey: reopened.dedupKey })
|
|
564
|
+
.from(triggerEvents)
|
|
565
|
+
.innerJoin(reopened, eq(reopened.eventId, triggerEvents.reopensEventId))
|
|
566
|
+
.where(eq(triggerEvents.eventId, eventId))
|
|
567
|
+
.execute());
|
|
568
|
+
return Option.fromNullishOr(rows[0]);
|
|
569
|
+
});
|
|
570
|
+
/** Records that `eventId` came from running a closed Run again, and the requesting login. */
|
|
571
|
+
const recordRunAgain = Effect.fn("RunStoreQueries.recordRunAgain")(function* (eventId, again) {
|
|
572
|
+
yield* database
|
|
573
|
+
.insert(runAgainEvents)
|
|
574
|
+
.values({
|
|
575
|
+
eventId,
|
|
576
|
+
fromRunId: again.from,
|
|
577
|
+
requestedBy: Option.getOrNull(again.requestedBy),
|
|
578
|
+
})
|
|
579
|
+
.execute();
|
|
580
|
+
});
|
|
581
|
+
/**
|
|
582
|
+
* Records a trigger Event and its route once, and returns the route as first recorded.
|
|
583
|
+
* `reopens` is the earlier Event that this one reopens.
|
|
584
|
+
*/
|
|
585
|
+
const recordTriggerEvent = Effect.fn("RunStoreQueries.recordTriggerEvent")(function* (submission, reopens = Option.none()) {
|
|
380
586
|
yield* database
|
|
381
587
|
.insert(events)
|
|
382
588
|
.values({
|
|
@@ -398,6 +604,7 @@ export const queriesFor = (database) => {
|
|
|
398
604
|
loopName: submission.loop,
|
|
399
605
|
stepName: submission.step,
|
|
400
606
|
approval: submission.approval,
|
|
607
|
+
permissions: submission.permissions,
|
|
401
608
|
cycleLimit: Option.getOrNull(submission.cycleLimit),
|
|
402
609
|
cycleGeneration: submission.cycleGeneration,
|
|
403
610
|
repository: submission.repository,
|
|
@@ -405,6 +612,11 @@ export const queriesFor = (database) => {
|
|
|
405
612
|
task: submission.task,
|
|
406
613
|
agentJson: storedAgent(submission.agent),
|
|
407
614
|
parentModel: Option.getOrNull(submission.parentModel),
|
|
615
|
+
reopensEventId: Option.getOrNull(Option.map(reopens, ({ eventId }) => eventId)),
|
|
616
|
+
reopening: Option.match(reopens, {
|
|
617
|
+
onNone: () => 0,
|
|
618
|
+
onSome: ({ reopening }) => reopening,
|
|
619
|
+
}),
|
|
408
620
|
})
|
|
409
621
|
.execute();
|
|
410
622
|
return submission;
|
|
@@ -416,6 +628,16 @@ export const queriesFor = (database) => {
|
|
|
416
628
|
const current = routes[0];
|
|
417
629
|
if (current === undefined)
|
|
418
630
|
return Option.none();
|
|
631
|
+
const suppression = yield* loopItemQueries(database).suppression(current.loop, current.itemKey);
|
|
632
|
+
if (Option.isSome(suppression)) {
|
|
633
|
+
yield* Effect.logInfo("Queued Events wait while a suppression keeps the Item", {
|
|
634
|
+
runId,
|
|
635
|
+
suppressedBy: suppression.value.runId,
|
|
636
|
+
loop: current.loop,
|
|
637
|
+
itemKey: current.itemKey,
|
|
638
|
+
});
|
|
639
|
+
return Option.none();
|
|
640
|
+
}
|
|
419
641
|
const queuedRows = yield* database
|
|
420
642
|
.select(eventRouteSelection)
|
|
421
643
|
.from(events)
|
|
@@ -424,55 +646,53 @@ export const queriesFor = (database) => {
|
|
|
424
646
|
.leftJoin(ignoredEvents, eq(ignoredEvents.eventId, events.eventId))
|
|
425
647
|
.where(and(eq(triggerEvents.loopName, current.loop), eq(triggerEvents.itemKey, current.itemKey), cycleQueries(database).currentEvent, isNull(runs.runId), isNull(ignoredEvents.eventId)))
|
|
426
648
|
.orderBy(desc(events.receivedAt))
|
|
427
|
-
.limit(1)
|
|
428
649
|
.execute();
|
|
429
650
|
const queued = yield* decodeEventRoutes(queuedRows);
|
|
430
|
-
return Option.fromNullishOr(queued[0]).pipe(Option.map(
|
|
651
|
+
return Option.fromNullishOr(queued[0]).pipe(Option.map((newest) => ({
|
|
652
|
+
submission: submissionFromRow(newest),
|
|
653
|
+
eventIds: queued.map((row) => row.eventId),
|
|
654
|
+
})));
|
|
431
655
|
});
|
|
432
|
-
const
|
|
656
|
+
const runIdsToDrive = Effect.fn("RunStoreQueries.runIdsToDrive")(function* () {
|
|
657
|
+
const waiting = "Waiting";
|
|
433
658
|
const rows = yield* database
|
|
434
|
-
.select({
|
|
435
|
-
...eventRouteSelection,
|
|
436
|
-
runId: runs.runId,
|
|
437
|
-
state: runs.stateJson,
|
|
438
|
-
})
|
|
659
|
+
.select({ runId: runs.runId })
|
|
439
660
|
.from(runs)
|
|
440
|
-
.
|
|
441
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, events.eventId))
|
|
442
|
-
.where(openState)
|
|
661
|
+
.where(and(openState, ne(storedStateTag, waiting)))
|
|
443
662
|
.execute();
|
|
444
|
-
return yield*
|
|
663
|
+
return (yield* decodeRunIds(rows)).map(({ runId }) => runId);
|
|
445
664
|
});
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
.
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
? -1
|
|
458
|
-
: 1);
|
|
459
|
-
});
|
|
460
|
-
const completeWorkflowCommand = Effect.fn("RunStoreQueries.completeWorkflowCommand")(function* (runId, command) {
|
|
461
|
-
yield* database
|
|
462
|
-
.delete(workflowCommands)
|
|
463
|
-
.where(and(eq(workflowCommands.runId, runId), eq(workflowCommands.command, command)))
|
|
665
|
+
// The newest closed Run of each Item that has a current queued Event and no open Run, excluding
|
|
666
|
+
// Items waiting for a cycle-budget reset or under a suppression.
|
|
667
|
+
const pendingContinuations = Effect.fn("RunStoreQueries.pendingContinuations")(function* () {
|
|
668
|
+
const items = yield* database
|
|
669
|
+
.selectDistinct({ loop: triggerEvents.loopName, itemKey: triggerEvents.itemKey })
|
|
670
|
+
.from(triggerEvents)
|
|
671
|
+
.leftJoin(runs, eq(runs.eventId, triggerEvents.eventId))
|
|
672
|
+
.leftJoin(ignoredEvents, eq(ignoredEvents.eventId, triggerEvents.eventId))
|
|
673
|
+
.leftJoin(itemCycleBudgets, eq(itemCycleBudgets.itemKey, triggerEvents.itemKey))
|
|
674
|
+
.leftJoin(loopItems, and(eq(loopItems.loopName, triggerEvents.loopName), eq(loopItems.itemKey, triggerEvents.itemKey)))
|
|
675
|
+
.where(and(isNull(runs.runId), isNull(ignoredEvents.eventId), isNull(itemCycleBudgets.askJson), isNull(loopItems.suppressedByRunId), cycleQueries(database).currentEvent))
|
|
464
676
|
.execute();
|
|
677
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ loop: LoopName, itemKey: ItemKey })))(items);
|
|
678
|
+
const closed = yield* Effect.forEach(decoded, ({ loop, itemKey }) => Effect.gen(function* () {
|
|
679
|
+
if ((yield* blockingRun(loop, itemKey)).length > 0)
|
|
680
|
+
return [];
|
|
681
|
+
return yield* decodeRunIds(yield* database
|
|
682
|
+
.select({ runId: runs.runId })
|
|
683
|
+
.from(runs)
|
|
684
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
685
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey)))
|
|
686
|
+
.orderBy(desc(runs.openedAt), desc(runs.runId))
|
|
687
|
+
.limit(1)
|
|
688
|
+
.execute());
|
|
689
|
+
}));
|
|
690
|
+
return closed.flat().map(({ runId }) => runId);
|
|
465
691
|
});
|
|
466
|
-
const
|
|
467
|
-
const rows = yield* decodeRunIds(yield* database
|
|
468
|
-
.select({ runId: runs.runId })
|
|
469
|
-
.from(runs)
|
|
470
|
-
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
471
|
-
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey), unstartedState))
|
|
472
|
-
.execute());
|
|
692
|
+
const revokeQueued = Effect.fn("RunStoreQueries.revokeQueued")(function* (loop, itemKey, eventIds) {
|
|
473
693
|
const candidate = alias(triggerEvents, "candidate");
|
|
474
694
|
const candidateRun = alias(runs, "candidate_run");
|
|
475
|
-
yield* database
|
|
695
|
+
const ignored = yield* database
|
|
476
696
|
.insert(ignoredEvents)
|
|
477
697
|
.select(database
|
|
478
698
|
.select({
|
|
@@ -481,41 +701,69 @@ export const queriesFor = (database) => {
|
|
|
481
701
|
})
|
|
482
702
|
.from(candidate)
|
|
483
703
|
.leftJoin(candidateRun, eq(candidateRun.eventId, candidate.eventId))
|
|
484
|
-
.where(and(eq(candidate.loopName, loop), eq(candidate.itemKey, itemKey), isNull(candidateRun.runId))))
|
|
704
|
+
.where(and(eq(candidate.loopName, loop), eq(candidate.itemKey, itemKey), inArray(candidate.eventId, eventIds), isNull(candidateRun.runId))))
|
|
485
705
|
.onConflictDoNothing()
|
|
706
|
+
.returning({ eventId: ignoredEvents.eventId })
|
|
486
707
|
.execute();
|
|
708
|
+
return ignored.length;
|
|
709
|
+
});
|
|
710
|
+
const revokeUnstarted = Effect.fn("RunStoreQueries.revokeUnstarted")(function* (loop, itemKey) {
|
|
711
|
+
const rows = yield* decodeRunIds(yield* database
|
|
712
|
+
.select({ runId: runs.runId })
|
|
713
|
+
.from(runs)
|
|
714
|
+
.innerJoin(triggerEvents, eq(triggerEvents.eventId, runs.eventId))
|
|
715
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey), unstartedState))
|
|
716
|
+
.execute());
|
|
717
|
+
const queued = database
|
|
718
|
+
.select({ eventId: triggerEvents.eventId })
|
|
719
|
+
.from(triggerEvents)
|
|
720
|
+
.where(and(eq(triggerEvents.loopName, loop), eq(triggerEvents.itemKey, itemKey)));
|
|
721
|
+
yield* revokeQueued(loop, itemKey, queued);
|
|
487
722
|
return rows;
|
|
488
723
|
});
|
|
489
724
|
return {
|
|
725
|
+
salvageCandidates,
|
|
726
|
+
salvageCandidate,
|
|
490
727
|
cycles: cycleQueries(database),
|
|
728
|
+
loopItems: loopItemQueries(database),
|
|
491
729
|
...transcriptQueries(database),
|
|
492
730
|
addAttempt,
|
|
731
|
+
recordReattach,
|
|
732
|
+
nextStart,
|
|
733
|
+
setNextStart,
|
|
734
|
+
recordSalvage,
|
|
735
|
+
recordComments,
|
|
736
|
+
recordOutOfMemory,
|
|
493
737
|
amend,
|
|
494
738
|
markIntervened,
|
|
495
739
|
approvalExists,
|
|
496
740
|
blockingRun,
|
|
497
741
|
closeAttempt,
|
|
498
|
-
completeWorkflowCommand,
|
|
499
742
|
createRun,
|
|
500
743
|
eventRouteByRun,
|
|
501
|
-
workflowCommandsFor,
|
|
502
744
|
ignoredEvent,
|
|
745
|
+
newestReopening,
|
|
503
746
|
nextQueued,
|
|
504
|
-
|
|
747
|
+
reopenedEvent,
|
|
748
|
+
recordRunAgain,
|
|
749
|
+
pendingContinuations,
|
|
505
750
|
recordTriggerEvent,
|
|
506
|
-
|
|
751
|
+
runIdsToDrive,
|
|
507
752
|
recordApproval,
|
|
508
|
-
requestInterrupt,
|
|
509
|
-
requestWorkflowCommand,
|
|
510
753
|
revision,
|
|
754
|
+
revokeQueued,
|
|
511
755
|
revokeUnstarted,
|
|
512
756
|
runSummaries,
|
|
757
|
+
closedRunSummaries,
|
|
758
|
+
closedRunSummariesSince,
|
|
513
759
|
itemRunSummaries,
|
|
760
|
+
runEvents,
|
|
514
761
|
runForEvent,
|
|
515
762
|
snapshot,
|
|
516
763
|
updateStepResult,
|
|
517
764
|
updateAttemptParentModel,
|
|
518
765
|
updateState,
|
|
766
|
+
resolveFailure,
|
|
519
767
|
};
|
|
520
768
|
};
|
|
521
769
|
//# sourceMappingURL=queries.js.map
|