@malloy-publisher/server 0.0.235 → 0.0.237
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 +83 -27
- package/dist/app/assets/{EnvironmentPage-BsAnavYN.js → EnvironmentPage-DaP6CpgK.js} +1 -1
- package/dist/app/assets/{HomePage-CADE138j.js → HomePage-C9yNw3iu.js} +1 -1
- package/dist/app/assets/{LightMode-Cfh7KzN8.js → LightMode-Be4-G-dX.js} +1 -1
- package/dist/app/assets/{MainPage-CO3pRlnV.js → MainPage-J_rU2SgI.js} +2 -2
- package/dist/app/assets/{MaterializationsPage-p9YjkRXZ.js → MaterializationsPage-DHgPI4MO.js} +1 -1
- package/dist/app/assets/ModelPage-BzXfjTu5.js +1 -0
- package/dist/app/assets/{PackagePage-e4kN75YR.js → PackagePage-ByoSTLJ4.js} +1 -1
- package/dist/app/assets/RouteError-CrSeKN2r.js +1 -0
- package/dist/app/assets/{ThemeEditorPage-CciagFTq.js → ThemeEditorPage-Btxnmhtk.js} +1 -1
- package/dist/app/assets/{WorkbookPage-DNWmkCXa.js → WorkbookPage-D6JLPnWO.js} +1 -1
- package/dist/app/assets/{core-Rj_4rRnA.es-BZyvITuO.js → core-BSRgt1mG.es-Bc_D2Lq4.js} +1 -1
- package/dist/app/assets/{index-CH2AcDzc.js → index-BRijWj6K.js} +15 -15
- package/dist/app/assets/{index-DQa463gC.js → index-BX4D94xw.js} +1 -1
- package/dist/app/assets/{index-VBbcc8s6.js → index-Cvy0xtb7.js} +1 -1
- package/dist/app/assets/{index-DQpV7MyA.js → index-DMAhIJNC.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/package_load_worker.mjs +59 -20
- package/dist/server.mjs +1342 -616
- package/package.json +8 -2
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -97
- package/dist/app/assets/ModelPage-C1OSTv-x.js +0 -1
- package/dist/app/assets/RouteError-CzbfOkng.js +0 -1
- 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,183 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
FreshnessAwareManifestEntry,
|
|
5
|
-
FreshnessManifest,
|
|
6
|
-
} from "../storage/DatabaseInterface";
|
|
7
|
-
import { evaluateManifestFreshness, filterFreshManifest } from "./freshness";
|
|
8
|
-
|
|
9
|
-
// A fixed "now" so age math is deterministic.
|
|
10
|
-
const NOW = new Date("2026-07-07T12:00:00.000Z");
|
|
11
|
-
const NOW_MS = NOW.getTime();
|
|
12
|
-
|
|
13
|
-
/** dataAsOf `ageSeconds` before NOW. */
|
|
14
|
-
function asOf(ageSeconds: number): string {
|
|
15
|
-
return new Date(NOW_MS - ageSeconds * 1000).toISOString();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe("evaluateManifestFreshness", () => {
|
|
19
|
-
it("serves the table when un-gated (no window)", () => {
|
|
20
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
21
|
-
tableName: "t",
|
|
22
|
-
dataAsOf: asOf(1_000_000),
|
|
23
|
-
};
|
|
24
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_table");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("serves the table when a window is declared but no dataAsOf anchor", () => {
|
|
28
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
29
|
-
tableName: "t",
|
|
30
|
-
freshnessWindowSeconds: 3600,
|
|
31
|
-
freshnessFallback: "live",
|
|
32
|
-
};
|
|
33
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_table");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("serves the table when fresh (age within window)", () => {
|
|
37
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
38
|
-
tableName: "t",
|
|
39
|
-
dataAsOf: asOf(1800),
|
|
40
|
-
freshnessWindowSeconds: 3600,
|
|
41
|
-
freshnessFallback: "live",
|
|
42
|
-
};
|
|
43
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_table");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("serves the table at the exact window boundary (age == window)", () => {
|
|
47
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
48
|
-
tableName: "t",
|
|
49
|
-
dataAsOf: asOf(3600),
|
|
50
|
-
freshnessWindowSeconds: 3600,
|
|
51
|
-
freshnessFallback: "live",
|
|
52
|
-
};
|
|
53
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_table");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("drops a stale entry with fallback live (=> serve live)", () => {
|
|
57
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
58
|
-
tableName: "t",
|
|
59
|
-
dataAsOf: asOf(7200),
|
|
60
|
-
freshnessWindowSeconds: 3600,
|
|
61
|
-
freshnessFallback: "live",
|
|
62
|
-
};
|
|
63
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_live");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("keeps a stale entry with fallback stale_ok (=> serve the stale table)", () => {
|
|
67
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
68
|
-
tableName: "t",
|
|
69
|
-
dataAsOf: asOf(7200),
|
|
70
|
-
freshnessWindowSeconds: 3600,
|
|
71
|
-
freshnessFallback: "stale_ok",
|
|
72
|
-
};
|
|
73
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_table");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("drops a stale entry with fallback fail (interim: serve live in v1)", () => {
|
|
77
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
78
|
-
tableName: "t",
|
|
79
|
-
dataAsOf: asOf(7200),
|
|
80
|
-
freshnessWindowSeconds: 3600,
|
|
81
|
-
freshnessFallback: "fail",
|
|
82
|
-
};
|
|
83
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_live");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("drops a stale entry with no declared fallback (=> serve live)", () => {
|
|
87
|
-
const entry: FreshnessAwareManifestEntry = {
|
|
88
|
-
tableName: "t",
|
|
89
|
-
dataAsOf: asOf(7200),
|
|
90
|
-
freshnessWindowSeconds: 3600,
|
|
91
|
-
};
|
|
92
|
-
expect(evaluateManifestFreshness(entry, NOW)).toBe("serve_live");
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe("filterFreshManifest", () => {
|
|
97
|
-
it("includes fresh/un-gated/stale_ok and drops stale live/fail", () => {
|
|
98
|
-
const entries: FreshnessManifest = {
|
|
99
|
-
fresh: {
|
|
100
|
-
tableName: "fresh_t",
|
|
101
|
-
dataAsOf: asOf(1800),
|
|
102
|
-
freshnessWindowSeconds: 3600,
|
|
103
|
-
freshnessFallback: "live",
|
|
104
|
-
},
|
|
105
|
-
ungated: { tableName: "ungated_t", dataAsOf: asOf(999999) },
|
|
106
|
-
staleLive: {
|
|
107
|
-
tableName: "stale_live_t",
|
|
108
|
-
dataAsOf: asOf(7200),
|
|
109
|
-
freshnessWindowSeconds: 3600,
|
|
110
|
-
freshnessFallback: "live",
|
|
111
|
-
},
|
|
112
|
-
staleOk: {
|
|
113
|
-
tableName: "stale_ok_t",
|
|
114
|
-
dataAsOf: asOf(7200),
|
|
115
|
-
freshnessWindowSeconds: 3600,
|
|
116
|
-
freshnessFallback: "stale_ok",
|
|
117
|
-
},
|
|
118
|
-
staleFail: {
|
|
119
|
-
tableName: "stale_fail_t",
|
|
120
|
-
dataAsOf: asOf(7200),
|
|
121
|
-
freshnessWindowSeconds: 3600,
|
|
122
|
-
freshnessFallback: "fail",
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const { manifest } = filterFreshManifest(entries, NOW);
|
|
127
|
-
expect(Object.keys(manifest).sort()).toEqual([
|
|
128
|
-
"fresh",
|
|
129
|
-
"staleOk",
|
|
130
|
-
"ungated",
|
|
131
|
-
]);
|
|
132
|
-
expect(manifest.fresh).toEqual({ tableName: "fresh_t" });
|
|
133
|
-
expect(manifest.staleOk).toEqual({ tableName: "stale_ok_t" });
|
|
134
|
-
expect(manifest.ungated).toEqual({ tableName: "ungated_t" });
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("reports the earliest future window-crossing as nextStaleSince", () => {
|
|
138
|
-
const entries: FreshnessManifest = {
|
|
139
|
-
a: {
|
|
140
|
-
tableName: "a_t",
|
|
141
|
-
dataAsOf: asOf(1800), // window 3600 => flips at NOW + 1800s
|
|
142
|
-
freshnessWindowSeconds: 3600,
|
|
143
|
-
freshnessFallback: "live",
|
|
144
|
-
},
|
|
145
|
-
b: {
|
|
146
|
-
tableName: "b_t",
|
|
147
|
-
dataAsOf: asOf(1000), // window 3600 => flips at NOW + 2600s (later)
|
|
148
|
-
freshnessWindowSeconds: 3600,
|
|
149
|
-
freshnessFallback: "live",
|
|
150
|
-
},
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const { nextStaleSince } = filterFreshManifest(entries, NOW);
|
|
154
|
-
// Earliest flip is entry a: dataAsOf(a) + window.
|
|
155
|
-
expect(nextStaleSince).toBe(NOW_MS - 1800 * 1000 + 3600 * 1000);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("kept stale_ok entries do not set a future nextStaleSince", () => {
|
|
159
|
-
const entries: FreshnessManifest = {
|
|
160
|
-
staleOk: {
|
|
161
|
-
tableName: "stale_ok_t",
|
|
162
|
-
dataAsOf: asOf(7200),
|
|
163
|
-
freshnessWindowSeconds: 3600,
|
|
164
|
-
freshnessFallback: "stale_ok",
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
const { manifest, nextStaleSince } = filterFreshManifest(entries, NOW);
|
|
168
|
-
expect(Object.keys(manifest)).toEqual(["staleOk"]);
|
|
169
|
-
// Its window crossing is already in the past, so nothing schedules a
|
|
170
|
-
// future recompute.
|
|
171
|
-
expect(nextStaleSince).toBeNull();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("returns null nextStaleSince when nothing is gated", () => {
|
|
175
|
-
const entries: FreshnessManifest = {
|
|
176
|
-
a: { tableName: "a_t" },
|
|
177
|
-
b: { tableName: "b_t", dataAsOf: asOf(10) },
|
|
178
|
-
};
|
|
179
|
-
const { manifest, nextStaleSince } = filterFreshManifest(entries, NOW);
|
|
180
|
-
expect(Object.keys(manifest).sort()).toEqual(["a", "b"]);
|
|
181
|
-
expect(nextStaleSince).toBeNull();
|
|
182
|
-
});
|
|
183
|
-
});
|
package/src/service/freshness.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-query freshness gate for persisted sources (persistence.md §9.3, §14
|
|
3
|
-
* Phase B). A query on a `#@ persist` source may use its materialized table
|
|
4
|
-
* only while that table is within its declared freshness window; otherwise the
|
|
5
|
-
* query does what the source's `fallback` says.
|
|
6
|
-
*
|
|
7
|
-
* Two settled design principles anchor this (do not re-derive them the easy
|
|
8
|
-
* way):
|
|
9
|
-
*
|
|
10
|
-
* 1. Enforce per query, not at bind. Filtering stale entries once when the
|
|
11
|
-
* manifest is bound would let a table that crosses its window while the
|
|
12
|
-
* package stays loaded keep serving stale. The serve path re-evaluates
|
|
13
|
-
* freshness on every query.
|
|
14
|
-
* 2. Clock on `dataAsOf`, not build completion. The window is a deadline on
|
|
15
|
-
* *data* age; the anchor is the artifact's data-as-of instant (snapshot
|
|
16
|
-
* start) supplied by the control plane. `age = now - dataAsOf`.
|
|
17
|
-
*
|
|
18
|
-
* Scope: `live`/`stale_ok` are fully publisher-side. `fail` degrades to
|
|
19
|
-
* serve-live in this first cut (the entry is dropped like `live`); the airtight
|
|
20
|
-
* `fail` path — which must *error* a query that joins a stale persist source —
|
|
21
|
-
* is a gated forked-malloy follow-up and is not built here.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
BuildManifest,
|
|
26
|
-
FreshnessAwareManifestEntry,
|
|
27
|
-
FreshnessManifest,
|
|
28
|
-
} from "../storage/DatabaseInterface";
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* What to do with a single manifest entry at query time.
|
|
32
|
-
* - `serve_table`: substitute the materialized table (include `{ tableName }`).
|
|
33
|
-
* - `serve_live`: drop the entry so `strict:false` falls through to live SQL.
|
|
34
|
-
*
|
|
35
|
-
* `fail` collapses to `serve_live` in v1 (see module note).
|
|
36
|
-
*/
|
|
37
|
-
export type FreshnessDecision = "serve_table" | "serve_live";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Evaluate one entry against `now`. An entry with no `freshnessWindowSeconds`
|
|
41
|
-
* is un-gated (always serve the table). A fresh entry (age within window) is
|
|
42
|
-
* served. A stale entry serves its table only under `stale_ok`; `live` and
|
|
43
|
-
* `fail` both drop to live in this first cut.
|
|
44
|
-
*/
|
|
45
|
-
export function evaluateManifestFreshness(
|
|
46
|
-
entry: FreshnessAwareManifestEntry,
|
|
47
|
-
now: Date,
|
|
48
|
-
): FreshnessDecision {
|
|
49
|
-
// Un-gated: no declared window ⇒ never stale. Also the rollout-safe default
|
|
50
|
-
// before the control plane starts stamping the freshness fields.
|
|
51
|
-
if (entry.freshnessWindowSeconds == null) return "serve_table";
|
|
52
|
-
// A window without an anchor can't be evaluated; treat as un-gated rather
|
|
53
|
-
// than tripping every query (matches "absent ⇒ never stale").
|
|
54
|
-
if (entry.dataAsOf == null) return "serve_table";
|
|
55
|
-
const dataAsOfMs = Date.parse(entry.dataAsOf);
|
|
56
|
-
if (Number.isNaN(dataAsOfMs)) return "serve_table";
|
|
57
|
-
const ageSeconds = (now.getTime() - dataAsOfMs) / 1000;
|
|
58
|
-
const stale = ageSeconds > entry.freshnessWindowSeconds;
|
|
59
|
-
if (!stale) return "serve_table";
|
|
60
|
-
return entry.freshnessFallback === "stale_ok" ? "serve_table" : "serve_live";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The instant a currently-fresh, gated entry will cross its window
|
|
65
|
-
* (`dataAsOf + window`), or `null` when the entry has no evaluable window
|
|
66
|
-
* (un-gated). Used to compute the next memo-invalidation instant.
|
|
67
|
-
*/
|
|
68
|
-
function staleSinceMs(entry: FreshnessAwareManifestEntry): number | null {
|
|
69
|
-
if (entry.freshnessWindowSeconds == null || entry.dataAsOf == null) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const dataAsOfMs = Date.parse(entry.dataAsOf);
|
|
73
|
-
if (Number.isNaN(dataAsOfMs)) return null;
|
|
74
|
-
return dataAsOfMs + entry.freshnessWindowSeconds * 1000;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Compute the freshness-filtered Malloy build manifest for `now` from the full
|
|
79
|
-
* retained wire entries.
|
|
80
|
-
*
|
|
81
|
-
* Returns the `sourceEntityId -> { tableName }` map of entries that should
|
|
82
|
-
* substitute their materialized table (fresh, un-gated, or stale-`stale_ok`),
|
|
83
|
-
* plus `nextStaleSince`: the earliest future instant a currently-included,
|
|
84
|
-
* gated entry will cross its window. Because age only ever grows, the
|
|
85
|
-
* fresh→stale transition is one-way, so a single `nextStaleSince` instant is a
|
|
86
|
-
* sufficient memo-invalidation deadline — no per-entry heap is needed. `null`
|
|
87
|
-
* means no included entry has an evaluable window, so the result never changes
|
|
88
|
-
* until the entries are replaced (rebind).
|
|
89
|
-
*/
|
|
90
|
-
export function filterFreshManifest(
|
|
91
|
-
entries: FreshnessManifest,
|
|
92
|
-
now: Date,
|
|
93
|
-
): { manifest: BuildManifest["entries"]; nextStaleSince: number | null } {
|
|
94
|
-
const manifest: BuildManifest["entries"] = {};
|
|
95
|
-
let nextStaleSince: number | null = null;
|
|
96
|
-
const nowMs = now.getTime();
|
|
97
|
-
|
|
98
|
-
for (const [sourceEntityId, entry] of Object.entries(entries)) {
|
|
99
|
-
if (evaluateManifestFreshness(entry, now) === "serve_live") continue;
|
|
100
|
-
manifest[sourceEntityId] = { tableName: entry.tableName };
|
|
101
|
-
|
|
102
|
-
// Only currently-fresh gated entries can flip later; a kept stale
|
|
103
|
-
// `stale_ok` entry (staleSince already in the past) never changes again.
|
|
104
|
-
const flipAt = staleSinceMs(entry);
|
|
105
|
-
if (flipAt != null && flipAt > nowMs) {
|
|
106
|
-
nextStaleSince =
|
|
107
|
-
nextStaleSince == null ? flipAt : Math.min(nextStaleSince, flipAt);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return { manifest, nextStaleSince };
|
|
112
|
-
}
|