@adonis-agora/durable 0.49.0 → 0.50.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/CHANGELOG.md +261 -0
- package/README.md +53 -22
- package/dist/assets/spa/assets/index-DupjrK7N.js +16 -0
- package/dist/assets/spa/assets/index-DupjrK7N.js.map +1 -0
- package/dist/assets/spa/index.html +1 -1
- package/dist/commands/durable_worker.d.ts.map +1 -1
- package/dist/commands/durable_worker.js +9 -8
- package/dist/commands/durable_worker.js.map +1 -1
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +1 -0
- package/dist/configure.js.map +1 -1
- package/dist/providers/dashboard_provider.d.ts +2 -0
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +50 -6
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/providers/durable_provider.d.ts.map +1 -1
- package/dist/providers/durable_provider.js +143 -34
- package/dist/providers/durable_provider.js.map +1 -1
- package/dist/src/admission-redis/redis-admission-backend.d.ts.map +1 -1
- package/dist/src/admission-redis/redis-admission-backend.js +3 -1
- package/dist/src/admission-redis/redis-admission-backend.js.map +1 -1
- package/dist/src/ambient-step.d.ts +24 -0
- package/dist/src/ambient-step.d.ts.map +1 -0
- package/dist/src/ambient-step.js +42 -0
- package/dist/src/ambient-step.js.map +1 -0
- package/dist/src/base-workflow.d.ts +10 -1
- package/dist/src/base-workflow.d.ts.map +1 -1
- package/dist/src/base-workflow.js.map +1 -1
- package/dist/src/codec-state-store.d.ts +28 -2
- package/dist/src/codec-state-store.d.ts.map +1 -1
- package/dist/src/codec-state-store.js +61 -3
- package/dist/src/codec-state-store.js.map +1 -1
- package/dist/src/commands/worker.d.ts +4 -1
- package/dist/src/commands/worker.d.ts.map +1 -1
- package/dist/src/commands/worker.js +27 -3
- package/dist/src/commands/worker.js.map +1 -1
- package/dist/src/config_types.d.ts +6 -0
- package/dist/src/config_types.d.ts.map +1 -1
- package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
- package/dist/src/control-plane-redis/redis-control-plane.js +22 -6
- package/dist/src/control-plane-redis/redis-control-plane.js.map +1 -1
- package/dist/src/control-planes/factory.d.ts +6 -0
- package/dist/src/control-planes/factory.d.ts.map +1 -1
- package/dist/src/control-planes/factory.js +1 -0
- package/dist/src/control-planes/factory.js.map +1 -1
- package/dist/src/dashboard/gateway-adapter.d.ts +15 -5
- package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -1
- package/dist/src/dashboard/gateway-adapter.js +71 -7
- package/dist/src/dashboard/gateway-adapter.js.map +1 -1
- package/dist/src/dashboard/handlers.d.ts +53 -1
- package/dist/src/dashboard/handlers.d.ts.map +1 -1
- package/dist/src/dashboard/handlers.js +200 -20
- package/dist/src/dashboard/handlers.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +2 -2
- package/dist/src/dashboard/index.d.ts.map +1 -1
- package/dist/src/dashboard/index.js +2 -2
- package/dist/src/dashboard/index.js.map +1 -1
- package/dist/src/dashboard/openapi.d.ts.map +1 -1
- package/dist/src/dashboard/openapi.js +71 -1
- package/dist/src/dashboard/openapi.js.map +1 -1
- package/dist/src/define_config.d.ts +122 -8
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics-bridge.d.ts +16 -3
- package/dist/src/diagnostics-bridge.d.ts.map +1 -1
- package/dist/src/diagnostics-bridge.js +9 -3
- package/dist/src/diagnostics-bridge.js.map +1 -1
- package/dist/src/discovery-roots.d.ts +92 -0
- package/dist/src/discovery-roots.d.ts.map +1 -0
- package/dist/src/discovery-roots.js +150 -0
- package/dist/src/discovery-roots.js.map +1 -0
- package/dist/src/engine.d.ts +418 -20
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +1523 -159
- package/dist/src/engine.js.map +1 -1
- package/dist/src/entities.d.ts +36 -1
- package/dist/src/entities.d.ts.map +1 -1
- package/dist/src/entities.js +65 -4
- package/dist/src/entities.js.map +1 -1
- package/dist/src/errors.d.ts +36 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +44 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/event-trigger-bridge.d.ts +8 -0
- package/dist/src/event-trigger-bridge.d.ts.map +1 -1
- package/dist/src/event-trigger-bridge.js +24 -3
- package/dist/src/event-trigger-bridge.js.map +1 -1
- package/dist/src/handshake/announced.d.ts +27 -0
- package/dist/src/handshake/announced.d.ts.map +1 -1
- package/dist/src/handshake/announced.js +62 -0
- package/dist/src/handshake/announced.js.map +1 -1
- package/dist/src/handshake/directory.d.ts +92 -0
- package/dist/src/handshake/directory.d.ts.map +1 -0
- package/dist/src/handshake/directory.js +139 -0
- package/dist/src/handshake/directory.js.map +1 -0
- package/dist/src/hooks/barrel-sources.d.ts +28 -0
- package/dist/src/hooks/barrel-sources.d.ts.map +1 -0
- package/dist/src/hooks/barrel-sources.js +92 -0
- package/dist/src/hooks/barrel-sources.js.map +1 -0
- package/dist/src/hooks/steps.d.ts +12 -3
- package/dist/src/hooks/steps.d.ts.map +1 -1
- package/dist/src/hooks/steps.js +7 -11
- package/dist/src/hooks/steps.js.map +1 -1
- package/dist/src/hooks/workflows.d.ts +12 -3
- package/dist/src/hooks/workflows.d.ts.map +1 -1
- package/dist/src/hooks/workflows.js +7 -13
- package/dist/src/hooks/workflows.js.map +1 -1
- package/dist/src/index.d.ts +12 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces.d.ts +309 -21
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/interfaces.js +10 -0
- package/dist/src/interfaces.js.map +1 -1
- package/dist/src/metrics.d.ts.map +1 -1
- package/dist/src/metrics.js +5 -0
- package/dist/src/metrics.js.map +1 -1
- package/dist/src/namespaces.d.ts +17 -0
- package/dist/src/namespaces.d.ts.map +1 -0
- package/dist/src/namespaces.js +24 -0
- package/dist/src/namespaces.js.map +1 -0
- package/dist/src/otel/durable-metrics.d.ts.map +1 -1
- package/dist/src/otel/durable-metrics.js +5 -0
- package/dist/src/otel/durable-metrics.js.map +1 -1
- package/dist/src/otel/durable-otel.d.ts.map +1 -1
- package/dist/src/otel/durable-otel.js +5 -0
- package/dist/src/otel/durable-otel.js.map +1 -1
- package/dist/src/pollers.d.ts +10 -1
- package/dist/src/pollers.d.ts.map +1 -1
- package/dist/src/pollers.js +15 -5
- package/dist/src/pollers.js.map +1 -1
- package/dist/src/protocol.d.ts +12 -0
- package/dist/src/protocol.d.ts.map +1 -1
- package/dist/src/protocol.js +27 -1
- package/dist/src/protocol.js.map +1 -1
- package/dist/src/remote-event.d.ts +6 -0
- package/dist/src/remote-event.d.ts.map +1 -0
- package/dist/src/remote-event.js +19 -0
- package/dist/src/remote-event.js.map +1 -0
- package/dist/src/remote-workflow-executor.d.ts +34 -12
- package/dist/src/remote-workflow-executor.d.ts.map +1 -1
- package/dist/src/remote-workflow-executor.js +42 -40
- package/dist/src/remote-workflow-executor.js.map +1 -1
- package/dist/src/run-detail-summary.d.ts +90 -3
- package/dist/src/run-detail-summary.d.ts.map +1 -1
- package/dist/src/run-detail-summary.js +360 -2
- package/dist/src/run-detail-summary.js.map +1 -1
- package/dist/src/run-facets.d.ts +48 -0
- package/dist/src/run-facets.d.ts.map +1 -0
- package/dist/src/run-facets.js +100 -0
- package/dist/src/run-facets.js.map +1 -0
- package/dist/src/run-gateway/interface.d.ts +27 -1
- package/dist/src/run-gateway/interface.d.ts.map +1 -1
- package/dist/src/run-gateway/proxy-run-gateway.d.ts +32 -4
- package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -1
- package/dist/src/run-gateway/proxy-run-gateway.js +101 -5
- package/dist/src/run-gateway/proxy-run-gateway.js.map +1 -1
- package/dist/src/run-gateway/run-request-responder.d.ts +18 -5
- package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -1
- package/dist/src/run-gateway/run-request-responder.js +155 -32
- package/dist/src/run-gateway/run-request-responder.js.map +1 -1
- package/dist/src/run-gateway/store-run-gateway.d.ts +23 -1
- package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -1
- package/dist/src/run-gateway/store-run-gateway.js +38 -0
- package/dist/src/run-gateway/store-run-gateway.js.map +1 -1
- package/dist/src/search-attributes-schema.d.ts +44 -0
- package/dist/src/search-attributes-schema.d.ts.map +1 -0
- package/dist/src/search-attributes-schema.js +51 -0
- package/dist/src/search-attributes-schema.js.map +1 -0
- package/dist/src/singleton-admission.d.ts +75 -0
- package/dist/src/singleton-admission.d.ts.map +1 -0
- package/dist/src/singleton-admission.js +156 -0
- package/dist/src/singleton-admission.js.map +1 -0
- package/dist/src/singleton-gate.d.ts +23 -8
- package/dist/src/singleton-gate.d.ts.map +1 -1
- package/dist/src/singleton-gate.js +59 -12
- package/dist/src/singleton-gate.js.map +1 -1
- package/dist/src/standard-schema.d.ts +83 -0
- package/dist/src/standard-schema.d.ts.map +1 -0
- package/dist/src/standard-schema.js +31 -0
- package/dist/src/standard-schema.js.map +1 -0
- package/dist/src/step-discovery.d.ts +21 -11
- package/dist/src/step-discovery.d.ts.map +1 -1
- package/dist/src/step-discovery.js +86 -45
- package/dist/src/step-discovery.js.map +1 -1
- package/dist/src/step-ref.d.ts +38 -12
- package/dist/src/step-ref.d.ts.map +1 -1
- package/dist/src/step-ref.js +5 -13
- package/dist/src/step-ref.js.map +1 -1
- package/dist/src/stores/factory.d.ts +6 -0
- package/dist/src/stores/factory.d.ts.map +1 -1
- package/dist/src/stores/factory.js +4 -1
- package/dist/src/stores/factory.js.map +1 -1
- package/dist/src/stores/lucid-helpers.d.ts +2 -0
- package/dist/src/stores/lucid-helpers.d.ts.map +1 -1
- package/dist/src/stores/lucid-helpers.js +7 -0
- package/dist/src/stores/lucid-helpers.js.map +1 -1
- package/dist/src/stores/lucid-mappers.d.ts +25 -0
- package/dist/src/stores/lucid-mappers.d.ts.map +1 -1
- package/dist/src/stores/lucid-mappers.js +81 -18
- package/dist/src/stores/lucid-mappers.js.map +1 -1
- package/dist/src/stores/lucid-schema.d.ts +59 -1
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +271 -25
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +61 -2
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +346 -41
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/telescope/dashboard.d.ts.map +1 -1
- package/dist/src/telescope/dashboard.js +30 -1
- package/dist/src/telescope/dashboard.js.map +1 -1
- package/dist/src/telescope/data-providers.d.ts +19 -1
- package/dist/src/telescope/data-providers.d.ts.map +1 -1
- package/dist/src/telescope/data-providers.js +119 -3
- package/dist/src/telescope/data-providers.js.map +1 -1
- package/dist/src/telescope/extension.d.ts.map +1 -1
- package/dist/src/telescope/extension.js +2 -1
- package/dist/src/telescope/extension.js.map +1 -1
- package/dist/src/telescope/index.d.ts +1 -1
- package/dist/src/telescope/index.d.ts.map +1 -1
- package/dist/src/telescope/index.js +1 -1
- package/dist/src/telescope/index.js.map +1 -1
- package/dist/src/testing/in-memory-state-store.d.ts +44 -15
- package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-state-store.js +204 -41
- package/dist/src/testing/in-memory-state-store.js.map +1 -1
- package/dist/src/testing-kit/checkpoint-aggregation-contract.d.ts +16 -0
- package/dist/src/testing-kit/checkpoint-aggregation-contract.d.ts.map +1 -0
- package/dist/src/testing-kit/checkpoint-aggregation-contract.js +228 -0
- package/dist/src/testing-kit/checkpoint-aggregation-contract.js.map +1 -0
- package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.js +355 -3
- package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
- package/dist/src/transport-pool.d.ts +15 -1
- package/dist/src/transport-pool.d.ts.map +1 -1
- package/dist/src/transport-pool.js +50 -4
- package/dist/src/transport-pool.js.map +1 -1
- package/dist/src/transports/adaptive-concurrency.d.ts +168 -0
- package/dist/src/transports/adaptive-concurrency.d.ts.map +1 -0
- package/dist/src/transports/adaptive-concurrency.js +353 -0
- package/dist/src/transports/adaptive-concurrency.js.map +1 -0
- package/dist/src/transports/bullmq/adaptive-concurrency.d.ts +1 -143
- package/dist/src/transports/bullmq/adaptive-concurrency.d.ts.map +1 -1
- package/dist/src/transports/bullmq/adaptive-concurrency.js +3 -311
- package/dist/src/transports/bullmq/adaptive-concurrency.js.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts +49 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.js +330 -38
- package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
- package/dist/src/transports/bullmq/deps.d.ts +18 -2
- package/dist/src/transports/bullmq/deps.d.ts.map +1 -1
- package/dist/src/transports/bullmq/deps.js +44 -2
- package/dist/src/transports/bullmq/deps.js.map +1 -1
- package/dist/src/transports/db-helpers.d.ts +39 -0
- package/dist/src/transports/db-helpers.d.ts.map +1 -1
- package/dist/src/transports/db-helpers.js +73 -0
- package/dist/src/transports/db-helpers.js.map +1 -1
- package/dist/src/transports/db-remote.d.ts +53 -0
- package/dist/src/transports/db-remote.d.ts.map +1 -0
- package/dist/src/transports/db-remote.js +147 -0
- package/dist/src/transports/db-remote.js.map +1 -0
- package/dist/src/transports/db-schema.d.ts +44 -1
- package/dist/src/transports/db-schema.d.ts.map +1 -1
- package/dist/src/transports/db-schema.js +227 -88
- package/dist/src/transports/db-schema.js.map +1 -1
- package/dist/src/transports/db.d.ts +189 -6
- package/dist/src/transports/db.d.ts.map +1 -1
- package/dist/src/transports/db.js +1291 -137
- package/dist/src/transports/db.js.map +1 -1
- package/dist/src/transports/event-emitter.d.ts +8 -0
- package/dist/src/transports/event-emitter.d.ts.map +1 -1
- package/dist/src/transports/event-emitter.js +36 -3
- package/dist/src/transports/event-emitter.js.map +1 -1
- package/dist/src/transports/factory.d.ts +110 -5
- package/dist/src/transports/factory.d.ts.map +1 -1
- package/dist/src/transports/factory.js +56 -9
- package/dist/src/transports/factory.js.map +1 -1
- package/dist/src/transports/queue-mock-adapter.d.ts +7 -2
- package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -1
- package/dist/src/transports/queue-mock-adapter.js +18 -2
- package/dist/src/transports/queue-mock-adapter.js.map +1 -1
- package/dist/src/transports/queue-remote.d.ts +146 -0
- package/dist/src/transports/queue-remote.d.ts.map +1 -0
- package/dist/src/transports/queue-remote.js +398 -0
- package/dist/src/transports/queue-remote.js.map +1 -0
- package/dist/src/transports/queue.d.ts +159 -6
- package/dist/src/transports/queue.d.ts.map +1 -1
- package/dist/src/transports/queue.js +747 -37
- package/dist/src/transports/queue.js.map +1 -1
- package/dist/src/transports/task-failure.d.ts +31 -0
- package/dist/src/transports/task-failure.d.ts.map +1 -0
- package/dist/src/transports/task-failure.js +50 -0
- package/dist/src/transports/task-failure.js.map +1 -0
- package/dist/src/workflow-als.d.ts +1 -1
- package/dist/src/workflow-als.d.ts.map +1 -1
- package/dist/src/workflow-ctx.d.ts +10 -0
- package/dist/src/workflow-ctx.d.ts.map +1 -1
- package/dist/src/workflow-ctx.js +97 -15
- package/dist/src/workflow-ctx.js.map +1 -1
- package/dist/src/workflow-discovery.d.ts +32 -41
- package/dist/src/workflow-discovery.d.ts.map +1 -1
- package/dist/src/workflow-discovery.js +65 -88
- package/dist/src/workflow-discovery.js.map +1 -1
- package/dist/src/workflow-ref.d.ts +24 -0
- package/dist/src/workflow-ref.d.ts.map +1 -1
- package/dist/src/workflow-ref.js.map +1 -1
- package/dist/stubs/config/durable.stub +6 -0
- package/dist/stubs/config/durable_dashboard.stub +7 -3
- package/dist/stubs/database/migrations/create_durable_tables.stub +2 -1
- package/dist/stubs/database/migrations/create_durable_transport_messages.stub +43 -0
- package/dist/stubs/database/migrations/create_durable_transport_tables.stub +12 -9
- package/dist/stubs/database/migrations/create_durable_transport_worker_keys.stub +1 -1
- package/dist/stubs/make/workflow/main.stub +1 -1
- package/package.json +6 -4
- package/skills/durable-cluster/SKILL.md +80 -11
- package/skills/durable-determinism/SKILL.md +11 -5
- package/skills/durable-reliability/SKILL.md +77 -37
- package/skills/durable-setup/SKILL.md +51 -22
- package/skills/durable-transports-stores/SKILL.md +130 -19
- package/skills/durable-workflows/SKILL.md +71 -18
- package/dist/assets/spa/assets/index-DdB9zxuX.js +0 -16
- package/dist/assets/spa/assets/index-DdB9zxuX.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lucid.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAKzD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"lucid.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"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"}
|
package/dist/src/stores/lucid.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { namespaceFilterValues, normalizeNamespace } from '../namespaces.js';
|
|
2
|
+
import { mergeRunFacetRows } from '../run-facets.js';
|
|
1
3
|
import { runPageWindow } from '../run-pagination.js';
|
|
2
4
|
import { axisIsRunColumn, mergeRunValueFacetRows, RUN_VALUE_FACET_LIMIT, scanRunValueFacets, } from '../run-value-facets.js';
|
|
3
5
|
import { attributeColumnFor, attributeOperand, attributeOperands, normalizeAttributeRows, sqlComparator, } from '../search-attributes.js';
|
|
4
|
-
import {
|
|
6
|
+
import { SINGLETON_ACTIVE_STATUSES, SINGLETON_ADMITTED_TAG, settlesTerminally, singletonAdmissionDenialPatch, singletonAdmissionTags, withoutAdmission, } from '../singleton-admission.js';
|
|
7
|
+
import { checkpointToRow, parseJsonColumn, rowToCheckpoint, rowToRun, rowToSignalWaiter, runPatchToRow, runToRow, } from './lucid-mappers.js';
|
|
5
8
|
import { createDurableTables, DURABLE_TABLES } from './lucid-schema.js';
|
|
6
9
|
/**
|
|
7
10
|
* A production-grade, persistent `StateStore` backed by AdonisJS **Lucid** (Knex). Runs, checkpoints,
|
|
@@ -17,9 +20,28 @@ import { createDurableTables, DURABLE_TABLES } from './lucid-schema.js';
|
|
|
17
20
|
export class LucidStateStore {
|
|
18
21
|
db;
|
|
19
22
|
connectionName;
|
|
23
|
+
options;
|
|
24
|
+
/** The namespace run reads are fenced to, or undefined for the operator view. */
|
|
25
|
+
scopeNamespace;
|
|
20
26
|
constructor(db, options = {}) {
|
|
21
27
|
this.db = db;
|
|
22
28
|
this.connectionName = options.connectionName;
|
|
29
|
+
this.options = options;
|
|
30
|
+
this.scopeNamespace = options.scope?.namespace;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A store over the same `Database` whose run reads are confined to `scope.namespace` (see
|
|
34
|
+
* {@link LucidStateStoreOptions.scope}). `{ namespace: undefined }` is the unscoped operator view.
|
|
35
|
+
*/
|
|
36
|
+
withScope(scope) {
|
|
37
|
+
return new LucidStateStore(this.db, { ...this.options, scope });
|
|
38
|
+
}
|
|
39
|
+
/** Fence a runs-table read to the store's scope (no-op for the operator view). */
|
|
40
|
+
fenced(q) {
|
|
41
|
+
if (this.scopeNamespace !== undefined) {
|
|
42
|
+
q.whereIn('namespace', namespaceFilterValues(this.scopeNamespace));
|
|
43
|
+
}
|
|
44
|
+
return q;
|
|
23
45
|
}
|
|
24
46
|
/** The base query client (or a named connection's), used unless an explicit tx client is passed. */
|
|
25
47
|
client() {
|
|
@@ -27,7 +49,7 @@ export class LucidStateStore {
|
|
|
27
49
|
}
|
|
28
50
|
/** Idempotently provision the durable tables (called on boot when `autoSchema` is on). */
|
|
29
51
|
async ensureSchema() {
|
|
30
|
-
await createDurableTables(this.db, this.connectionName);
|
|
52
|
+
await createDurableTables(this.db, this.connectionName, this.options.collation !== undefined ? { collation: this.options.collation } : {});
|
|
31
53
|
}
|
|
32
54
|
// --- runs ---------------------------------------------------------------
|
|
33
55
|
async createRun(run) {
|
|
@@ -37,8 +59,8 @@ export class LucidStateStore {
|
|
|
37
59
|
});
|
|
38
60
|
}
|
|
39
61
|
async updateRun(runId, patch) {
|
|
40
|
-
const row = runPatchToRow(patch);
|
|
41
62
|
await this.client().transaction(async (trx) => {
|
|
63
|
+
const row = await this.releaseSlot(trx, runId, patch);
|
|
42
64
|
// Knex throws on an empty `.update({})`; skip the UPDATE when no mapped column changed.
|
|
43
65
|
if (Object.keys(row).length) {
|
|
44
66
|
await trx.from(DURABLE_TABLES.runs).where('id', runId).update(row);
|
|
@@ -49,8 +71,8 @@ export class LucidStateStore {
|
|
|
49
71
|
});
|
|
50
72
|
}
|
|
51
73
|
async updateRunIf(runId, expectedStatuses, patch) {
|
|
52
|
-
const row = runPatchToRow(patch);
|
|
53
74
|
return this.client().transaction(async (trx) => {
|
|
75
|
+
const row = await this.releaseSlot(trx, runId, patch);
|
|
54
76
|
let applied;
|
|
55
77
|
if (Object.keys(row).length) {
|
|
56
78
|
// The status predicate lives IN the UPDATE's WHERE clause, alongside `updateRun`'s existing
|
|
@@ -79,8 +101,89 @@ export class LucidStateStore {
|
|
|
79
101
|
return applied;
|
|
80
102
|
});
|
|
81
103
|
}
|
|
104
|
+
/** Whether the runs table has `awaiting_decision_task_id`, probed once per store and cached. */
|
|
105
|
+
decisionMarkerColumn;
|
|
106
|
+
hasDecisionMarkerColumn(trx) {
|
|
107
|
+
// Probed on the caller's transaction: a single-connection pool (SQLite) has no other to use.
|
|
108
|
+
this.decisionMarkerColumn ??= trx.schema
|
|
109
|
+
.hasColumn(DURABLE_TABLES.runs, 'awaiting_decision_task_id')
|
|
110
|
+
.catch(() => {
|
|
111
|
+
this.decisionMarkerColumn = undefined; // a failed probe is retried, never cached
|
|
112
|
+
return true;
|
|
113
|
+
});
|
|
114
|
+
return this.decisionMarkerColumn;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The row patch for `patch`, plus — when it settles the run terminally — the run's tags without the
|
|
118
|
+
* singleton admission marker, so a terminal settle releases the slot. The current tags are read
|
|
119
|
+
* under a row lock (Postgres/MySQL) so an admission committing concurrently can't be overwritten
|
|
120
|
+
* with a stale tag list.
|
|
121
|
+
*/
|
|
122
|
+
async releaseSlot(trx, runId, patch) {
|
|
123
|
+
const row = runPatchToRow(patch);
|
|
124
|
+
// A patch CLEARING the remote-workflow decision marker (cancel, requeue, settle) on a schema
|
|
125
|
+
// that predates the column (`autoSchema: false`, an old migration): leave it out, as `runToRow`
|
|
126
|
+
// does on insert — there is nothing to clear. Setting it still needs the column.
|
|
127
|
+
if ('awaiting_decision_task_id' in row &&
|
|
128
|
+
row.awaiting_decision_task_id == null &&
|
|
129
|
+
!(await this.hasDecisionMarkerColumn(trx))) {
|
|
130
|
+
delete row.awaiting_decision_task_id;
|
|
131
|
+
}
|
|
132
|
+
if (!settlesTerminally(patch))
|
|
133
|
+
return row;
|
|
134
|
+
let tags;
|
|
135
|
+
if ('tags' in patch) {
|
|
136
|
+
tags = patch.tags;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const q = trx.from(DURABLE_TABLES.runs).where('id', runId).select('tags');
|
|
140
|
+
if (locksRows(trx))
|
|
141
|
+
q.forUpdate();
|
|
142
|
+
const current = (await q.first());
|
|
143
|
+
tags = parseJsonColumn(current?.tags, `${DURABLE_TABLES.runs}.tags`, runId);
|
|
144
|
+
}
|
|
145
|
+
const released = withoutAdmission(tags);
|
|
146
|
+
return released ? { ...row, ...runPatchToRow({ tags: released }) } : row;
|
|
147
|
+
}
|
|
148
|
+
async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
|
|
149
|
+
return this.client().transaction(async (trx) => {
|
|
150
|
+
// Every active run sharing the key. The tag LIKE may over-match (it is only a lock-scope
|
|
151
|
+
// narrowing); the decision re-filters exactly.
|
|
152
|
+
const sharing = () => trx
|
|
153
|
+
.from(DURABLE_TABLES.runs)
|
|
154
|
+
.where('workflow', workflow)
|
|
155
|
+
.whereIn('status', SINGLETON_ACTIVE_STATUSES)
|
|
156
|
+
.whereRaw(`tags like ? escape '!'`, [`%"${escapeLike(tag)}"%`]);
|
|
157
|
+
let rows;
|
|
158
|
+
if (locksRows(trx)) {
|
|
159
|
+
// Lock the key's active rows in a stable order (no lock-order deadlock between two claims).
|
|
160
|
+
// A competing claim blocks here until this one commits, then reads the committed holder.
|
|
161
|
+
rows = (await sharing().orderBy('id', 'asc').forUpdate());
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// SQLite has no row locks: take the database write lock FIRST with a no-op write, then read,
|
|
165
|
+
// so no other connection or process can claim between this read and the write below.
|
|
166
|
+
await sharing().update({ workflow });
|
|
167
|
+
rows = (await sharing());
|
|
168
|
+
}
|
|
169
|
+
const runs = rows.map(rowToRun);
|
|
170
|
+
const tags = singletonAdmissionTags(runs, runId, tag, workflow, limit);
|
|
171
|
+
if (!tags) {
|
|
172
|
+
const parked = singletonAdmissionDenialPatch(runs, runId, tag, workflow, retryWakeAt);
|
|
173
|
+
if (parked) {
|
|
174
|
+
await trx.from(DURABLE_TABLES.runs).where('id', runId).update(runPatchToRow(parked));
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
// Already a holder (a resumed turn): nothing to write.
|
|
179
|
+
if (!runs.find((r) => r.id === runId)?.tags?.includes(SINGLETON_ADMITTED_TAG)) {
|
|
180
|
+
await trx.from(DURABLE_TABLES.runs).where('id', runId).update(runPatchToRow({ tags }));
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
82
185
|
async getRun(runId) {
|
|
83
|
-
const row = await this.client().from(DURABLE_TABLES.runs).where('id', runId).first();
|
|
186
|
+
const row = await this.fenced(this.client().from(DURABLE_TABLES.runs).where('id', runId)).first();
|
|
84
187
|
return row ? rowToRun(row) : null;
|
|
85
188
|
}
|
|
86
189
|
async deleteRun(runId) {
|
|
@@ -94,6 +197,22 @@ export class LucidStateStore {
|
|
|
94
197
|
await trx.from(DURABLE_TABLES.runs).where('id', runId).delete();
|
|
95
198
|
});
|
|
96
199
|
}
|
|
200
|
+
async deleteRuns(runIds) {
|
|
201
|
+
const ids = [...new Set(runIds)];
|
|
202
|
+
if (ids.length === 0)
|
|
203
|
+
return;
|
|
204
|
+
// deleteRun's four statements, `IN (...)` over chunks that stay under every dialect's bind limit,
|
|
205
|
+
// all in one transaction.
|
|
206
|
+
await this.client().transaction(async (trx) => {
|
|
207
|
+
for (let at = 0; at < ids.length; at += DELETE_RUNS_CHUNK) {
|
|
208
|
+
const chunk = ids.slice(at, at + DELETE_RUNS_CHUNK);
|
|
209
|
+
await trx.from(DURABLE_TABLES.checkpoints).whereIn('run_id', chunk).delete();
|
|
210
|
+
await trx.from(DURABLE_TABLES.signalWaiters).whereIn('run_id', chunk).delete();
|
|
211
|
+
await trx.from(DURABLE_TABLES.attributes).whereIn('run_id', chunk).delete();
|
|
212
|
+
await trx.from(DURABLE_TABLES.runs).whereIn('id', chunk).delete();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
97
216
|
// --- checkpoints --------------------------------------------------------
|
|
98
217
|
async getCheckpoint(runId, seq) {
|
|
99
218
|
const row = await this.client()
|
|
@@ -107,14 +226,21 @@ export class LucidStateStore {
|
|
|
107
226
|
await this.upsertCheckpoint(this.client(), checkpoint);
|
|
108
227
|
}
|
|
109
228
|
async saveCheckpointIfPending(checkpoint) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
229
|
+
return updateCheckpointIfPending(this.client(), checkpoint);
|
|
230
|
+
}
|
|
231
|
+
async saveCheckpointsIfPending(checkpoints) {
|
|
232
|
+
if (checkpoints.length === 0)
|
|
233
|
+
return [];
|
|
234
|
+
// The conditional UPDATE of `saveCheckpointIfPending`, once per entry and in order, in one
|
|
235
|
+
// transaction: one commit for the batch, and a rejection writes nothing. In order, so a second
|
|
236
|
+
// entry for the same row is compared against the first one's write.
|
|
237
|
+
return this.client().transaction(async (trx) => {
|
|
238
|
+
const wrote = [];
|
|
239
|
+
for (const checkpoint of checkpoints) {
|
|
240
|
+
wrote.push(await updateCheckpointIfPending(trx, checkpoint));
|
|
241
|
+
}
|
|
242
|
+
return wrote;
|
|
243
|
+
});
|
|
118
244
|
}
|
|
119
245
|
/** Persist the latest worker heartbeat on the step's checkpoint row (see the interface doc).
|
|
120
246
|
* A targeted two-column UPDATE — never an upsert: a beat racing the checkpoint's own insert (or
|
|
@@ -146,31 +272,34 @@ export class LucidStateStore {
|
|
|
146
272
|
}
|
|
147
273
|
// --- recovery / dispatch queries ----------------------------------------
|
|
148
274
|
async listIncompleteRuns(namespace) {
|
|
149
|
-
const q = this.client().from(DURABLE_TABLES.runs).where('status', 'running');
|
|
275
|
+
const q = this.fenced(this.client().from(DURABLE_TABLES.runs).where('status', 'running'));
|
|
150
276
|
if (namespace !== undefined)
|
|
151
|
-
q.
|
|
277
|
+
q.whereIn('namespace', namespaceFilterValues(namespace));
|
|
152
278
|
const rows = await q;
|
|
153
279
|
return rows.map(rowToRun);
|
|
154
280
|
}
|
|
155
|
-
async listOrphanedRuns(nowMs, limit, namespace) {
|
|
281
|
+
async listOrphanedRuns(nowMs, limit, namespace, owner) {
|
|
156
282
|
// The lease predicate lives IN the query: only genuinely orphaned runs (no lease, or an expired
|
|
157
283
|
// one) come back, so a healthy fleet's recovery tick fetches ~zero rows instead of every
|
|
158
|
-
// running run — and issues ~zero lock-probe UPDATEs.
|
|
159
|
-
|
|
284
|
+
// running run — and issues ~zero lock-probe UPDATEs. `owner` adds the runs still leased to that
|
|
285
|
+
// instance id (a rebooted instance's own leftovers; the engine filters what it is executing).
|
|
286
|
+
const q = this.fenced(this.client()
|
|
160
287
|
.from(DURABLE_TABLES.runs)
|
|
161
288
|
.where('status', 'running')
|
|
162
289
|
.andWhere((sub) => {
|
|
163
290
|
sub.whereNull('locked_until').orWhere('locked_until', '<=', nowMs);
|
|
164
|
-
|
|
291
|
+
if (owner !== undefined)
|
|
292
|
+
sub.orWhere('locked_by', owner);
|
|
293
|
+
}));
|
|
165
294
|
if (namespace !== undefined)
|
|
166
|
-
q.
|
|
295
|
+
q.whereIn('namespace', namespaceFilterValues(namespace));
|
|
167
296
|
const rows = await q.orderBy('created_at', 'asc').limit(limit);
|
|
168
297
|
return rows.map(rowToRun);
|
|
169
298
|
}
|
|
170
299
|
async listPendingRuns(limit, namespace) {
|
|
171
|
-
const q = this.client().from(DURABLE_TABLES.runs).where('status', 'pending');
|
|
300
|
+
const q = this.fenced(this.client().from(DURABLE_TABLES.runs).where('status', 'pending'));
|
|
172
301
|
if (namespace !== undefined)
|
|
173
|
-
q.
|
|
302
|
+
q.whereIn('namespace', namespaceFilterValues(namespace));
|
|
174
303
|
const rows = await q
|
|
175
304
|
.orderBy('created_at', 'asc') // FIFO dispatch
|
|
176
305
|
.orderBy('id', 'asc') // stable tiebreak, mirrors the in-memory store
|
|
@@ -178,13 +307,13 @@ export class LucidStateStore {
|
|
|
178
307
|
return rows.map(rowToRun);
|
|
179
308
|
}
|
|
180
309
|
async listDueTimers(nowMs, namespace, limit) {
|
|
181
|
-
const q = this.client()
|
|
310
|
+
const q = this.fenced(this.client()
|
|
182
311
|
.from(DURABLE_TABLES.runs)
|
|
183
312
|
.where('status', 'suspended')
|
|
184
313
|
.whereNotNull('wake_at')
|
|
185
|
-
.andWhere('wake_at', '<=', nowMs);
|
|
314
|
+
.andWhere('wake_at', '<=', nowMs));
|
|
186
315
|
if (namespace !== undefined)
|
|
187
|
-
q.
|
|
316
|
+
q.whereIn('namespace', namespaceFilterValues(namespace));
|
|
188
317
|
// Most overdue first, bounded: a backlog drains over several polls instead of one unbounded
|
|
189
318
|
// fetch (served by the existing (status, wake_at) index).
|
|
190
319
|
q.orderBy('wake_at', 'asc');
|
|
@@ -344,6 +473,15 @@ export class LucidStateStore {
|
|
|
344
473
|
.table(DURABLE_TABLES.bufferedSignals)
|
|
345
474
|
.insert({ token, payload: payload === undefined ? null : JSON.stringify(payload) });
|
|
346
475
|
}
|
|
476
|
+
async listBufferedSignalTokens(prefix, limit) {
|
|
477
|
+
const rows = (await this.client()
|
|
478
|
+
.from(DURABLE_TABLES.bufferedSignals)
|
|
479
|
+
.whereRaw(`token like ? escape '!'`, [`${escapeLike(prefix)}%`])
|
|
480
|
+
.distinct('token')
|
|
481
|
+
.orderBy('token', 'asc')
|
|
482
|
+
.limit(limit));
|
|
483
|
+
return rows.map((r) => r.token);
|
|
484
|
+
}
|
|
347
485
|
async takeBufferedSignal(token) {
|
|
348
486
|
return this.client().transaction(async (trx) => {
|
|
349
487
|
const row = await trx
|
|
@@ -357,7 +495,9 @@ export class LucidStateStore {
|
|
|
357
495
|
const deleted = await trx.from(DURABLE_TABLES.bufferedSignals).where('id', r.id).delete();
|
|
358
496
|
if (rowsAffected(deleted) !== 1)
|
|
359
497
|
return null;
|
|
360
|
-
return {
|
|
498
|
+
return {
|
|
499
|
+
payload: parseJsonColumn(r.payload, `${DURABLE_TABLES.bufferedSignals}.payload`, String(r.id)),
|
|
500
|
+
};
|
|
361
501
|
});
|
|
362
502
|
}
|
|
363
503
|
// --- buffered events (name-keyed, match-based consumption) ---------------
|
|
@@ -379,10 +519,31 @@ export class LucidStateStore {
|
|
|
379
519
|
.limit(limit);
|
|
380
520
|
return rows.map((r) => ({
|
|
381
521
|
id: r.id,
|
|
382
|
-
payload: r.payload
|
|
522
|
+
payload: parseJsonColumn(r.payload, `${DURABLE_TABLES.bufferedEvents}.payload`, r.id),
|
|
383
523
|
publishedAt: Number(r.published_at),
|
|
384
524
|
}));
|
|
385
525
|
}
|
|
526
|
+
async pruneBufferedEvents(publishedBefore, limit) {
|
|
527
|
+
if (!(limit > 0))
|
|
528
|
+
return 0;
|
|
529
|
+
// Two statements, not `DELETE ... WHERE id IN (SELECT ... LIMIT n)`: MySQL rejects a LIMIT in an
|
|
530
|
+
// IN subquery. The age predicate is repeated on the DELETE so a row re-published under the same
|
|
531
|
+
// id meanwhile is left alone.
|
|
532
|
+
const rows = (await this.client()
|
|
533
|
+
.from(DURABLE_TABLES.bufferedEvents)
|
|
534
|
+
.select('id')
|
|
535
|
+
.where('published_at', '<', publishedBefore)
|
|
536
|
+
.orderBy('published_at', 'asc')
|
|
537
|
+
.limit(limit));
|
|
538
|
+
if (rows.length === 0)
|
|
539
|
+
return 0;
|
|
540
|
+
const deleted = await this.client()
|
|
541
|
+
.from(DURABLE_TABLES.bufferedEvents)
|
|
542
|
+
.whereIn('id', rows.map((r) => r.id))
|
|
543
|
+
.andWhere('published_at', '<', publishedBefore)
|
|
544
|
+
.delete();
|
|
545
|
+
return rowsAffected(deleted);
|
|
546
|
+
}
|
|
386
547
|
async removeBufferedEvent(id) {
|
|
387
548
|
// Atomic claim: whoever's delete affects the row (returns 1) is the one that gets to deliver;
|
|
388
549
|
// a concurrent claimant sees 0 and backs off. `id` is the primary key.
|
|
@@ -403,6 +564,14 @@ export class LucidStateStore {
|
|
|
403
564
|
}));
|
|
404
565
|
}
|
|
405
566
|
// --- dashboard queries --------------------------------------------------
|
|
567
|
+
/** One `GROUP BY status, origin` over the same predicates {@link listRuns} applies. */
|
|
568
|
+
async runFacets(query) {
|
|
569
|
+
const rows = (await this.scopedRuns(query)
|
|
570
|
+
.select('status', 'origin')
|
|
571
|
+
.count('* as count')
|
|
572
|
+
.groupBy('status', 'origin'));
|
|
573
|
+
return mergeRunFacetRows(rows.map((row) => ({ status: row.status, origin: row.origin, count: Number(row.count) })));
|
|
574
|
+
}
|
|
406
575
|
/** One `COUNT(*)` over the same predicates {@link listRuns} applies. */
|
|
407
576
|
async countRuns(query) {
|
|
408
577
|
const rows = await this.scopedRuns(query).count('* as count');
|
|
@@ -426,7 +595,7 @@ export class LucidStateStore {
|
|
|
426
595
|
async updateSchedule(id, patch, expectedNextFireAt) {
|
|
427
596
|
const set = {};
|
|
428
597
|
if (patch.namespace !== undefined)
|
|
429
|
-
set.namespace = patch.namespace;
|
|
598
|
+
set.namespace = normalizeNamespace(patch.namespace);
|
|
430
599
|
if (patch.workflow !== undefined)
|
|
431
600
|
set.workflow = patch.workflow;
|
|
432
601
|
if (patch.paused !== undefined)
|
|
@@ -456,8 +625,9 @@ export class LucidStateStore {
|
|
|
456
625
|
}
|
|
457
626
|
async listSchedules(query) {
|
|
458
627
|
const q = this.client().from(DURABLE_TABLES.schedules);
|
|
459
|
-
if (query.namespace !== undefined)
|
|
460
|
-
q.
|
|
628
|
+
if (query.namespace !== undefined) {
|
|
629
|
+
q.whereIn('namespace', namespaceFilterValues(query.namespace));
|
|
630
|
+
}
|
|
461
631
|
if (query.workflow !== undefined)
|
|
462
632
|
q.where('workflow', query.workflow);
|
|
463
633
|
if (query.paused !== undefined)
|
|
@@ -521,7 +691,11 @@ export class LucidStateStore {
|
|
|
521
691
|
if (needle)
|
|
522
692
|
q.andWhereRaw(`lower(${column}) like ? escape '!'`, [`%${escapeLike(needle)}%`]);
|
|
523
693
|
const rows = (await q);
|
|
524
|
-
return mergeRunValueFacetRows(rows.map((row) => ({
|
|
694
|
+
return mergeRunValueFacetRows(rows.map((row) => ({
|
|
695
|
+
// A legacy '' namespace counts as the default pool, as everywhere else.
|
|
696
|
+
value: column === 'namespace' ? normalizeNamespace(row.value) : row.value,
|
|
697
|
+
count: Number(row.count),
|
|
698
|
+
})), { ...opts, search: undefined });
|
|
525
699
|
}
|
|
526
700
|
/**
|
|
527
701
|
* The runs matching `query`'s predicates (no ordering, no paging) — one source of truth shared by
|
|
@@ -529,7 +703,7 @@ export class LucidStateStore {
|
|
|
529
703
|
* exactly the set the list would show.
|
|
530
704
|
*/
|
|
531
705
|
scopedRuns(query) {
|
|
532
|
-
const q = this.client().from(DURABLE_TABLES.runs);
|
|
706
|
+
const q = this.fenced(this.client().from(DURABLE_TABLES.runs));
|
|
533
707
|
if (query.workflow)
|
|
534
708
|
q.where('workflow', query.workflow);
|
|
535
709
|
if (query.workflows) {
|
|
@@ -539,11 +713,13 @@ export class LucidStateStore {
|
|
|
539
713
|
else
|
|
540
714
|
q.whereRaw('1 = 0');
|
|
541
715
|
}
|
|
542
|
-
if (query.namespace !== undefined)
|
|
543
|
-
q.
|
|
716
|
+
if (query.namespace !== undefined) {
|
|
717
|
+
q.whereIn('namespace', namespaceFilterValues(query.namespace));
|
|
718
|
+
}
|
|
544
719
|
if (query.namespaces) {
|
|
545
|
-
if (query.namespaces.length)
|
|
546
|
-
q.whereIn('namespace', query.namespaces);
|
|
720
|
+
if (query.namespaces.length) {
|
|
721
|
+
q.whereIn('namespace', query.namespaces.flatMap(namespaceFilterValues));
|
|
722
|
+
}
|
|
547
723
|
else
|
|
548
724
|
q.whereRaw('1 = 0');
|
|
549
725
|
}
|
|
@@ -668,6 +844,93 @@ export class LucidStateStore {
|
|
|
668
844
|
.count('* as count');
|
|
669
845
|
return Number(rows[0]?.count ?? 0);
|
|
670
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* One `GROUP BY parallel_group, status, <name kind>` over the run's checkpoints — the buckets a run
|
|
849
|
+
* detail summarizes a fan from, without loading its rows. The name-kind `CASE` is the SQL of
|
|
850
|
+
* `checkpointNameKind` (case-sensitive prefix tests, like `startsWith`), and
|
|
851
|
+
* `min(coalesce(enqueued_at, started_at))` reads `enqueuedAt` the way {@link listCheckpoints} does.
|
|
852
|
+
*/
|
|
853
|
+
async checkpointStats(runId, opts) {
|
|
854
|
+
const client = this.client();
|
|
855
|
+
const kind = nameKindSql(client.dialect.name);
|
|
856
|
+
const q = client
|
|
857
|
+
.from(DURABLE_TABLES.checkpoints)
|
|
858
|
+
.select('parallel_group as grp', 'status')
|
|
859
|
+
.select(client.raw(`${kind} as name_kind`))
|
|
860
|
+
.count('* as n')
|
|
861
|
+
.min('seq as min_seq')
|
|
862
|
+
.max('seq as max_seq')
|
|
863
|
+
.min('name as min_name')
|
|
864
|
+
.max('name as max_name')
|
|
865
|
+
.select(client.raw('min(coalesce(enqueued_at, started_at)) as min_enqueued_at'))
|
|
866
|
+
.min('started_at as min_started_at')
|
|
867
|
+
.max('started_at as max_started_at')
|
|
868
|
+
.max('finished_at as max_finished_at')
|
|
869
|
+
.sum('attempts as sum_attempts')
|
|
870
|
+
.where('run_id', runId);
|
|
871
|
+
if (opts?.group !== undefined)
|
|
872
|
+
q.andWhere('parallel_group', opts.group);
|
|
873
|
+
q.groupBy('parallel_group', 'status').groupByRaw(kind);
|
|
874
|
+
const rows = (await q);
|
|
875
|
+
// Counts, sums and bigint aggregates come back as strings on Postgres/MySQL.
|
|
876
|
+
const ms = (value) => new Date(Number(value));
|
|
877
|
+
return rows.map((row) => ({
|
|
878
|
+
group: row.grp ?? null,
|
|
879
|
+
status: row.status,
|
|
880
|
+
nameKind: row.name_kind,
|
|
881
|
+
count: Number(row.n),
|
|
882
|
+
minSeq: Number(row.min_seq),
|
|
883
|
+
maxSeq: Number(row.max_seq),
|
|
884
|
+
minName: String(row.min_name),
|
|
885
|
+
maxName: String(row.max_name),
|
|
886
|
+
minEnqueuedAt: ms(row.min_enqueued_at),
|
|
887
|
+
minStartedAt: ms(row.min_started_at),
|
|
888
|
+
maxStartedAt: ms(row.max_started_at),
|
|
889
|
+
maxFinishedAt: ms(row.max_finished_at),
|
|
890
|
+
sumAttempts: Number(row.sum_attempts),
|
|
891
|
+
}));
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* The run's checkpoints matching `query`, in `seq` order and paged in the database — a fan's
|
|
895
|
+
* failures or one page of its members without the rest of the run. An excluded group keeps the
|
|
896
|
+
* ungrouped rows (`parallel_group is null or not in (...)`); an empty `statuses`/`namePrefixes`
|
|
897
|
+
* matches nothing, so it returns before querying.
|
|
898
|
+
*/
|
|
899
|
+
async queryCheckpoints(runId, query) {
|
|
900
|
+
if (query.statuses?.length === 0 || query.namePrefixes?.length === 0)
|
|
901
|
+
return [];
|
|
902
|
+
const client = this.client();
|
|
903
|
+
const q = client.from(DURABLE_TABLES.checkpoints).where('run_id', runId);
|
|
904
|
+
if (query.group !== undefined)
|
|
905
|
+
q.andWhere('parallel_group', query.group);
|
|
906
|
+
if (query.excludeGroups?.length) {
|
|
907
|
+
const excluded = query.excludeGroups;
|
|
908
|
+
q.andWhere((sub) => {
|
|
909
|
+
sub.whereNull('parallel_group').orWhereNotIn('parallel_group', excluded);
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
if (query.statuses)
|
|
913
|
+
q.whereIn('status', query.statuses);
|
|
914
|
+
if (query.namePrefixes) {
|
|
915
|
+
const prefixes = query.namePrefixes;
|
|
916
|
+
const dialect = client.dialect.name;
|
|
917
|
+
q.andWhere((sub) => {
|
|
918
|
+
for (const prefix of prefixes) {
|
|
919
|
+
sub.orWhereRaw(prefixSql(dialect, 'name', prefix), [prefix]);
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
q.orderBy('seq', 'asc');
|
|
924
|
+
if (query.limit !== undefined)
|
|
925
|
+
q.limit(query.limit);
|
|
926
|
+
if (query.offset) {
|
|
927
|
+
// An OFFSET alone is not valid SQL on SQLite/MySQL; an unbounded page gets a limit no run reaches.
|
|
928
|
+
if (query.limit === undefined)
|
|
929
|
+
q.limit(Number.MAX_SAFE_INTEGER);
|
|
930
|
+
q.offset(query.offset);
|
|
931
|
+
}
|
|
932
|
+
return (await q).map(rowToCheckpoint);
|
|
933
|
+
}
|
|
671
934
|
// --- internals ----------------------------------------------------------
|
|
672
935
|
/** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
|
|
673
936
|
* the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
|
|
@@ -693,9 +956,49 @@ export class LucidStateStore {
|
|
|
693
956
|
*/
|
|
694
957
|
/** Rows per multi-row INSERT in {@link LucidStateStore.registerChildWaits}. */
|
|
695
958
|
const CHILD_WAIT_ROWS = 100;
|
|
959
|
+
/** Run ids per `IN (...)` in {@link LucidStateStore.deleteRuns}. */
|
|
960
|
+
const DELETE_RUNS_CHUNK = 500;
|
|
961
|
+
/**
|
|
962
|
+
* One conditional UPDATE: the `status = 'pending'` predicate is the compare, the affected-row count
|
|
963
|
+
* the answer. A missing row matches nothing, so it is never created.
|
|
964
|
+
*/
|
|
965
|
+
async function updateCheckpointIfPending(client, checkpoint) {
|
|
966
|
+
const { run_id: _runId, seq: _seq, ...row } = checkpointToRow(checkpoint);
|
|
967
|
+
const affected = await client
|
|
968
|
+
.from(DURABLE_TABLES.checkpoints)
|
|
969
|
+
.where('run_id', checkpoint.runId)
|
|
970
|
+
.andWhere('seq', checkpoint.seq)
|
|
971
|
+
.andWhere('status', 'pending')
|
|
972
|
+
.update(row);
|
|
973
|
+
return rowsAffected(affected) > 0;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* A case-sensitive "`column` starts with ?" predicate, binding the prefix once. `LIKE` would ignore
|
|
977
|
+
* case on SQLite and on MySQL's default collations (and treat `_` as a wildcard), where the JS
|
|
978
|
+
* `startsWith` this must agree with does not. MySQL compares bytes via `BINARY`.
|
|
979
|
+
*/
|
|
980
|
+
function prefixSql(dialect, column, prefix) {
|
|
981
|
+
const head = `substr(${column}, 1, ${prefix.length})`;
|
|
982
|
+
return dialect === 'mysql' ? `binary ${head} = ?` : `${head} = ?`;
|
|
983
|
+
}
|
|
984
|
+
/** The SQL `CASE` of `checkpointNameKind` over the `name` column. */
|
|
985
|
+
function nameKindSql(dialect) {
|
|
986
|
+
const starts = (prefix) => {
|
|
987
|
+
const head = `substr(name, 1, ${prefix.length})`;
|
|
988
|
+
return dialect === 'mysql' ? `binary ${head} = '${prefix}'` : `${head} = '${prefix}'`;
|
|
989
|
+
};
|
|
990
|
+
return (`case when ${starts('signal:child:')} then 'child' ` +
|
|
991
|
+
`when ${starts('fan:')} then 'folded' ` +
|
|
992
|
+
`when ${starts('spawn:')} then 'spawn' else 'step' end`);
|
|
993
|
+
}
|
|
696
994
|
function escapeLike(value) {
|
|
697
995
|
return value.replace(/[!%_]/g, (c) => `!${c}`);
|
|
698
996
|
}
|
|
997
|
+
/** Whether this connection's dialect supports `SELECT … FOR UPDATE` row locks. */
|
|
998
|
+
function locksRows(client) {
|
|
999
|
+
const name = client.dialect.name;
|
|
1000
|
+
return name === 'postgres' || name === 'mysql';
|
|
1001
|
+
}
|
|
699
1002
|
/** Knex `.update()` / `.delete()` return the affected row count as a number across dialects. */
|
|
700
1003
|
function rowsAffected(result) {
|
|
701
1004
|
if (typeof result === 'number')
|
|
@@ -707,7 +1010,7 @@ function rowsAffected(result) {
|
|
|
707
1010
|
function scheduleToRow(r) {
|
|
708
1011
|
return {
|
|
709
1012
|
id: r.id,
|
|
710
|
-
namespace: r.namespace,
|
|
1013
|
+
namespace: normalizeNamespace(r.namespace),
|
|
711
1014
|
workflow: r.workflow,
|
|
712
1015
|
paused: r.paused,
|
|
713
1016
|
next_fire_at: r.nextFireAt,
|
|
@@ -719,17 +1022,19 @@ function scheduleToRow(r) {
|
|
|
719
1022
|
};
|
|
720
1023
|
}
|
|
721
1024
|
function rowToSchedule(row) {
|
|
722
|
-
const tags = row.tags
|
|
1025
|
+
const tags = row.tags
|
|
1026
|
+
? parseJsonColumn(row.tags, `${DURABLE_TABLES.schedules}.tags`, row.id)
|
|
1027
|
+
: undefined;
|
|
723
1028
|
return {
|
|
724
1029
|
id: row.id,
|
|
725
|
-
namespace: row.namespace,
|
|
1030
|
+
namespace: normalizeNamespace(row.namespace),
|
|
726
1031
|
workflow: row.workflow,
|
|
727
1032
|
// SQLite/MySQL hand a boolean column back as 0/1 (some drivers as a string).
|
|
728
1033
|
paused: row.paused === true || row.paused === 1 || row.paused === '1' || row.paused === 'true',
|
|
729
1034
|
nextFireAt: row.next_fire_at == null ? null : Number(row.next_fire_at),
|
|
730
1035
|
...(tags?.length ? { tags } : {}),
|
|
731
|
-
spec: row.spec
|
|
732
|
-
state: row.state
|
|
1036
|
+
spec: parseJsonColumn(row.spec, `${DURABLE_TABLES.schedules}.spec`, row.id) ?? {},
|
|
1037
|
+
state: parseJsonColumn(row.state, `${DURABLE_TABLES.schedules}.state`, row.id) ?? {},
|
|
733
1038
|
createdAt: new Date(Number(row.created_at)),
|
|
734
1039
|
updatedAt: new Date(Number(row.updated_at)),
|
|
735
1040
|
};
|