duron 0.2.1 → 0.3.0-beta.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/dist/action-job.d.ts +2 -0
- package/dist/action-job.d.ts.map +1 -1
- package/dist/action-job.js +20 -1
- package/dist/action-manager.d.ts +2 -0
- package/dist/action-manager.d.ts.map +1 -1
- package/dist/action-manager.js +3 -0
- package/dist/action.d.ts +7 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +1 -0
- package/dist/adapters/adapter.d.ts +10 -2
- package/dist/adapters/adapter.d.ts.map +1 -1
- package/dist/adapters/adapter.js +59 -1
- package/dist/adapters/postgres/base.d.ts +9 -4
- package/dist/adapters/postgres/base.d.ts.map +1 -1
- package/dist/adapters/postgres/base.js +269 -19
- package/dist/adapters/postgres/schema.d.ts +249 -105
- package/dist/adapters/postgres/schema.d.ts.map +1 -1
- package/dist/adapters/postgres/schema.default.d.ts +249 -106
- package/dist/adapters/postgres/schema.default.d.ts.map +1 -1
- package/dist/adapters/postgres/schema.default.js +2 -2
- package/dist/adapters/postgres/schema.js +29 -1
- package/dist/adapters/schemas.d.ts +140 -7
- package/dist/adapters/schemas.d.ts.map +1 -1
- package/dist/adapters/schemas.js +52 -4
- package/dist/client.d.ts +8 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -1
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +16 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/server.d.ts +220 -16
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +123 -8
- package/dist/step-manager.d.ts +8 -2
- package/dist/step-manager.d.ts.map +1 -1
- package/dist/step-manager.js +138 -15
- package/dist/telemetry/adapter.d.ts +85 -0
- package/dist/telemetry/adapter.d.ts.map +1 -0
- package/dist/telemetry/adapter.js +128 -0
- package/dist/telemetry/index.d.ts +5 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +4 -0
- package/dist/telemetry/local.d.ts +21 -0
- package/dist/telemetry/local.d.ts.map +1 -0
- package/dist/telemetry/local.js +180 -0
- package/dist/telemetry/noop.d.ts +16 -0
- package/dist/telemetry/noop.d.ts.map +1 -0
- package/dist/telemetry/noop.js +39 -0
- package/dist/telemetry/opentelemetry.d.ts +24 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -0
- package/dist/telemetry/opentelemetry.js +202 -0
- package/migrations/postgres/20260117231749_clumsy_penance/migration.sql +3 -0
- package/migrations/postgres/20260117231749_clumsy_penance/snapshot.json +988 -0
- package/migrations/postgres/20260118202533_wealthy_mysterio/migration.sql +24 -0
- package/migrations/postgres/20260118202533_wealthy_mysterio/snapshot.json +1362 -0
- package/package.json +6 -4
- package/src/action-job.ts +35 -0
- package/src/action-manager.ts +5 -0
- package/src/action.ts +56 -0
- package/src/adapters/adapter.ts +151 -0
- package/src/adapters/postgres/base.ts +342 -23
- package/src/adapters/postgres/schema.default.ts +2 -2
- package/src/adapters/postgres/schema.ts +49 -1
- package/src/adapters/schemas.ts +81 -5
- package/src/client.ts +80 -2
- package/src/errors.ts +45 -1
- package/src/index.ts +3 -1
- package/src/server.ts +163 -8
- package/src/step-manager.ts +232 -13
- package/src/telemetry/adapter.ts +468 -0
- package/src/telemetry/index.ts +17 -0
- package/src/telemetry/local.ts +336 -0
- package/src/telemetry/noop.ts +95 -0
- package/src/telemetry/opentelemetry.ts +310 -0
|
@@ -64,6 +64,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
64
64
|
status: JOB_STATUS_COMPLETED,
|
|
65
65
|
output,
|
|
66
66
|
finished_at: sql `now()`,
|
|
67
|
+
updated_at: sql `now()`,
|
|
67
68
|
})
|
|
68
69
|
.where(and(eq(this.tables.jobsTable.id, jobId), eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE), eq(this.tables.jobsTable.client_id, this.id), gt(this.tables.jobsTable.expires_at, sql `now()`)))
|
|
69
70
|
.returning({ id: this.tables.jobsTable.id });
|
|
@@ -76,6 +77,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
76
77
|
status: JOB_STATUS_FAILED,
|
|
77
78
|
error,
|
|
78
79
|
finished_at: sql `now()`,
|
|
80
|
+
updated_at: sql `now()`,
|
|
79
81
|
})
|
|
80
82
|
.where(and(eq(this.tables.jobsTable.id, jobId), eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE), eq(this.tables.jobsTable.client_id, this.id)))
|
|
81
83
|
.returning({ id: this.tables.jobsTable.id });
|
|
@@ -87,6 +89,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
87
89
|
.set({
|
|
88
90
|
status: JOB_STATUS_CANCELLED,
|
|
89
91
|
finished_at: sql `now()`,
|
|
92
|
+
updated_at: sql `now()`,
|
|
90
93
|
})
|
|
91
94
|
.where(and(eq(this.tables.jobsTable.id, jobId), or(eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE), eq(this.tables.jobsTable.status, JOB_STATUS_CREATED))))
|
|
92
95
|
.returning({ id: this.tables.jobsTable.id });
|
|
@@ -166,6 +169,173 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
166
169
|
}
|
|
167
170
|
return result[0].id;
|
|
168
171
|
}
|
|
172
|
+
async _timeTravelJob({ jobId, stepId }) {
|
|
173
|
+
const result = this._map(await this.db.execute(sql `
|
|
174
|
+
WITH RECURSIVE
|
|
175
|
+
-- Lock and validate the job
|
|
176
|
+
locked_job AS (
|
|
177
|
+
SELECT j.id
|
|
178
|
+
FROM ${this.tables.jobsTable} j
|
|
179
|
+
WHERE j.id = ${jobId}
|
|
180
|
+
AND j.status IN (${JOB_STATUS_COMPLETED}, ${JOB_STATUS_FAILED}, ${JOB_STATUS_CANCELLED})
|
|
181
|
+
FOR UPDATE OF j
|
|
182
|
+
),
|
|
183
|
+
-- Validate target step exists and belongs to job
|
|
184
|
+
target_step AS (
|
|
185
|
+
SELECT s.id, s.parent_step_id, s.created_at
|
|
186
|
+
FROM ${this.tables.jobStepsTable} s
|
|
187
|
+
WHERE s.id = ${stepId}
|
|
188
|
+
AND s.job_id = ${jobId}
|
|
189
|
+
AND EXISTS (SELECT 1 FROM locked_job)
|
|
190
|
+
),
|
|
191
|
+
-- Find all ancestor steps recursively (from target up to root)
|
|
192
|
+
ancestors AS (
|
|
193
|
+
SELECT s.id, s.parent_step_id, 0 AS depth
|
|
194
|
+
FROM ${this.tables.jobStepsTable} s
|
|
195
|
+
WHERE s.id = (SELECT parent_step_id FROM target_step)
|
|
196
|
+
AND EXISTS (SELECT 1 FROM target_step)
|
|
197
|
+
UNION ALL
|
|
198
|
+
SELECT s.id, s.parent_step_id, a.depth + 1
|
|
199
|
+
FROM ${this.tables.jobStepsTable} s
|
|
200
|
+
INNER JOIN ancestors a ON s.id = a.parent_step_id
|
|
201
|
+
),
|
|
202
|
+
-- Steps to keep: completed steps created before target + completed parallel siblings of target and ancestors + their descendants
|
|
203
|
+
parallel_siblings AS (
|
|
204
|
+
-- Completed parallel siblings of target step
|
|
205
|
+
SELECT s.id
|
|
206
|
+
FROM ${this.tables.jobStepsTable} s
|
|
207
|
+
CROSS JOIN target_step ts
|
|
208
|
+
WHERE s.job_id = ${jobId}
|
|
209
|
+
AND s.id != ts.id
|
|
210
|
+
AND s.branch = true
|
|
211
|
+
AND s.status = ${STEP_STATUS_COMPLETED}
|
|
212
|
+
AND (
|
|
213
|
+
(s.parent_step_id IS NULL AND ts.parent_step_id IS NULL)
|
|
214
|
+
OR s.parent_step_id = ts.parent_step_id
|
|
215
|
+
)
|
|
216
|
+
UNION
|
|
217
|
+
-- Completed parallel siblings of each ancestor
|
|
218
|
+
SELECT s.id
|
|
219
|
+
FROM ${this.tables.jobStepsTable} s
|
|
220
|
+
INNER JOIN ancestors a ON (
|
|
221
|
+
(s.parent_step_id IS NULL AND a.parent_step_id IS NULL)
|
|
222
|
+
OR s.parent_step_id = a.parent_step_id
|
|
223
|
+
)
|
|
224
|
+
WHERE s.job_id = ${jobId}
|
|
225
|
+
AND s.id NOT IN (SELECT id FROM ancestors)
|
|
226
|
+
AND s.branch = true
|
|
227
|
+
AND s.status = ${STEP_STATUS_COMPLETED}
|
|
228
|
+
),
|
|
229
|
+
-- Find all descendants of parallel siblings (to keep their children too)
|
|
230
|
+
parallel_descendants AS (
|
|
231
|
+
SELECT s.id
|
|
232
|
+
FROM ${this.tables.jobStepsTable} s
|
|
233
|
+
WHERE s.id IN (SELECT id FROM parallel_siblings)
|
|
234
|
+
UNION ALL
|
|
235
|
+
SELECT s.id
|
|
236
|
+
FROM ${this.tables.jobStepsTable} s
|
|
237
|
+
INNER JOIN parallel_descendants pd ON s.parent_step_id = pd.id
|
|
238
|
+
WHERE s.job_id = ${jobId}
|
|
239
|
+
),
|
|
240
|
+
steps_to_keep AS (
|
|
241
|
+
-- Steps created before target that are completed (non-ancestor, non-target)
|
|
242
|
+
SELECT s.id
|
|
243
|
+
FROM ${this.tables.jobStepsTable} s
|
|
244
|
+
CROSS JOIN target_step ts
|
|
245
|
+
WHERE s.job_id = ${jobId}
|
|
246
|
+
AND s.created_at < ts.created_at
|
|
247
|
+
AND s.status = ${STEP_STATUS_COMPLETED}
|
|
248
|
+
AND s.id NOT IN (SELECT id FROM ancestors)
|
|
249
|
+
AND s.id != ts.id
|
|
250
|
+
UNION
|
|
251
|
+
-- All parallel siblings and their descendants
|
|
252
|
+
SELECT id FROM parallel_descendants
|
|
253
|
+
),
|
|
254
|
+
-- Calculate time offset: shift preserved steps to start from "now"
|
|
255
|
+
time_offset AS (
|
|
256
|
+
SELECT
|
|
257
|
+
now() - MIN(s.started_at) AS offset_interval
|
|
258
|
+
FROM ${this.tables.jobStepsTable} s
|
|
259
|
+
WHERE s.id IN (SELECT id FROM steps_to_keep)
|
|
260
|
+
),
|
|
261
|
+
-- Shift times of preserved steps to align with current time (only started_at/finished_at, NOT created_at to preserve ordering)
|
|
262
|
+
shift_preserved_times AS (
|
|
263
|
+
UPDATE ${this.tables.jobStepsTable}
|
|
264
|
+
SET
|
|
265
|
+
started_at = started_at + (SELECT offset_interval FROM time_offset),
|
|
266
|
+
finished_at = CASE
|
|
267
|
+
WHEN finished_at IS NOT NULL
|
|
268
|
+
THEN finished_at + (SELECT offset_interval FROM time_offset)
|
|
269
|
+
ELSE NULL
|
|
270
|
+
END,
|
|
271
|
+
updated_at = now()
|
|
272
|
+
WHERE id IN (SELECT id FROM steps_to_keep)
|
|
273
|
+
AND (SELECT offset_interval FROM time_offset) IS NOT NULL
|
|
274
|
+
RETURNING id
|
|
275
|
+
),
|
|
276
|
+
-- Delete steps that are not in the keep list and are not ancestors/target
|
|
277
|
+
deleted_steps AS (
|
|
278
|
+
DELETE FROM ${this.tables.jobStepsTable}
|
|
279
|
+
WHERE job_id = ${jobId}
|
|
280
|
+
AND id NOT IN (SELECT id FROM steps_to_keep)
|
|
281
|
+
AND id NOT IN (SELECT id FROM ancestors)
|
|
282
|
+
AND id != (SELECT id FROM target_step)
|
|
283
|
+
RETURNING id
|
|
284
|
+
),
|
|
285
|
+
-- Reset ancestor steps to active
|
|
286
|
+
reset_ancestors AS (
|
|
287
|
+
UPDATE ${this.tables.jobStepsTable}
|
|
288
|
+
SET
|
|
289
|
+
status = ${STEP_STATUS_ACTIVE},
|
|
290
|
+
output = NULL,
|
|
291
|
+
error = NULL,
|
|
292
|
+
finished_at = NULL,
|
|
293
|
+
started_at = now(),
|
|
294
|
+
expires_at = now() + (timeout_ms || ' milliseconds')::interval,
|
|
295
|
+
retries_count = 0,
|
|
296
|
+
delayed_ms = NULL,
|
|
297
|
+
history_failed_attempts = '{}'::jsonb,
|
|
298
|
+
updated_at = now()
|
|
299
|
+
WHERE id IN (SELECT id FROM ancestors)
|
|
300
|
+
RETURNING id
|
|
301
|
+
),
|
|
302
|
+
-- Reset target step to active
|
|
303
|
+
reset_target AS (
|
|
304
|
+
UPDATE ${this.tables.jobStepsTable}
|
|
305
|
+
SET
|
|
306
|
+
status = ${STEP_STATUS_ACTIVE},
|
|
307
|
+
output = NULL,
|
|
308
|
+
error = NULL,
|
|
309
|
+
finished_at = NULL,
|
|
310
|
+
started_at = now(),
|
|
311
|
+
expires_at = now() + (timeout_ms || ' milliseconds')::interval,
|
|
312
|
+
retries_count = 0,
|
|
313
|
+
delayed_ms = NULL,
|
|
314
|
+
history_failed_attempts = '{}'::jsonb,
|
|
315
|
+
updated_at = now()
|
|
316
|
+
WHERE id = (SELECT id FROM target_step)
|
|
317
|
+
RETURNING id
|
|
318
|
+
),
|
|
319
|
+
-- Reset job to created status
|
|
320
|
+
reset_job AS (
|
|
321
|
+
UPDATE ${this.tables.jobsTable}
|
|
322
|
+
SET
|
|
323
|
+
status = ${JOB_STATUS_CREATED},
|
|
324
|
+
output = NULL,
|
|
325
|
+
error = NULL,
|
|
326
|
+
started_at = NULL,
|
|
327
|
+
finished_at = NULL,
|
|
328
|
+
client_id = NULL,
|
|
329
|
+
expires_at = NULL,
|
|
330
|
+
updated_at = now()
|
|
331
|
+
WHERE id = ${jobId}
|
|
332
|
+
AND EXISTS (SELECT 1 FROM target_step)
|
|
333
|
+
RETURNING id
|
|
334
|
+
)
|
|
335
|
+
SELECT EXISTS(SELECT 1 FROM reset_job) AS success
|
|
336
|
+
`));
|
|
337
|
+
return result.length > 0 && result[0].success === true;
|
|
338
|
+
}
|
|
169
339
|
async _deleteJob({ jobId }) {
|
|
170
340
|
const result = await this.db
|
|
171
341
|
.delete(this.tables.jobsTable)
|
|
@@ -271,7 +441,8 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
271
441
|
SET status = ${JOB_STATUS_ACTIVE},
|
|
272
442
|
started_at = now(),
|
|
273
443
|
expires_at = now() + (timeout_ms || ' milliseconds')::interval,
|
|
274
|
-
client_id = ${this.id}
|
|
444
|
+
client_id = ${this.id},
|
|
445
|
+
updated_at = now()
|
|
275
446
|
FROM verify_concurrency vc
|
|
276
447
|
WHERE j.id = vc.id
|
|
277
448
|
AND vc.current_active < vc.concurrency_limit -- Final concurrency check using job's concurrency limit
|
|
@@ -337,7 +508,8 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
337
508
|
expires_at = NULL,
|
|
338
509
|
finished_at = NULL,
|
|
339
510
|
output = NULL,
|
|
340
|
-
error = NULL
|
|
511
|
+
error = NULL,
|
|
512
|
+
updated_at = now()
|
|
341
513
|
WHERE EXISTS (SELECT 1 FROM locked_jobs lj WHERE lj.id = j.id)
|
|
342
514
|
RETURNING id, checksum
|
|
343
515
|
),
|
|
@@ -355,7 +527,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
355
527
|
}
|
|
356
528
|
return 0;
|
|
357
529
|
}
|
|
358
|
-
async _createOrRecoverJobStep({ jobId, name, timeoutMs, retriesLimit, }) {
|
|
530
|
+
async _createOrRecoverJobStep({ jobId, name, timeoutMs, retriesLimit, parentStepId, parallel = false, }) {
|
|
359
531
|
const [result] = this._map(await this.db.execute(sql `
|
|
360
532
|
WITH job_check AS (
|
|
361
533
|
SELECT j.id
|
|
@@ -373,6 +545,8 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
373
545
|
upserted_step AS (
|
|
374
546
|
INSERT INTO ${this.tables.jobStepsTable} (
|
|
375
547
|
job_id,
|
|
548
|
+
parent_step_id,
|
|
549
|
+
branch,
|
|
376
550
|
name,
|
|
377
551
|
timeout_ms,
|
|
378
552
|
retries_limit,
|
|
@@ -384,6 +558,8 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
384
558
|
)
|
|
385
559
|
SELECT
|
|
386
560
|
${jobId},
|
|
561
|
+
${parentStepId},
|
|
562
|
+
${parallel},
|
|
387
563
|
${name},
|
|
388
564
|
${timeoutMs},
|
|
389
565
|
${retriesLimit},
|
|
@@ -452,6 +628,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
452
628
|
status: STEP_STATUS_COMPLETED,
|
|
453
629
|
output,
|
|
454
630
|
finished_at: sql `now()`,
|
|
631
|
+
updated_at: sql `now()`,
|
|
455
632
|
})
|
|
456
633
|
.from(this.tables.jobsTable)
|
|
457
634
|
.where(and(eq(this.tables.jobStepsTable.job_id, this.tables.jobsTable.id), eq(this.tables.jobStepsTable.id, stepId), eq(this.tables.jobStepsTable.status, STEP_STATUS_ACTIVE), eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE), or(isNull(this.tables.jobsTable.expires_at), gt(this.tables.jobsTable.expires_at, sql `now()`))))
|
|
@@ -465,6 +642,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
465
642
|
status: STEP_STATUS_FAILED,
|
|
466
643
|
error,
|
|
467
644
|
finished_at: sql `now()`,
|
|
645
|
+
updated_at: sql `now()`,
|
|
468
646
|
})
|
|
469
647
|
.from(this.tables.jobsTable)
|
|
470
648
|
.where(and(eq(this.tables.jobStepsTable.job_id, this.tables.jobsTable.id), eq(this.tables.jobStepsTable.id, stepId), eq(this.tables.jobStepsTable.status, STEP_STATUS_ACTIVE), eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE)))
|
|
@@ -488,6 +666,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
488
666
|
'delayedMs', ${delayMs}::integer
|
|
489
667
|
)
|
|
490
668
|
)`,
|
|
669
|
+
updated_at: sql `now()`,
|
|
491
670
|
})
|
|
492
671
|
.from(jobsTable)
|
|
493
672
|
.where(and(eq(jobStepsTable.job_id, jobsTable.id), eq(jobStepsTable.id, stepId), eq(jobStepsTable.status, STEP_STATUS_ACTIVE), eq(jobsTable.status, JOB_STATUS_ACTIVE)))
|
|
@@ -500,6 +679,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
500
679
|
.set({
|
|
501
680
|
status: STEP_STATUS_CANCELLED,
|
|
502
681
|
finished_at: sql `now()`,
|
|
682
|
+
updated_at: sql `now()`,
|
|
503
683
|
})
|
|
504
684
|
.from(this.tables.jobsTable)
|
|
505
685
|
.where(and(eq(this.tables.jobStepsTable.job_id, this.tables.jobsTable.id), eq(this.tables.jobStepsTable.id, stepId), eq(this.tables.jobStepsTable.status, STEP_STATUS_ACTIVE), or(eq(this.tables.jobsTable.status, JOB_STATUS_ACTIVE), eq(this.tables.jobsTable.status, JOB_STATUS_CANCELLED))))
|
|
@@ -531,7 +711,7 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
531
711
|
return job ?? null;
|
|
532
712
|
}
|
|
533
713
|
async _getJobSteps(options) {
|
|
534
|
-
const { jobId,
|
|
714
|
+
const { jobId, search } = options;
|
|
535
715
|
const jobStepsTable = this.tables.jobStepsTable;
|
|
536
716
|
const fuzzySearch = search?.trim();
|
|
537
717
|
const where = and(eq(jobStepsTable.job_id, jobId), fuzzySearch && fuzzySearch.length > 0
|
|
@@ -539,19 +719,12 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
539
719
|
: undefined, options.updatedAfter
|
|
540
720
|
? sql `date_trunc('milliseconds', ${jobStepsTable.updated_at}) > ${options.updatedAfter.toISOString()}::timestamptz`
|
|
541
721
|
: undefined);
|
|
542
|
-
const total = await this.db.$count(jobStepsTable, where);
|
|
543
|
-
if (!total) {
|
|
544
|
-
return {
|
|
545
|
-
steps: [],
|
|
546
|
-
total: 0,
|
|
547
|
-
page,
|
|
548
|
-
pageSize,
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
722
|
const steps = await this.db
|
|
552
723
|
.select({
|
|
553
724
|
id: jobStepsTable.id,
|
|
554
725
|
jobId: jobStepsTable.job_id,
|
|
726
|
+
parentStepId: jobStepsTable.parent_step_id,
|
|
727
|
+
parallel: jobStepsTable.parallel,
|
|
555
728
|
name: jobStepsTable.name,
|
|
556
729
|
status: jobStepsTable.status,
|
|
557
730
|
error: jobStepsTable.error,
|
|
@@ -568,14 +741,10 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
568
741
|
})
|
|
569
742
|
.from(jobStepsTable)
|
|
570
743
|
.where(where)
|
|
571
|
-
.orderBy(asc(jobStepsTable.created_at))
|
|
572
|
-
.limit(pageSize)
|
|
573
|
-
.offset((page - 1) * pageSize);
|
|
744
|
+
.orderBy(asc(jobStepsTable.created_at));
|
|
574
745
|
return {
|
|
575
746
|
steps,
|
|
576
|
-
total,
|
|
577
|
-
page,
|
|
578
|
-
pageSize,
|
|
747
|
+
total: steps.length,
|
|
579
748
|
};
|
|
580
749
|
}
|
|
581
750
|
_buildJobsWhereClause(filters) {
|
|
@@ -686,6 +855,8 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
686
855
|
.select({
|
|
687
856
|
id: this.tables.jobStepsTable.id,
|
|
688
857
|
jobId: this.tables.jobStepsTable.job_id,
|
|
858
|
+
parentStepId: this.tables.jobStepsTable.parent_step_id,
|
|
859
|
+
parallel: this.tables.jobStepsTable.parallel,
|
|
689
860
|
name: this.tables.jobStepsTable.name,
|
|
690
861
|
output: this.tables.jobStepsTable.output,
|
|
691
862
|
status: this.tables.jobStepsTable.status,
|
|
@@ -759,6 +930,85 @@ export class PostgresBaseAdapter extends Adapter {
|
|
|
759
930
|
})),
|
|
760
931
|
};
|
|
761
932
|
}
|
|
933
|
+
async _insertMetrics(metrics) {
|
|
934
|
+
if (metrics.length === 0) {
|
|
935
|
+
return 0;
|
|
936
|
+
}
|
|
937
|
+
const values = metrics.map((m) => ({
|
|
938
|
+
job_id: m.jobId,
|
|
939
|
+
step_id: m.stepId ?? null,
|
|
940
|
+
name: m.name,
|
|
941
|
+
value: m.value,
|
|
942
|
+
attributes: m.attributes ?? {},
|
|
943
|
+
type: m.type,
|
|
944
|
+
}));
|
|
945
|
+
const result = await this.db
|
|
946
|
+
.insert(this.tables.metricsTable)
|
|
947
|
+
.values(values)
|
|
948
|
+
.returning({ id: this.tables.metricsTable.id });
|
|
949
|
+
return result.length;
|
|
950
|
+
}
|
|
951
|
+
async _getMetrics(options) {
|
|
952
|
+
const metricsTable = this.tables.metricsTable;
|
|
953
|
+
const filters = options.filters ?? {};
|
|
954
|
+
const where = this._buildMetricsWhereClause(options.jobId, options.stepId, filters);
|
|
955
|
+
const sortInput = options.sort ?? { field: 'timestamp', order: 'desc' };
|
|
956
|
+
const sortFieldMap = {
|
|
957
|
+
name: metricsTable.name,
|
|
958
|
+
value: metricsTable.value,
|
|
959
|
+
timestamp: metricsTable.timestamp,
|
|
960
|
+
createdAt: metricsTable.created_at,
|
|
961
|
+
};
|
|
962
|
+
const total = await this.db.$count(metricsTable, where);
|
|
963
|
+
if (!total) {
|
|
964
|
+
return {
|
|
965
|
+
metrics: [],
|
|
966
|
+
total: 0,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
const sortField = sortFieldMap[sortInput.field];
|
|
970
|
+
const orderByClause = sortInput.order === 'asc' ? asc(sortField) : desc(sortField);
|
|
971
|
+
const metrics = await this.db
|
|
972
|
+
.select({
|
|
973
|
+
id: metricsTable.id,
|
|
974
|
+
jobId: metricsTable.job_id,
|
|
975
|
+
stepId: metricsTable.step_id,
|
|
976
|
+
name: metricsTable.name,
|
|
977
|
+
value: metricsTable.value,
|
|
978
|
+
attributes: metricsTable.attributes,
|
|
979
|
+
type: metricsTable.type,
|
|
980
|
+
timestamp: metricsTable.timestamp,
|
|
981
|
+
createdAt: metricsTable.created_at,
|
|
982
|
+
})
|
|
983
|
+
.from(metricsTable)
|
|
984
|
+
.where(where)
|
|
985
|
+
.orderBy(orderByClause);
|
|
986
|
+
return {
|
|
987
|
+
metrics,
|
|
988
|
+
total,
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
async _deleteMetrics(options) {
|
|
992
|
+
const result = await this.db
|
|
993
|
+
.delete(this.tables.metricsTable)
|
|
994
|
+
.where(eq(this.tables.metricsTable.job_id, options.jobId))
|
|
995
|
+
.returning({ id: this.tables.metricsTable.id });
|
|
996
|
+
return result.length;
|
|
997
|
+
}
|
|
998
|
+
_buildMetricsWhereClause(jobId, stepId, filters) {
|
|
999
|
+
const metricsTable = this.tables.metricsTable;
|
|
1000
|
+
return and(jobId ? eq(metricsTable.job_id, jobId) : undefined, stepId ? eq(metricsTable.step_id, stepId) : undefined, filters?.name
|
|
1001
|
+
? Array.isArray(filters.name)
|
|
1002
|
+
? or(...filters.name.map((n) => ilike(metricsTable.name, `%${n}%`)))
|
|
1003
|
+
: ilike(metricsTable.name, `%${filters.name}%`)
|
|
1004
|
+
: undefined, filters?.type
|
|
1005
|
+
? inArray(metricsTable.type, Array.isArray(filters.type) ? filters.type : [filters.type])
|
|
1006
|
+
: undefined, filters?.timestampRange && filters.timestampRange.length === 2
|
|
1007
|
+
? between(metricsTable.timestamp, filters.timestampRange[0], filters.timestampRange[1])
|
|
1008
|
+
: undefined, ...(filters?.attributesFilter && Object.keys(filters.attributesFilter).length > 0
|
|
1009
|
+
? this.#buildJsonbWhereConditions(filters.attributesFilter, metricsTable.attributes)
|
|
1010
|
+
: []));
|
|
1011
|
+
}
|
|
762
1012
|
#buildJsonbWhereConditions(filter, jsonbColumn) {
|
|
763
1013
|
const conditions = [];
|
|
764
1014
|
for (const [key, value] of Object.entries(filter)) {
|