@malloy-publisher/server 0.0.235 → 0.0.236
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/app/api-doc.yaml +47 -17
- package/dist/package_load_worker.mjs +8 -1
- package/dist/server.mjs +471 -54
- package/package.json +5 -2
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -97
- package/eslint.config.mjs +0 -76
- package/k6-tests/README.md +0 -144
- package/k6-tests/bun.lock +0 -797
- package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
- package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
- package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
- package/k6-tests/load-test/load-test-crud.ts +0 -142
- package/k6-tests/load-test/load-test.ts +0 -784
- package/k6-tests/package.json +0 -27
- package/k6-tests/scripts/run-with-otel.sh +0 -50
- package/k6-tests/smoke-test/smoke-test.ts +0 -158
- package/k6-tests/tsconfig.json +0 -29
- package/k6-tests/utils/client_factory.ts +0 -128
- package/k6-tests/utils/common.ts +0 -489
- package/k6-tests/utils/logger.ts +0 -62
- package/k6-tests/utils/types.d.ts +0 -35
- package/malloy_mcp.dxt +0 -0
- package/publisher.config.example.bigquery.json +0 -33
- package/publisher.config.example.duckdb.json +0 -23
- package/publisher.config.json +0 -23
- package/scripts/bake-duckdb-extensions.js +0 -110
- package/src/config.spec.ts +0 -1535
- package/src/config.theme.spec.ts +0 -178
- package/src/config.ts +0 -1142
- package/src/constants.ts +0 -105
- package/src/controller/compile.controller.ts +0 -40
- package/src/controller/connection.controller.spec.ts +0 -849
- package/src/controller/connection.controller.ts +0 -901
- package/src/controller/database.controller.ts +0 -24
- package/src/controller/materialization.controller.spec.ts +0 -293
- package/src/controller/materialization.controller.ts +0 -319
- package/src/controller/model.controller.ts +0 -164
- package/src/controller/package.controller.spec.ts +0 -273
- package/src/controller/package.controller.ts +0 -306
- package/src/controller/query.controller.ts +0 -152
- package/src/controller/theme.controller.ts +0 -83
- package/src/controller/watch-mode.controller.ts +0 -243
- package/src/data_styles.ts +0 -71
- package/src/default-publisher.config.json +0 -23
- package/src/ducklake_version.spec.ts +0 -163
- package/src/ducklake_version.ts +0 -153
- package/src/errors.spec.ts +0 -119
- package/src/errors.ts +0 -237
- package/src/filter_deprecation.spec.ts +0 -64
- package/src/filter_deprecation.ts +0 -42
- package/src/health.spec.ts +0 -123
- package/src/health.ts +0 -233
- package/src/heap_check.spec.ts +0 -144
- package/src/heap_check.ts +0 -144
- package/src/instrumentation.ts +0 -214
- package/src/json_utils.spec.ts +0 -51
- package/src/json_utils.ts +0 -33
- package/src/logger.spec.ts +0 -210
- package/src/logger.ts +0 -251
- package/src/malloy_pin_prereqs.spec.ts +0 -25
- package/src/materialization_metrics.spec.ts +0 -169
- package/src/materialization_metrics.ts +0 -346
- package/src/mcp/error_messages.spec.ts +0 -35
- package/src/mcp/error_messages.ts +0 -183
- package/src/mcp/handler_utils.spec.ts +0 -108
- package/src/mcp/handler_utils.ts +0 -251
- package/src/mcp/mcp_constants.ts +0 -6
- package/src/mcp/query_envelope.spec.ts +0 -229
- package/src/mcp/query_envelope.ts +0 -240
- package/src/mcp/server.protocol.spec.ts +0 -250
- package/src/mcp/server.ts +0 -87
- package/src/mcp/skills/build_skills_bundle.spec.ts +0 -51
- package/src/mcp/skills/build_skills_bundle.ts +0 -176
- package/src/mcp/skills/skills_bundle.json +0 -1
- package/src/mcp/skills/skills_bundle.spec.ts +0 -171
- package/src/mcp/tool_response.spec.ts +0 -108
- package/src/mcp/tool_response.ts +0 -138
- package/src/mcp/tools/compile_tool.spec.ts +0 -315
- package/src/mcp/tools/compile_tool.ts +0 -208
- package/src/mcp/tools/docs_search/build_docs_index.ts +0 -132
- package/src/mcp/tools/docs_search/malloy_docs_index.json +0 -1
- package/src/mcp/tools/docs_search_tool.spec.ts +0 -32
- package/src/mcp/tools/docs_search_tool.ts +0 -128
- package/src/mcp/tools/embedding_index.spec.ts +0 -1236
- package/src/mcp/tools/embedding_index.ts +0 -808
- package/src/mcp/tools/execute_query_tool.spec.ts +0 -294
- package/src/mcp/tools/execute_query_tool.ts +0 -316
- package/src/mcp/tools/get_context_eval.ts +0 -275
- package/src/mcp/tools/get_context_tool.spec.ts +0 -592
- package/src/mcp/tools/get_context_tool.ts +0 -585
- package/src/mcp/tools/reload_package_tool.spec.ts +0 -232
- package/src/mcp/tools/reload_package_tool.ts +0 -132
- package/src/mcp_config.spec.ts +0 -919
- package/src/mcp_config.ts +0 -425
- package/src/oom_guards.integration.spec.ts +0 -269
- package/src/package_load/package_load_pool.spec.ts +0 -252
- package/src/package_load/package_load_pool.ts +0 -931
- package/src/package_load/package_load_worker.ts +0 -1034
- package/src/package_load/protocol.ts +0 -364
- package/src/package_load/rpc_wait_accountant.spec.ts +0 -109
- package/src/package_load/rpc_wait_accountant.ts +0 -76
- package/src/package_load_metrics.spec.ts +0 -114
- package/src/package_load_metrics.ts +0 -127
- package/src/path_safety.spec.ts +0 -158
- package/src/path_safety.ts +0 -146
- package/src/pg_helpers.spec.ts +0 -223
- package/src/pg_helpers.ts +0 -52
- package/src/query_cap_metrics.spec.ts +0 -89
- package/src/query_cap_metrics.ts +0 -116
- package/src/query_concurrency.spec.ts +0 -247
- package/src/query_concurrency.ts +0 -237
- package/src/query_metadata_metrics.ts +0 -49
- package/src/query_param_utils.ts +0 -29
- package/src/query_timeout.spec.ts +0 -224
- package/src/query_timeout.ts +0 -179
- package/src/runtime/publisher.js +0 -328
- package/src/server-old.ts +0 -988
- package/src/server.ts +0 -2059
- package/src/service/annotations.spec.ts +0 -118
- package/src/service/annotations.ts +0 -91
- package/src/service/authorize.spec.ts +0 -154
- package/src/service/authorize.ts +0 -498
- package/src/service/authorize_integration.spec.ts +0 -2041
- package/src/service/build_plan.spec.ts +0 -685
- package/src/service/build_plan.ts +0 -797
- package/src/service/compile_authorize.spec.ts +0 -85
- package/src/service/compile_fragment_techniques.spec.ts +0 -156
- package/src/service/connection.spec.ts +0 -2360
- package/src/service/connection.ts +0 -2187
- package/src/service/connection_config.spec.ts +0 -734
- package/src/service/connection_config.ts +0 -899
- package/src/service/connection_federation.spec.ts +0 -184
- package/src/service/connection_fingerprint.spec.ts +0 -102
- package/src/service/connection_service.spec.ts +0 -711
- package/src/service/connection_service.ts +0 -291
- package/src/service/cron_evaluator.spec.ts +0 -79
- package/src/service/cron_evaluator.ts +0 -105
- package/src/service/db_utils.spec.ts +0 -1085
- package/src/service/db_utils.ts +0 -1502
- package/src/service/duckdb_instance_isolation.spec.ts +0 -137
- package/src/service/ducklake_lazy_attach.spec.ts +0 -110
- package/src/service/embedding_provider.spec.ts +0 -329
- package/src/service/embedding_provider.ts +0 -236
- package/src/service/environment.ts +0 -2191
- package/src/service/environment_admission.spec.ts +0 -344
- package/src/service/environment_compile.spec.ts +0 -197
- package/src/service/environment_store.spec.ts +0 -2274
- package/src/service/environment_store.ts +0 -2434
- package/src/service/environment_store_anchoring.spec.ts +0 -107
- package/src/service/environment_store_clone.spec.ts +0 -350
- package/src/service/explore_visibility.spec.ts +0 -434
- package/src/service/exports_probe.spec.ts +0 -107
- package/src/service/extension_fetch_policy.spec.ts +0 -256
- package/src/service/filter.spec.ts +0 -458
- package/src/service/filter.ts +0 -341
- package/src/service/filter_bypass.spec.ts +0 -418
- package/src/service/filter_integration.spec.ts +0 -1004
- package/src/service/freshness.spec.ts +0 -183
- package/src/service/freshness.ts +0 -112
- package/src/service/gcs_s3_utils.ts +0 -427
- package/src/service/given.ts +0 -105
- package/src/service/givens_integration.spec.ts +0 -219
- package/src/service/manifest_loader.spec.ts +0 -189
- package/src/service/manifest_loader.ts +0 -156
- package/src/service/materialization_build_session.spec.ts +0 -435
- package/src/service/materialization_build_session.ts +0 -681
- package/src/service/materialization_config_validation.spec.ts +0 -99
- package/src/service/materialization_config_validation.ts +0 -120
- package/src/service/materialization_eligibility.spec.ts +0 -158
- package/src/service/materialization_eligibility.ts +0 -305
- package/src/service/materialization_schedule_surface.spec.ts +0 -256
- package/src/service/materialization_scheduler.spec.ts +0 -289
- package/src/service/materialization_scheduler.ts +0 -304
- package/src/service/materialization_serve_transform.spec.ts +0 -1003
- package/src/service/materialization_serve_transform.ts +0 -779
- package/src/service/materialization_service.spec.ts +0 -2373
- package/src/service/materialization_service.ts +0 -2342
- package/src/service/materialization_test_fixtures.ts +0 -249
- package/src/service/model.spec.ts +0 -1157
- package/src/service/model.ts +0 -3370
- package/src/service/model_limits.spec.ts +0 -209
- package/src/service/model_limits.ts +0 -131
- package/src/service/model_storage_serve.spec.ts +0 -193
- package/src/service/model_storage_serve_joins.spec.ts +0 -193
- package/src/service/package.spec.ts +0 -612
- package/src/service/package.ts +0 -1799
- package/src/service/package_manifest.spec.ts +0 -230
- package/src/service/package_manifest.ts +0 -241
- package/src/service/package_memory_governor.spec.ts +0 -173
- package/src/service/package_memory_governor.ts +0 -233
- package/src/service/package_quote_bound_tables.spec.ts +0 -171
- package/src/service/package_race.spec.ts +0 -208
- package/src/service/package_reload_safety.spec.ts +0 -193
- package/src/service/package_rollback.spec.ts +0 -190
- package/src/service/package_worker_path.spec.ts +0 -645
- package/src/service/path_injection.spec.ts +0 -39
- package/src/service/persist_annotation_validation.spec.ts +0 -89
- package/src/service/persist_annotation_validation.ts +0 -59
- package/src/service/persistence_policy.spec.ts +0 -374
- package/src/service/proxy.spec.ts +0 -414
- package/src/service/proxy.ts +0 -248
- package/src/service/query_boundary.spec.ts +0 -470
- package/src/service/query_metadata.spec.ts +0 -408
- package/src/service/query_metadata.ts +0 -492
- package/src/service/query_metadata_identity.spec.ts +0 -149
- package/src/service/query_text.spec.ts +0 -79
- package/src/service/query_text.ts +0 -43
- package/src/service/quoting.spec.ts +0 -135
- package/src/service/quoting.ts +0 -78
- package/src/service/resolve_environment.ts +0 -15
- package/src/service/restricted_mode.spec.ts +0 -299
- package/src/service/source_extraction.ts +0 -225
- package/src/service/theme_store.ts +0 -199
- package/src/storage/DatabaseInterface.ts +0 -198
- package/src/storage/StorageManager.ts +0 -113
- package/src/storage/duckdb/ConnectionRepository.ts +0 -143
- package/src/storage/duckdb/DuckDBConnection.ts +0 -123
- package/src/storage/duckdb/DuckDBRepository.ts +0 -208
- package/src/storage/duckdb/EnvironmentRepository.ts +0 -148
- package/src/storage/duckdb/MaterializationRepository.spec.ts +0 -191
- package/src/storage/duckdb/MaterializationRepository.ts +0 -336
- package/src/storage/duckdb/PackageRepository.ts +0 -135
- package/src/storage/duckdb/schema.ts +0 -229
- package/src/stream_helpers.spec.ts +0 -280
- package/src/stream_helpers.ts +0 -162
- package/src/telemetry.ts +0 -20
- package/src/test_helpers/metrics_harness.ts +0 -166
- package/src/theme_key_parity.spec.ts +0 -57
- package/src/utils.ts +0 -29
- package/tests/fixtures/authorize-compile/model.malloy +0 -9
- package/tests/fixtures/authorize-compile/publisher.json +0 -4
- package/tests/fixtures/html-pages-nopublic/model.malloy +0 -1
- package/tests/fixtures/html-pages-nopublic/publisher.json +0 -5
- package/tests/fixtures/html-pages-test/data.csv +0 -3
- package/tests/fixtures/html-pages-test/public/assets/app.css +0 -3
- package/tests/fixtures/html-pages-test/public/barehtml.html +0 -4
- package/tests/fixtures/html-pages-test/public/bodymeta.html +0 -8
- package/tests/fixtures/html-pages-test/public/data.json +0 -1
- package/tests/fixtures/html-pages-test/public/fitcomment.html +0 -11
- package/tests/fixtures/html-pages-test/public/index.html +0 -9
- package/tests/fixtures/html-pages-test/public/nohead.html +0 -8
- package/tests/fixtures/html-pages-test/public/notfit.html +0 -13
- package/tests/fixtures/html-pages-test/public/slides.html +0 -12
- package/tests/fixtures/html-pages-test/public/sub/page2.html +0 -9
- package/tests/fixtures/html-pages-test/public/unterminated.html +0 -10
- package/tests/fixtures/html-pages-test/publisher.json +0 -5
- package/tests/fixtures/html-pages-test/report.malloy +0 -1
- package/tests/fixtures/persist-multi-level/data/orders.csv +0 -5
- package/tests/fixtures/persist-multi-level/multi_level.malloy +0 -18
- package/tests/fixtures/persist-multi-level/publisher.json +0 -5
- package/tests/fixtures/persist-schedule-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-schedule-test/persist_schedule_test.malloy +0 -11
- package/tests/fixtures/persist-schedule-test/publisher.json +0 -7
- package/tests/fixtures/persist-schedule-warn-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-schedule-warn-test/persist_warn_test.malloy +0 -11
- package/tests/fixtures/persist-schedule-warn-test/publisher.json +0 -6
- package/tests/fixtures/persist-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-test/persist_test.malloy +0 -11
- package/tests/fixtures/persist-test/publisher.json +0 -5
- package/tests/fixtures/publisher.config.json +0 -15
- package/tests/fixtures/query-givens/data/orders.csv +0 -7
- package/tests/fixtures/query-givens/model.malloy +0 -34
- package/tests/fixtures/query-givens/publisher.json +0 -5
- package/tests/fixtures/xlsx/database.xlsx +0 -0
- package/tests/harness/mcp_test_setup.ts +0 -332
- package/tests/harness/rest_e2e.ts +0 -68
- package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +0 -92
- package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +0 -280
- package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +0 -138
- package/tests/integration/first_boot/readiness_line.integration.spec.ts +0 -177
- package/tests/integration/html_pages/html_pages.integration.spec.ts +0 -440
- package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +0 -259
- package/tests/integration/materialization/environment_list.integration.spec.ts +0 -133
- package/tests/integration/materialization/freshness_gate.integration.spec.ts +0 -292
- package/tests/integration/materialization/manifest_binding.integration.spec.ts +0 -430
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -441
- package/tests/integration/materialization/orchestrated_rebind.integration.spec.ts +0 -150
- package/tests/integration/materialization/reference_manifest.integration.spec.ts +0 -251
- package/tests/integration/materialization/schedule_edit.integration.spec.ts +0 -126
- package/tests/integration/materialization/scheduler.integration.spec.ts +0 -200
- package/tests/integration/materialization/scheduler_recovery.integration.spec.ts +0 -172
- package/tests/integration/materialization/scheduler_transitions.integration.spec.ts +0 -256
- package/tests/integration/materialization/scheduler_wiring.integration.spec.ts +0 -143
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -438
- package/tests/integration/mcp/mcp_get_context_semantic.integration.spec.ts +0 -235
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -98
- package/tests/integration/query_givens/query_givens.integration.spec.ts +0 -146
- package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +0 -121
- package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +0 -110
- package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -421
- package/tests/unit/controller/theme.controller.spec.ts +0 -141
- package/tests/unit/duckdb/attached_databases.test.ts +0 -1243
- package/tests/unit/duckdb/duckdb_connection.test.ts +0 -181
- package/tests/unit/duckdb/legacy_schema_migration.test.ts +0 -197
- package/tests/unit/duckdb/repositories.test.ts +0 -208
- package/tests/unit/ducklake/ducklake.test.ts +0 -952
- package/tests/unit/service/theme_store.spec.ts +0 -139
- package/tsconfig.json +0 -32
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import type { MaterializationSchedulerConfig } from "../config";
|
|
2
|
-
import { MaterializationConflictError } from "../errors";
|
|
3
|
-
import { logger } from "../logger";
|
|
4
|
-
import { recordScheduledFire } from "../materialization_metrics";
|
|
5
|
-
import { CronEvaluator } from "./cron_evaluator";
|
|
6
|
-
import type { EnvironmentStore } from "./environment_store";
|
|
7
|
-
import type { MaterializationService } from "./materialization_service";
|
|
8
|
-
|
|
9
|
-
/** Per-package arming state, keyed by `${environmentName}::${packageName}`. */
|
|
10
|
-
interface ScheduleState {
|
|
11
|
-
/** The cron the state was armed from; a change re-arms `nextFireAtMs`. */
|
|
12
|
-
schedule: string;
|
|
13
|
-
/** Epoch ms of the next due fire (`cron.nextAfter`, strictly future when armed). */
|
|
14
|
-
nextFireAtMs: number;
|
|
15
|
-
/** Epoch ms of the last fire attempt, or null if never fired. */
|
|
16
|
-
lastFiredAtMs: number | null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Standalone materialization scheduler: fires a loaded package's
|
|
21
|
-
* `materialization.schedule` cron so an open-source publisher (no control plane)
|
|
22
|
-
* can rebuild on a cadence. Modeled on {@link PackageMemoryGovernor} — a
|
|
23
|
-
* config-driven `setInterval` with `start`/`stop` and one `tick`.
|
|
24
|
-
*
|
|
25
|
-
* **Orchestration safety (two independent guards):**
|
|
26
|
-
* 1. Disabled by default — only constructed when
|
|
27
|
-
* {@link getMaterializationSchedulerConfig} returns non-null
|
|
28
|
-
* (`PUBLISHER_LOCAL_MATERIALIZATION_SCHEDULER`), which an orchestrated
|
|
29
|
-
* deployment never sets, so the scheduler never runs there.
|
|
30
|
-
* 2. Even when enabled, a package with a configured `manifestLocation` (i.e.
|
|
31
|
-
* control-plane-driven) is skipped — the CP already fires it, so we never
|
|
32
|
-
* double-drive.
|
|
33
|
-
*
|
|
34
|
-
* These guards are **independent and both load-bearing — never collapse them to
|
|
35
|
-
* just the `manifestLocation` check.** A control-plane-loaded package that is
|
|
36
|
-
* *serving live* has `manifestLocation === null`, so Guard 2 does not cover it;
|
|
37
|
-
* only Guard 1 (the opt-in flag, which an orchestrated worker must never set —
|
|
38
|
-
* see {@link getMaterializationSchedulerConfig}) keeps the scheduler off there.
|
|
39
|
-
*
|
|
40
|
-
* It only sweeps **already-loaded** packages (never forces a load), fires the
|
|
41
|
-
* existing `createMaterialization` auto-run path tagged `trigger=SCHEDULER`, and
|
|
42
|
-
* only fires packages whose persistence policy is valid (`scope: version`, no
|
|
43
|
-
* `freshness` — the same rule a publish enforces). Arming state is in memory,
|
|
44
|
-
* but on the first arm after a (re)start it re-anchors from the newest recorded
|
|
45
|
-
* `SCHEDULER` fire (see {@link arm}), so an occurrence that came due during
|
|
46
|
-
* downtime fires exactly once and then jumps forward — recovery matches the
|
|
47
|
-
* control plane's persisted-`next_fire_at` behavior rather than silently
|
|
48
|
-
* skipping. A never-fired schedule still does not fire on the arming tick
|
|
49
|
-
* (`nextAfter(now)` is strictly future).
|
|
50
|
-
*/
|
|
51
|
-
export class MaterializationScheduler {
|
|
52
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
53
|
-
private readonly cron: CronEvaluator;
|
|
54
|
-
private readonly state = new Map<string, ScheduleState>();
|
|
55
|
-
|
|
56
|
-
constructor(
|
|
57
|
-
private readonly environmentStore: EnvironmentStore,
|
|
58
|
-
private readonly materializationService: MaterializationService,
|
|
59
|
-
private readonly config: MaterializationSchedulerConfig,
|
|
60
|
-
cron?: CronEvaluator,
|
|
61
|
-
) {
|
|
62
|
-
this.cron = cron ?? new CronEvaluator();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Begin the periodic sweep. Idempotent; the interval is `unref`'d. */
|
|
66
|
-
public start(): void {
|
|
67
|
-
if (this.timer !== null) return;
|
|
68
|
-
this.timer = setInterval(() => {
|
|
69
|
-
void this.tick();
|
|
70
|
-
}, this.config.tickIntervalMs);
|
|
71
|
-
(
|
|
72
|
-
this.timer as ReturnType<typeof setInterval> & { unref?: () => void }
|
|
73
|
-
).unref?.();
|
|
74
|
-
logger.info(
|
|
75
|
-
`MaterializationScheduler started (interval=${this.config.tickIntervalMs}ms, maxFiresPerTick=${this.config.maxFiresPerTick})`,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Stop the periodic sweep. Idempotent. */
|
|
80
|
-
public stop(): void {
|
|
81
|
-
if (this.timer !== null) {
|
|
82
|
-
clearInterval(this.timer);
|
|
83
|
-
this.timer = null;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* One sweep: arm newly-seen schedules, fire due ones (capped per tick), and
|
|
89
|
-
* prune state for packages no longer loaded/scheduled. Exposed so tests can
|
|
90
|
-
* drive it synchronously. Never throws — per-package errors are isolated.
|
|
91
|
-
*/
|
|
92
|
-
public async tick(nowMs: number = Date.now()): Promise<void> {
|
|
93
|
-
const seen = new Set<string>();
|
|
94
|
-
let fired = 0;
|
|
95
|
-
|
|
96
|
-
for (const env of this.environmentStore.getLoadedEnvironments()) {
|
|
97
|
-
const environmentName = env.getEnvironmentName();
|
|
98
|
-
for (const pkg of env.getLoadedPackages()) {
|
|
99
|
-
const packageName = pkg.getPackageName();
|
|
100
|
-
const key = `${environmentName}::${packageName}`;
|
|
101
|
-
try {
|
|
102
|
-
const schedule = this.schedulableCron(pkg);
|
|
103
|
-
if (schedule === null) {
|
|
104
|
-
// Not a standalone-schedulable package (no cron, orchestrated,
|
|
105
|
-
// or invalid policy/cron) — drop any stale arming state.
|
|
106
|
-
this.state.delete(key);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
seen.add(key);
|
|
110
|
-
|
|
111
|
-
const armed = await this.arm(
|
|
112
|
-
environmentName,
|
|
113
|
-
packageName,
|
|
114
|
-
key,
|
|
115
|
-
schedule,
|
|
116
|
-
nowMs,
|
|
117
|
-
);
|
|
118
|
-
if (nowMs < armed.nextFireAtMs) continue; // not due yet
|
|
119
|
-
|
|
120
|
-
if (fired >= this.config.maxFiresPerTick) {
|
|
121
|
-
// Stampede guard: leave it due; it fires on a later tick.
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
await this.fire(environmentName, packageName);
|
|
125
|
-
fired++;
|
|
126
|
-
// Advance regardless of outcome so a persistent failure retries on
|
|
127
|
-
// the next cron occurrence (cron cadence), never every tick.
|
|
128
|
-
armed.nextFireAtMs = this.cron
|
|
129
|
-
.nextAfter(schedule, new Date(nowMs))
|
|
130
|
-
.getTime();
|
|
131
|
-
armed.lastFiredAtMs = nowMs;
|
|
132
|
-
} catch (err) {
|
|
133
|
-
logger.warn("MaterializationScheduler: package sweep failed", {
|
|
134
|
-
environmentName,
|
|
135
|
-
packageName,
|
|
136
|
-
error: err instanceof Error ? err.message : String(err),
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
for (const key of [...this.state.keys()]) {
|
|
143
|
-
if (!seen.has(key)) this.state.delete(key);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* The package's schedulable cron, or null when it must be skipped: no
|
|
149
|
-
* `materialization.schedule`; a `manifestLocation` (control-plane-driven —
|
|
150
|
-
* Guard 2); an invalid persistence policy (e.g. schedule without
|
|
151
|
-
* `scope: version`, or schedule + freshness — the publish-gate rules); or an
|
|
152
|
-
* unparseable cron. Skips are debug-logged (the policy issues are already
|
|
153
|
-
* warned at package load), not warned each tick.
|
|
154
|
-
*/
|
|
155
|
-
private schedulableCron(pkg: {
|
|
156
|
-
getPackageMetadata(): {
|
|
157
|
-
materialization?: { schedule?: string | null } | null;
|
|
158
|
-
manifestLocation?: string | null;
|
|
159
|
-
};
|
|
160
|
-
persistencePolicyWarnings(): string[];
|
|
161
|
-
getPackageName(): string;
|
|
162
|
-
}): string | null {
|
|
163
|
-
const metadata = pkg.getPackageMetadata();
|
|
164
|
-
const schedule = metadata.materialization?.schedule ?? null;
|
|
165
|
-
if (!schedule) return null;
|
|
166
|
-
if (metadata.manifestLocation) return null; // orchestrated — CP fires it
|
|
167
|
-
if (pkg.persistencePolicyWarnings().length > 0) {
|
|
168
|
-
logger.debug(
|
|
169
|
-
"MaterializationScheduler: skipping package with invalid persistence policy",
|
|
170
|
-
{ packageName: pkg.getPackageName() },
|
|
171
|
-
);
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
if (!this.cron.isValid(schedule)) {
|
|
175
|
-
logger.debug("MaterializationScheduler: skipping invalid cron", {
|
|
176
|
-
packageName: pkg.getPackageName(),
|
|
177
|
-
schedule,
|
|
178
|
-
});
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
return schedule;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Return the package's arming state, (re)arming when it is new or its cron
|
|
186
|
-
* changed.
|
|
187
|
-
*
|
|
188
|
-
* **First arm of a package this process** (no in-memory state — a fresh start
|
|
189
|
-
* or a newly-loaded package): anchor `nextFireAt` to the newest recorded
|
|
190
|
-
* `SCHEDULER` fire, `nextAfter(lastScheduledFire)`. If that instant is already
|
|
191
|
-
* past (an occurrence came due while the publisher was down), it fires once
|
|
192
|
-
* on this tick and then jumps forward to `nextAfter(now)` — exactly one
|
|
193
|
-
* catch-up, matching the control plane's persisted-`next_fire_at` recovery
|
|
194
|
-
* contract, never a burst. With no prior `SCHEDULER` fire on record we fall
|
|
195
|
-
* back to `nextAfter(now)` (strictly future), so a never-fired schedule does
|
|
196
|
-
* not fire on the arming tick. (Residual divergence: a schedule set while the
|
|
197
|
-
* scheduler was disabled has no prior fire, so it is not caught up on first
|
|
198
|
-
* enable.)
|
|
199
|
-
*
|
|
200
|
-
* **Cron changed**: re-arm from `nextAfter(now)` — an edited cadence starts
|
|
201
|
-
* fresh, with no catch-up for the old cron.
|
|
202
|
-
*/
|
|
203
|
-
private async arm(
|
|
204
|
-
environmentName: string,
|
|
205
|
-
packageName: string,
|
|
206
|
-
key: string,
|
|
207
|
-
schedule: string,
|
|
208
|
-
nowMs: number,
|
|
209
|
-
): Promise<ScheduleState> {
|
|
210
|
-
const existing = this.state.get(key);
|
|
211
|
-
if (existing && existing.schedule === schedule) {
|
|
212
|
-
return existing;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const anchor =
|
|
216
|
-
existing === undefined
|
|
217
|
-
? await this.recoveryAnchor(environmentName, packageName, nowMs)
|
|
218
|
-
: new Date(nowMs);
|
|
219
|
-
|
|
220
|
-
const armed: ScheduleState = {
|
|
221
|
-
schedule,
|
|
222
|
-
nextFireAtMs: this.cron.nextAfter(schedule, anchor).getTime(),
|
|
223
|
-
lastFiredAtMs: existing?.lastFiredAtMs ?? null,
|
|
224
|
-
};
|
|
225
|
-
this.state.set(key, armed);
|
|
226
|
-
return armed;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* The instant to compute the first `nextFireAt` from after a (re)start: the
|
|
231
|
-
* newest recorded `SCHEDULER` fire if any, else `now`. Any lookup failure
|
|
232
|
-
* degrades to `now` (no catch-up) so arming always succeeds.
|
|
233
|
-
*/
|
|
234
|
-
private async recoveryAnchor(
|
|
235
|
-
environmentName: string,
|
|
236
|
-
packageName: string,
|
|
237
|
-
nowMs: number,
|
|
238
|
-
): Promise<Date> {
|
|
239
|
-
try {
|
|
240
|
-
const last =
|
|
241
|
-
await this.materializationService.getLatestScheduledFireAt(
|
|
242
|
-
environmentName,
|
|
243
|
-
packageName,
|
|
244
|
-
);
|
|
245
|
-
return last ?? new Date(nowMs);
|
|
246
|
-
} catch (err) {
|
|
247
|
-
logger.warn(
|
|
248
|
-
"MaterializationScheduler: could not read last scheduled fire; arming from now",
|
|
249
|
-
{
|
|
250
|
-
environmentName,
|
|
251
|
-
packageName,
|
|
252
|
-
error: err instanceof Error ? err.message : String(err),
|
|
253
|
-
},
|
|
254
|
-
);
|
|
255
|
-
return new Date(nowMs);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Fire a whole-package scheduled rebuild via the existing auto-run path. An
|
|
261
|
-
* already-active materialization coalesces (the in-flight build covers this
|
|
262
|
-
* occurrence); any other error is logged and metered but never propagated.
|
|
263
|
-
*
|
|
264
|
-
* **Standalone fires are tables-only.** This path runs a single-phase build
|
|
265
|
-
* that materializes the package's persist sources into tables. A hosted
|
|
266
|
-
* (control-plane-driven) deployment runs a two-phase job — tables, then a
|
|
267
|
-
* second pass over indexed dimensions — so index behavior is a hosted-only
|
|
268
|
-
* concern that cannot be exercised by the standalone scheduler. Anything a
|
|
269
|
-
* schedule fires locally is exactly what the `createMaterialization`
|
|
270
|
-
* auto-run produces on demand; only the `trigger` label differs.
|
|
271
|
-
*/
|
|
272
|
-
private async fire(
|
|
273
|
-
environmentName: string,
|
|
274
|
-
packageName: string,
|
|
275
|
-
): Promise<void> {
|
|
276
|
-
try {
|
|
277
|
-
await this.materializationService.createMaterialization(
|
|
278
|
-
environmentName,
|
|
279
|
-
packageName,
|
|
280
|
-
{ forceRefresh: true, trigger: "SCHEDULER" },
|
|
281
|
-
);
|
|
282
|
-
recordScheduledFire("fired");
|
|
283
|
-
logger.info("MaterializationScheduler: fired scheduled rebuild", {
|
|
284
|
-
environmentName,
|
|
285
|
-
packageName,
|
|
286
|
-
});
|
|
287
|
-
} catch (err) {
|
|
288
|
-
if (err instanceof MaterializationConflictError) {
|
|
289
|
-
recordScheduledFire("conflict");
|
|
290
|
-
logger.debug(
|
|
291
|
-
"MaterializationScheduler: skipped fire; a materialization is already active",
|
|
292
|
-
{ environmentName, packageName },
|
|
293
|
-
);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
recordScheduledFire("error");
|
|
297
|
-
logger.warn("MaterializationScheduler: scheduled fire failed", {
|
|
298
|
-
environmentName,
|
|
299
|
-
packageName,
|
|
300
|
-
error: err instanceof Error ? err.message : String(err),
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|