@sixb/pipeline-worker 0.0.1
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 +21 -0
- package/README.md +34 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/events.d.ts +15 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/execute-run-step.d.ts +19 -0
- package/dist/execute-run-step.d.ts.map +1 -0
- package/dist/execute-sql-step.d.ts +18 -0
- package/dist/execute-sql-step.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +654 -0
- package/dist/index.js.map +17 -0
- package/dist/run-pipeline-job.d.ts +9 -0
- package/dist/run-pipeline-job.d.ts.map +1 -0
- package/dist/run-step.d.ts +14 -0
- package/dist/run-step.d.ts.map +1 -0
- package/dist/step-inputs.d.ts +16 -0
- package/dist/step-inputs.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/worker.d.ts +13 -0
- package/dist/worker.d.ts.map +1 -0
- package/package.json +57 -0
- package/src/errors.ts +110 -0
- package/src/events.ts +189 -0
- package/src/execute-run-step.ts +98 -0
- package/src/execute-sql-step.ts +115 -0
- package/src/index.ts +2 -0
- package/src/run-pipeline-job.ts +130 -0
- package/src/run-step.ts +154 -0
- package/src/step-inputs.ts +84 -0
- package/src/types.ts +88 -0
- package/src/worker.ts +165 -0
package/src/worker.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { reportRunFailure } from "@sixb/core/internal/error-reporting"
|
|
2
|
+
import type { QueueWorkerFailureDecision } from "@sixb/core/internal/workers"
|
|
3
|
+
import { QueueWorker } from "@sixb/core/internal/workers"
|
|
4
|
+
import type { ClaimedQueueJob, PipelineRunRequestedQueueJob } from "@sixb/core/queues"
|
|
5
|
+
import type { PipelineRunStorage } from "@sixb/core/storage"
|
|
6
|
+
import {
|
|
7
|
+
emitDatasetVersionCommitted,
|
|
8
|
+
emitPipelineRunFinished,
|
|
9
|
+
emitPipelineRunStarted,
|
|
10
|
+
emitPipelineRunStepFinished,
|
|
11
|
+
emitPipelineRunStepStarted,
|
|
12
|
+
} from "./events"
|
|
13
|
+
import { PipelineRunAlreadyStartedError, runPipelineJob } from "./run-pipeline-job"
|
|
14
|
+
import type {
|
|
15
|
+
PipelineJob,
|
|
16
|
+
PipelineRunResult,
|
|
17
|
+
PipelineWorkerContext,
|
|
18
|
+
PipelineWorkerSixb,
|
|
19
|
+
} from "./types"
|
|
20
|
+
|
|
21
|
+
export class PipelineWorker extends QueueWorker<PipelineRunRequestedQueueJob> {
|
|
22
|
+
private readonly context: PipelineWorkerContext
|
|
23
|
+
private readonly sixb: PipelineWorkerSixb
|
|
24
|
+
|
|
25
|
+
constructor(sixb: PipelineWorkerSixb) {
|
|
26
|
+
if (sixb.listPipelines().length === 0) {
|
|
27
|
+
throw new Error("[SixbPipelineWorker] No pipeline definitions are registered.")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const pipelineRunsStorage = sixb.storage.pipelineRuns
|
|
31
|
+
if (!pipelineRunsStorage) {
|
|
32
|
+
throw new Error("[SixbPipelineWorker] Pipeline workers require storage.pipelineRuns support.")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
super({
|
|
36
|
+
projectId: sixb.id,
|
|
37
|
+
queue: sixb.queues.pipelines,
|
|
38
|
+
workerId: `pipeline-worker-${sixb.id}`,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
this.context = buildPipelineContext(sixb, pipelineRunsStorage)
|
|
42
|
+
this.sixb = sixb
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected async execute(
|
|
46
|
+
claimed: ClaimedQueueJob<PipelineRunRequestedQueueJob>,
|
|
47
|
+
signal: AbortSignal
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const { job } = claimed
|
|
50
|
+
const pipelineJob: PipelineJob = {
|
|
51
|
+
id: job.payload.runId ?? `${job.id}:attempt:${job.attempt}`,
|
|
52
|
+
pipelineId: job.payload.pipelineId,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let result: PipelineRunResult
|
|
56
|
+
try {
|
|
57
|
+
result = await runPipelineJob({
|
|
58
|
+
runtime: this.context,
|
|
59
|
+
job: pipelineJob,
|
|
60
|
+
signal,
|
|
61
|
+
onRunStarted: (run) => emitPipelineRunStarted(this.sixb.events, run),
|
|
62
|
+
onRunFailed: (error, run) => {
|
|
63
|
+
reportRunFailure(this.sixb, error, {
|
|
64
|
+
projectId: this.sixb.id,
|
|
65
|
+
occurredAt: run.finishedAt,
|
|
66
|
+
attempt: job.attempt,
|
|
67
|
+
run: {
|
|
68
|
+
kind: "pipeline",
|
|
69
|
+
runId: run.id,
|
|
70
|
+
pipelineId: run.pipelineId,
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
onStepStarted: (step, context) =>
|
|
75
|
+
emitPipelineRunStepStarted(this.sixb.events, step, context),
|
|
76
|
+
onStepFinished: (step, context) =>
|
|
77
|
+
emitPipelineRunStepFinished(this.sixb.events, step, context),
|
|
78
|
+
onStepCommitted: (step) => emitDatasetVersionCommitted(this.sixb.events, pipelineJob, step),
|
|
79
|
+
})
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error instanceof PipelineRunAlreadyStartedError) return
|
|
82
|
+
throw error
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await emitPipelineRunFinished(this.sixb.events, {
|
|
86
|
+
id: pipelineJob.id,
|
|
87
|
+
pipelineId: pipelineJob.pipelineId,
|
|
88
|
+
status: "succeeded",
|
|
89
|
+
datasetId: result.version?.datasetId,
|
|
90
|
+
versionId: result.version?.versionId,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected override async onExecutionError(
|
|
95
|
+
claimed: ClaimedQueueJob<PipelineRunRequestedQueueJob>,
|
|
96
|
+
_error: unknown
|
|
97
|
+
): Promise<QueueWorkerFailureDecision> {
|
|
98
|
+
const { job } = claimed
|
|
99
|
+
await emitPipelineRunFinished(this.sixb.events, {
|
|
100
|
+
id: job.payload.runId ?? `${job.id}:attempt:${job.attempt}`,
|
|
101
|
+
pipelineId: job.payload.pipelineId,
|
|
102
|
+
status: "failed",
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Pipeline runs are not all-or-nothing in V1. A previous step may have committed an append
|
|
106
|
+
// output, so failed pipeline jobs must not be retried automatically.
|
|
107
|
+
return { kind: "fail" }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected override async onAbortError(
|
|
111
|
+
claimed: ClaimedQueueJob<PipelineRunRequestedQueueJob>,
|
|
112
|
+
_error: unknown
|
|
113
|
+
): Promise<QueueWorkerFailureDecision> {
|
|
114
|
+
const { job } = claimed
|
|
115
|
+
const pipelineJob: PipelineJob = {
|
|
116
|
+
id: job.payload.runId ?? `${job.id}:attempt:${job.attempt}`,
|
|
117
|
+
pipelineId: job.payload.pipelineId,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!(await hasCommittedStep(this.context, pipelineJob.id))) {
|
|
121
|
+
return { kind: "retry" }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// After a step commit, rerunning the whole pipeline could duplicate append outputs.
|
|
125
|
+
await emitPipelineRunFinished(this.sixb.events, {
|
|
126
|
+
id: pipelineJob.id,
|
|
127
|
+
pipelineId: pipelineJob.pipelineId,
|
|
128
|
+
status: "cancelled",
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
return { kind: "fail" }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildPipelineContext(
|
|
136
|
+
sixb: PipelineWorkerSixb,
|
|
137
|
+
pipelineRunsStorage: PipelineRunStorage
|
|
138
|
+
): PipelineWorkerContext {
|
|
139
|
+
return {
|
|
140
|
+
id: sixb.id,
|
|
141
|
+
pipelineRunsStorage,
|
|
142
|
+
lakeStorage: sixb.lakeStorage,
|
|
143
|
+
logs: sixb.logs,
|
|
144
|
+
getPipelineById(pipelineId) {
|
|
145
|
+
return sixb.getPipelineById(pipelineId)
|
|
146
|
+
},
|
|
147
|
+
getDatasetById(datasetId) {
|
|
148
|
+
return sixb.getDatasetById(datasetId)
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function hasCommittedStep(
|
|
154
|
+
context: PipelineWorkerContext,
|
|
155
|
+
pipelineRunId: string
|
|
156
|
+
): Promise<boolean> {
|
|
157
|
+
const result = await context.pipelineRunsStorage.listSteps({
|
|
158
|
+
projectId: context.id,
|
|
159
|
+
pipelineRunId,
|
|
160
|
+
statuses: ["succeeded"],
|
|
161
|
+
limit: 1,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return result.steps.some((step) => Boolean(step.output))
|
|
165
|
+
}
|