@malloy-publisher/server 0.10.3 → 0.10.4
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 +14 -8
- package/dist/instrumentation.mjs +1 -1
- package/dist/package_load_worker.mjs +81 -48
- package/dist/server.mjs +216 -78
- package/package.json +1 -1
package/dist/app/api-doc.yaml
CHANGED
|
@@ -7990,17 +7990,18 @@ components:
|
|
|
7990
7990
|
BuildPlan:
|
|
7991
7991
|
type: object
|
|
7992
7992
|
description: |
|
|
7993
|
-
The package's persist build plan
|
|
7994
|
-
|
|
7995
|
-
identity/naming/realization.
|
|
7996
|
-
are intentionally omitted until they
|
|
7993
|
+
The package's persist build plan, from Malloy's build targets: the order
|
|
7994
|
+
its persist sources build in, the tables each reads, and the minimal
|
|
7995
|
+
per-source detail a caller needs to assign identity/naming/realization.
|
|
7996
|
+
Policy and connection capability are intentionally omitted until they
|
|
7997
|
+
carry real data.
|
|
7997
7998
|
required: [graphs, sources]
|
|
7998
7999
|
properties:
|
|
7999
8000
|
graphs:
|
|
8000
8001
|
type: array
|
|
8001
8002
|
description: >-
|
|
8002
8003
|
Dependency-ordered build graphs. One per connection per model, not one
|
|
8003
|
-
per connection: a model contributes a graph for each connection its
|
|
8004
|
+
per connection: a model contributes a graph for each connection its
|
|
8004
8005
|
persist sources use, and a package's plan concatenates every model's, so
|
|
8005
8006
|
the same source appears in the graph of every model that imports it.
|
|
8006
8007
|
items:
|
|
@@ -8143,8 +8144,9 @@ components:
|
|
|
8143
8144
|
nodes:
|
|
8144
8145
|
type: array
|
|
8145
8146
|
description:
|
|
8146
|
-
|
|
8147
|
-
|
|
8147
|
+
A single level holding every persist source of the graph, in build
|
|
8148
|
+
order. Each node's `dependsOn` names every source on every table it
|
|
8149
|
+
reads, so the edges are complete without walking anything.
|
|
8148
8150
|
items:
|
|
8149
8151
|
type: array
|
|
8150
8152
|
items:
|
|
@@ -8159,7 +8161,11 @@ components:
|
|
|
8159
8161
|
description: "sourceName@modelURL"
|
|
8160
8162
|
dependsOn:
|
|
8161
8163
|
type: array
|
|
8162
|
-
description:
|
|
8164
|
+
description: >-
|
|
8165
|
+
sourceIDs of the sources on the tables this source reads. Sources on
|
|
8166
|
+
one table (a source and its extensions) read none of each other. A
|
|
8167
|
+
source Malloy cannot plan, which is refused rather than built, lists
|
|
8168
|
+
none.
|
|
8163
8169
|
items:
|
|
8164
8170
|
type: string
|
|
8165
8171
|
|
package/dist/instrumentation.mjs
CHANGED
|
@@ -164831,7 +164831,7 @@ var init_telemetry = __esm(() => {
|
|
|
164831
164831
|
});
|
|
164832
164832
|
|
|
164833
164833
|
// package.json
|
|
164834
|
-
var version = "0.10.
|
|
164834
|
+
var version = "0.10.4";
|
|
164835
164835
|
var init_package = () => {};
|
|
164836
164836
|
|
|
164837
164837
|
// src/version.ts
|
|
@@ -22216,7 +22216,7 @@ async function compileSynthesizedPreaggregation(args) {
|
|
|
22216
22216
|
const { runtime, importBaseURL } = await args.getRuntime(new Map([[synthesizedURL.href, text]]));
|
|
22217
22217
|
const materializer = runtime.loadModel(synthesizedURL, { importBaseURL });
|
|
22218
22218
|
const model = await materializer.getModel();
|
|
22219
|
-
return { plans, model, materializer, text };
|
|
22219
|
+
return { plans, model, materializer, runtime, text };
|
|
22220
22220
|
}
|
|
22221
22221
|
async function tryCompileSynthesizedPreaggregation(args) {
|
|
22222
22222
|
try {
|
|
@@ -23215,27 +23215,47 @@ function composeDeclaredQueryMetadata(layers) {
|
|
|
23215
23215
|
const resolved = Object.assign({}, ...ordered);
|
|
23216
23216
|
return Object.keys(resolved).length > 0 ? resolved : null;
|
|
23217
23217
|
}
|
|
23218
|
-
function
|
|
23219
|
-
|
|
23220
|
-
}
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
for (const dep of node.dependsOn) {
|
|
23227
|
-
yield* visit(dep);
|
|
23218
|
+
function sourceBuildOf(build, unbuildable) {
|
|
23219
|
+
const order = [];
|
|
23220
|
+
const dependsOn = {};
|
|
23221
|
+
for (const target of build.targets) {
|
|
23222
|
+
const reads = target.dependsOn.flatMap((t) => t.sources.map((s) => s.sourceID));
|
|
23223
|
+
for (const source of target.sources) {
|
|
23224
|
+
order.push(source.sourceID);
|
|
23225
|
+
dependsOn[source.sourceID] = reads;
|
|
23228
23226
|
}
|
|
23229
|
-
|
|
23230
|
-
|
|
23227
|
+
}
|
|
23228
|
+
for (const { source } of unbuildable) {
|
|
23229
|
+
if (source.connectionName !== build.connectionName)
|
|
23230
|
+
continue;
|
|
23231
|
+
order.push(source.sourceID);
|
|
23232
|
+
dependsOn[source.sourceID] = [];
|
|
23233
|
+
}
|
|
23234
|
+
return { connectionName: build.connectionName, order, dependsOn };
|
|
23235
|
+
}
|
|
23236
|
+
function sourceBuildsOf(targets) {
|
|
23237
|
+
const builds = targets.connections.map((c) => sourceBuildOf(c, targets.unbuildable));
|
|
23238
|
+
const planned = new Set(builds.map((b) => b.connectionName));
|
|
23239
|
+
for (const { source } of targets.unbuildable) {
|
|
23240
|
+
if (planned.has(source.connectionName))
|
|
23241
|
+
continue;
|
|
23242
|
+
planned.add(source.connectionName);
|
|
23243
|
+
builds.push(sourceBuildOf({ connectionName: source.connectionName, targets: [] }, targets.unbuildable));
|
|
23244
|
+
}
|
|
23245
|
+
return builds;
|
|
23246
|
+
}
|
|
23247
|
+
function plannedSources(targets) {
|
|
23248
|
+
return [
|
|
23249
|
+
...targets.connections.flatMap((c) => c.targets.flatMap((t) => t.sources)),
|
|
23250
|
+
...targets.unbuildable.map((u) => u.source)
|
|
23251
|
+
];
|
|
23252
|
+
}
|
|
23253
|
+
function* sourcesInBuildOrder(build, sources) {
|
|
23254
|
+
for (const sourceID of build.order) {
|
|
23255
|
+
const source = sources[sourceID];
|
|
23231
23256
|
if (source)
|
|
23232
23257
|
yield source;
|
|
23233
23258
|
}
|
|
23234
|
-
for (const level of graph.nodes) {
|
|
23235
|
-
for (const node of level) {
|
|
23236
|
-
yield* visit(node);
|
|
23237
|
-
}
|
|
23238
|
-
}
|
|
23239
23259
|
}
|
|
23240
23260
|
function computeSourceEntityId(source, connectionDigests) {
|
|
23241
23261
|
return source.makeBuildId(connectionDigests[source.connectionName], addressPartitionLayout(source, source.getSQL()));
|
|
@@ -23324,7 +23344,7 @@ async function classifyPersistSourceGate(persistSource, modelDef, materializer,
|
|
|
23324
23344
|
}
|
|
23325
23345
|
function emptyBuildPlanParts() {
|
|
23326
23346
|
return {
|
|
23327
|
-
|
|
23347
|
+
builds: [],
|
|
23328
23348
|
sources: {},
|
|
23329
23349
|
sourceModelPaths: {},
|
|
23330
23350
|
droppedPersistSources: [],
|
|
@@ -23333,7 +23353,7 @@ function emptyBuildPlanParts() {
|
|
|
23333
23353
|
};
|
|
23334
23354
|
}
|
|
23335
23355
|
function mergeBuildPlanParts(into, from) {
|
|
23336
|
-
into.
|
|
23356
|
+
into.builds.push(...from.builds);
|
|
23337
23357
|
Object.assign(into.sources, from.sources);
|
|
23338
23358
|
Object.assign(into.sourceModelPaths, from.sourceModelPaths);
|
|
23339
23359
|
into.droppedPersistSources.push(...from.droppedPersistSources);
|
|
@@ -23341,7 +23361,14 @@ function mergeBuildPlanParts(into, from) {
|
|
|
23341
23361
|
Object.assign(into.sourceGateOutcomes, from.sourceGateOutcomes);
|
|
23342
23362
|
}
|
|
23343
23363
|
async function collectModelBuildPlan(parts, args) {
|
|
23344
|
-
const {
|
|
23364
|
+
const {
|
|
23365
|
+
modelPath,
|
|
23366
|
+
packagePath,
|
|
23367
|
+
materializer,
|
|
23368
|
+
malloyModel,
|
|
23369
|
+
runtime,
|
|
23370
|
+
getRuntime
|
|
23371
|
+
} = args;
|
|
23345
23372
|
const synthesized = args.synthesized !== undefined ? args.synthesized : await tryCompileSynthesizedPreaggregation({
|
|
23346
23373
|
packagePath,
|
|
23347
23374
|
modelPath,
|
|
@@ -23349,14 +23376,14 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23349
23376
|
contents: malloyModel._modelDef?.contents ?? {}
|
|
23350
23377
|
});
|
|
23351
23378
|
if (synthesized) {
|
|
23352
|
-
const
|
|
23353
|
-
|
|
23354
|
-
parts.graphs.push(...rollupPlan.graphs);
|
|
23379
|
+
const rollupTargets = await synthesized.runtime.getBuildTargets(synthesized.model);
|
|
23380
|
+
parts.builds.push(...sourceBuildsOf(rollupTargets));
|
|
23355
23381
|
const planByName = new Map(synthesized.plans.map((p) => [p.rollupSourceName, p]));
|
|
23356
23382
|
let rollupClassificationCtx;
|
|
23357
|
-
for (const
|
|
23358
|
-
if (!
|
|
23383
|
+
for (const source of plannedSources(rollupTargets)) {
|
|
23384
|
+
if (!planByName.has(source.name))
|
|
23359
23385
|
continue;
|
|
23386
|
+
const sourceID = source.sourceID;
|
|
23360
23387
|
parts.sources[sourceID] = source;
|
|
23361
23388
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
23362
23389
|
const rollup = planByName.get(source.name);
|
|
@@ -23375,23 +23402,25 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23375
23402
|
const modelTag = malloyModel.modelAnnotations.parseAsTag("!").tag;
|
|
23376
23403
|
if (!modelTag.has("experimental", "persistence"))
|
|
23377
23404
|
return;
|
|
23378
|
-
const
|
|
23379
|
-
for (const msg of
|
|
23405
|
+
const targets = await runtime.getBuildTargets(malloyModel);
|
|
23406
|
+
for (const msg of targets.tagParseLog) {
|
|
23380
23407
|
logger.warn("Persist annotation issue", {
|
|
23381
23408
|
modelPath,
|
|
23382
23409
|
message: msg.message,
|
|
23383
23410
|
severity: msg.severity
|
|
23384
23411
|
});
|
|
23385
23412
|
}
|
|
23386
|
-
const
|
|
23413
|
+
const planned = plannedSources(targets);
|
|
23414
|
+
const recognizedNames = new Set(planned.map((s) => s.name));
|
|
23387
23415
|
for (const name of detectDroppedPersistSources(malloyModel, recognizedNames)) {
|
|
23388
23416
|
parts.droppedPersistSources.push({ name, modelPath });
|
|
23389
23417
|
}
|
|
23390
|
-
if (
|
|
23418
|
+
if (planned.length === 0)
|
|
23391
23419
|
return;
|
|
23392
|
-
parts.
|
|
23420
|
+
parts.builds.push(...sourceBuildsOf(targets));
|
|
23393
23421
|
let classificationCtx;
|
|
23394
|
-
for (const
|
|
23422
|
+
for (const source of planned) {
|
|
23423
|
+
const sourceID = source.sourceID;
|
|
23395
23424
|
parts.sources[sourceID] = source;
|
|
23396
23425
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
23397
23426
|
if (!classificationCtx) {
|
|
@@ -23404,28 +23433,30 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23404
23433
|
parts.sourceGateOutcomes[sourceID] = await classifyPersistSourceGate(source, classificationCtx.modelDef, materializer, classificationCtx.deps, modelPath);
|
|
23405
23434
|
}
|
|
23406
23435
|
}
|
|
23407
|
-
async function resolveConnectionDigests(connections,
|
|
23436
|
+
async function resolveConnectionDigests(connections, builds, onSkipped) {
|
|
23408
23437
|
const connectionDigests = {};
|
|
23409
|
-
for (const
|
|
23410
|
-
const conn = connections.get(
|
|
23438
|
+
for (const build of builds) {
|
|
23439
|
+
const conn = connections.get(build.connectionName);
|
|
23411
23440
|
if (!conn) {
|
|
23412
|
-
onSkipped(
|
|
23441
|
+
onSkipped(build.connectionName);
|
|
23413
23442
|
continue;
|
|
23414
23443
|
}
|
|
23415
|
-
if (!connectionDigests[
|
|
23416
|
-
connectionDigests[
|
|
23444
|
+
if (!connectionDigests[build.connectionName]) {
|
|
23445
|
+
connectionDigests[build.connectionName] = await conn.getDigest();
|
|
23417
23446
|
}
|
|
23418
23447
|
}
|
|
23419
23448
|
return connectionDigests;
|
|
23420
23449
|
}
|
|
23421
|
-
function deriveBuildPlan(
|
|
23450
|
+
function deriveBuildPlan(builds, sources, connectionDigests, sourceNames, sourceModelPaths, packageMaterialization, options) {
|
|
23422
23451
|
const include = sourceNames ? new Set(sourceNames) : null;
|
|
23423
|
-
const wireGraphs =
|
|
23424
|
-
connectionName:
|
|
23425
|
-
nodes:
|
|
23426
|
-
sourceID
|
|
23427
|
-
|
|
23428
|
-
|
|
23452
|
+
const wireGraphs = builds.map((build) => ({
|
|
23453
|
+
connectionName: build.connectionName,
|
|
23454
|
+
nodes: [
|
|
23455
|
+
build.order.map((sourceID) => ({
|
|
23456
|
+
sourceID,
|
|
23457
|
+
dependsOn: build.dependsOn[sourceID] ?? []
|
|
23458
|
+
}))
|
|
23459
|
+
]
|
|
23429
23460
|
}));
|
|
23430
23461
|
const wireSources = {};
|
|
23431
23462
|
const refusedSources = {};
|
|
@@ -23514,7 +23545,7 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames, source
|
|
|
23514
23545
|
function deriveBuildPlanOutcome(compiled, materializationConfig) {
|
|
23515
23546
|
const droppedPersistSources = compiled.droppedPersistSources ?? [];
|
|
23516
23547
|
const incrementalDeclarations = collectIncrementalDeclarations(compiled.sources);
|
|
23517
|
-
const plan = compiled.
|
|
23548
|
+
const plan = compiled.builds.length === 0 ? null : deriveBuildPlan(compiled.builds, compiled.sources, compiled.connectionDigests, undefined, compiled.sourceModelPaths, materializationConfig, {
|
|
23518
23549
|
preaggregatePlans: compiled.preaggregatePlans,
|
|
23519
23550
|
sourceGateOutcomes: compiled.sourceGateOutcomes
|
|
23520
23551
|
});
|
|
@@ -24474,6 +24505,7 @@ async function compileMalloyModel(job, malloyConfig, modelPath, load) {
|
|
|
24474
24505
|
modelPath,
|
|
24475
24506
|
materializer: mm,
|
|
24476
24507
|
model: compiled,
|
|
24508
|
+
runtime,
|
|
24477
24509
|
importBaseURL,
|
|
24478
24510
|
queries,
|
|
24479
24511
|
sources
|
|
@@ -24559,6 +24591,7 @@ async function prepareForMainThread(job, malloyConfig, load, model) {
|
|
|
24559
24591
|
packagePath: job.packagePath,
|
|
24560
24592
|
materializer,
|
|
24561
24593
|
malloyModel: model.model,
|
|
24594
|
+
runtime: model.runtime,
|
|
24562
24595
|
getRuntime: companionRuntime,
|
|
24563
24596
|
synthesized: companion
|
|
24564
24597
|
});
|
|
@@ -24798,8 +24831,8 @@ async function finishWorkerBuildPlan(malloyConfig, modelPaths, load, materializa
|
|
|
24798
24831
|
}
|
|
24799
24832
|
const connections = await resolvePackageConnections({
|
|
24800
24833
|
getMalloyConnection: (name) => malloyConfig.connections.lookupConnection(name)
|
|
24801
|
-
}, parts.
|
|
24802
|
-
const connectionDigests = await resolveConnectionDigests(connections, parts.
|
|
24834
|
+
}, parts.builds.map((b) => b.connectionName));
|
|
24835
|
+
const connectionDigests = await resolveConnectionDigests(connections, parts.builds, (name) => digestSkipped.push(name));
|
|
24803
24836
|
const outcome = deriveBuildPlanOutcome({ ...parts, connectionDigests }, materializationConfig);
|
|
24804
24837
|
return {
|
|
24805
24838
|
ok: true,
|
package/dist/server.mjs
CHANGED
|
@@ -164832,7 +164832,7 @@ var init_telemetry = __esm(() => {
|
|
|
164832
164832
|
});
|
|
164833
164833
|
|
|
164834
164834
|
// package.json
|
|
164835
|
-
var version = "0.10.
|
|
164835
|
+
var version = "0.10.4";
|
|
164836
164836
|
var init_package = () => {};
|
|
164837
164837
|
|
|
164838
164838
|
// src/version.ts
|
|
@@ -348115,7 +348115,7 @@ async function compileSynthesizedPreaggregation(args) {
|
|
|
348115
348115
|
const { runtime, importBaseURL } = await args.getRuntime(new Map([[synthesizedURL.href, text]]));
|
|
348116
348116
|
const materializer = runtime.loadModel(synthesizedURL, { importBaseURL });
|
|
348117
348117
|
const model = await materializer.getModel();
|
|
348118
|
-
return { plans, model, materializer, text };
|
|
348118
|
+
return { plans, model, materializer, runtime, text };
|
|
348119
348119
|
}
|
|
348120
348120
|
async function tryCompileSynthesizedPreaggregation(args) {
|
|
348121
348121
|
try {
|
|
@@ -348302,27 +348302,47 @@ function composeDeclaredQueryMetadata(layers) {
|
|
|
348302
348302
|
const resolved = Object.assign({}, ...ordered);
|
|
348303
348303
|
return Object.keys(resolved).length > 0 ? resolved : null;
|
|
348304
348304
|
}
|
|
348305
|
-
function
|
|
348306
|
-
|
|
348307
|
-
}
|
|
348308
|
-
|
|
348309
|
-
|
|
348310
|
-
|
|
348311
|
-
|
|
348312
|
-
|
|
348313
|
-
for (const dep of node.dependsOn) {
|
|
348314
|
-
yield* visit(dep);
|
|
348305
|
+
function sourceBuildOf(build, unbuildable) {
|
|
348306
|
+
const order = [];
|
|
348307
|
+
const dependsOn = {};
|
|
348308
|
+
for (const target of build.targets) {
|
|
348309
|
+
const reads = target.dependsOn.flatMap((t2) => t2.sources.map((s2) => s2.sourceID));
|
|
348310
|
+
for (const source of target.sources) {
|
|
348311
|
+
order.push(source.sourceID);
|
|
348312
|
+
dependsOn[source.sourceID] = reads;
|
|
348315
348313
|
}
|
|
348316
|
-
|
|
348317
|
-
|
|
348314
|
+
}
|
|
348315
|
+
for (const { source } of unbuildable) {
|
|
348316
|
+
if (source.connectionName !== build.connectionName)
|
|
348317
|
+
continue;
|
|
348318
|
+
order.push(source.sourceID);
|
|
348319
|
+
dependsOn[source.sourceID] = [];
|
|
348320
|
+
}
|
|
348321
|
+
return { connectionName: build.connectionName, order, dependsOn };
|
|
348322
|
+
}
|
|
348323
|
+
function sourceBuildsOf(targets) {
|
|
348324
|
+
const builds = targets.connections.map((c) => sourceBuildOf(c, targets.unbuildable));
|
|
348325
|
+
const planned = new Set(builds.map((b) => b.connectionName));
|
|
348326
|
+
for (const { source } of targets.unbuildable) {
|
|
348327
|
+
if (planned.has(source.connectionName))
|
|
348328
|
+
continue;
|
|
348329
|
+
planned.add(source.connectionName);
|
|
348330
|
+
builds.push(sourceBuildOf({ connectionName: source.connectionName, targets: [] }, targets.unbuildable));
|
|
348331
|
+
}
|
|
348332
|
+
return builds;
|
|
348333
|
+
}
|
|
348334
|
+
function plannedSources(targets) {
|
|
348335
|
+
return [
|
|
348336
|
+
...targets.connections.flatMap((c) => c.targets.flatMap((t2) => t2.sources)),
|
|
348337
|
+
...targets.unbuildable.map((u) => u.source)
|
|
348338
|
+
];
|
|
348339
|
+
}
|
|
348340
|
+
function* sourcesInBuildOrder(build, sources) {
|
|
348341
|
+
for (const sourceID of build.order) {
|
|
348342
|
+
const source = sources[sourceID];
|
|
348318
348343
|
if (source)
|
|
348319
348344
|
yield source;
|
|
348320
348345
|
}
|
|
348321
|
-
for (const level of graph.nodes) {
|
|
348322
|
-
for (const node of level) {
|
|
348323
|
-
yield* visit(node);
|
|
348324
|
-
}
|
|
348325
|
-
}
|
|
348326
348346
|
}
|
|
348327
348347
|
function computeSourceEntityId(source, connectionDigests) {
|
|
348328
348348
|
return source.makeBuildId(connectionDigests[source.connectionName], addressPartitionLayout(source, source.getSQL()));
|
|
@@ -348411,7 +348431,7 @@ async function classifyPersistSourceGate(persistSource, modelDef, materializer,
|
|
|
348411
348431
|
}
|
|
348412
348432
|
function emptyBuildPlanParts() {
|
|
348413
348433
|
return {
|
|
348414
|
-
|
|
348434
|
+
builds: [],
|
|
348415
348435
|
sources: {},
|
|
348416
348436
|
sourceModelPaths: {},
|
|
348417
348437
|
droppedPersistSources: [],
|
|
@@ -348420,7 +348440,7 @@ function emptyBuildPlanParts() {
|
|
|
348420
348440
|
};
|
|
348421
348441
|
}
|
|
348422
348442
|
function mergeBuildPlanParts(into, from) {
|
|
348423
|
-
into.
|
|
348443
|
+
into.builds.push(...from.builds);
|
|
348424
348444
|
Object.assign(into.sources, from.sources);
|
|
348425
348445
|
Object.assign(into.sourceModelPaths, from.sourceModelPaths);
|
|
348426
348446
|
into.droppedPersistSources.push(...from.droppedPersistSources);
|
|
@@ -348428,7 +348448,14 @@ function mergeBuildPlanParts(into, from) {
|
|
|
348428
348448
|
Object.assign(into.sourceGateOutcomes, from.sourceGateOutcomes);
|
|
348429
348449
|
}
|
|
348430
348450
|
async function collectModelBuildPlan(parts, args) {
|
|
348431
|
-
const {
|
|
348451
|
+
const {
|
|
348452
|
+
modelPath,
|
|
348453
|
+
packagePath,
|
|
348454
|
+
materializer,
|
|
348455
|
+
malloyModel,
|
|
348456
|
+
runtime,
|
|
348457
|
+
getRuntime
|
|
348458
|
+
} = args;
|
|
348432
348459
|
const synthesized = args.synthesized !== undefined ? args.synthesized : await tryCompileSynthesizedPreaggregation({
|
|
348433
348460
|
packagePath,
|
|
348434
348461
|
modelPath,
|
|
@@ -348436,14 +348463,14 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348436
348463
|
contents: malloyModel._modelDef?.contents ?? {}
|
|
348437
348464
|
});
|
|
348438
348465
|
if (synthesized) {
|
|
348439
|
-
const
|
|
348440
|
-
|
|
348441
|
-
parts.graphs.push(...rollupPlan.graphs);
|
|
348466
|
+
const rollupTargets = await synthesized.runtime.getBuildTargets(synthesized.model);
|
|
348467
|
+
parts.builds.push(...sourceBuildsOf(rollupTargets));
|
|
348442
348468
|
const planByName = new Map(synthesized.plans.map((p) => [p.rollupSourceName, p]));
|
|
348443
348469
|
let rollupClassificationCtx;
|
|
348444
|
-
for (const
|
|
348445
|
-
if (!
|
|
348470
|
+
for (const source of plannedSources(rollupTargets)) {
|
|
348471
|
+
if (!planByName.has(source.name))
|
|
348446
348472
|
continue;
|
|
348473
|
+
const sourceID = source.sourceID;
|
|
348447
348474
|
parts.sources[sourceID] = source;
|
|
348448
348475
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
348449
348476
|
const rollup = planByName.get(source.name);
|
|
@@ -348462,23 +348489,25 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348462
348489
|
const modelTag = malloyModel.modelAnnotations.parseAsTag("!").tag;
|
|
348463
348490
|
if (!modelTag.has("experimental", "persistence"))
|
|
348464
348491
|
return;
|
|
348465
|
-
const
|
|
348466
|
-
for (const msg of
|
|
348492
|
+
const targets = await runtime.getBuildTargets(malloyModel);
|
|
348493
|
+
for (const msg of targets.tagParseLog) {
|
|
348467
348494
|
logger.warn("Persist annotation issue", {
|
|
348468
348495
|
modelPath,
|
|
348469
348496
|
message: msg.message,
|
|
348470
348497
|
severity: msg.severity
|
|
348471
348498
|
});
|
|
348472
348499
|
}
|
|
348473
|
-
const
|
|
348500
|
+
const planned = plannedSources(targets);
|
|
348501
|
+
const recognizedNames = new Set(planned.map((s2) => s2.name));
|
|
348474
348502
|
for (const name of detectDroppedPersistSources(malloyModel, recognizedNames)) {
|
|
348475
348503
|
parts.droppedPersistSources.push({ name, modelPath });
|
|
348476
348504
|
}
|
|
348477
|
-
if (
|
|
348505
|
+
if (planned.length === 0)
|
|
348478
348506
|
return;
|
|
348479
|
-
parts.
|
|
348507
|
+
parts.builds.push(...sourceBuildsOf(targets));
|
|
348480
348508
|
let classificationCtx;
|
|
348481
|
-
for (const
|
|
348509
|
+
for (const source of planned) {
|
|
348510
|
+
const sourceID = source.sourceID;
|
|
348482
348511
|
parts.sources[sourceID] = source;
|
|
348483
348512
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
348484
348513
|
if (!classificationCtx) {
|
|
@@ -348491,28 +348520,30 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348491
348520
|
parts.sourceGateOutcomes[sourceID] = await classifyPersistSourceGate(source, classificationCtx.modelDef, materializer, classificationCtx.deps, modelPath);
|
|
348492
348521
|
}
|
|
348493
348522
|
}
|
|
348494
|
-
async function resolveConnectionDigests(connections,
|
|
348523
|
+
async function resolveConnectionDigests(connections, builds, onSkipped) {
|
|
348495
348524
|
const connectionDigests = {};
|
|
348496
|
-
for (const
|
|
348497
|
-
const conn = connections.get(
|
|
348525
|
+
for (const build of builds) {
|
|
348526
|
+
const conn = connections.get(build.connectionName);
|
|
348498
348527
|
if (!conn) {
|
|
348499
|
-
onSkipped(
|
|
348528
|
+
onSkipped(build.connectionName);
|
|
348500
348529
|
continue;
|
|
348501
348530
|
}
|
|
348502
|
-
if (!connectionDigests[
|
|
348503
|
-
connectionDigests[
|
|
348531
|
+
if (!connectionDigests[build.connectionName]) {
|
|
348532
|
+
connectionDigests[build.connectionName] = await conn.getDigest();
|
|
348504
348533
|
}
|
|
348505
348534
|
}
|
|
348506
348535
|
return connectionDigests;
|
|
348507
348536
|
}
|
|
348508
|
-
function deriveBuildPlan(
|
|
348537
|
+
function deriveBuildPlan(builds, sources, connectionDigests, sourceNames, sourceModelPaths, packageMaterialization, options) {
|
|
348509
348538
|
const include = sourceNames ? new Set(sourceNames) : null;
|
|
348510
|
-
const wireGraphs =
|
|
348511
|
-
connectionName:
|
|
348512
|
-
nodes:
|
|
348513
|
-
sourceID
|
|
348514
|
-
|
|
348515
|
-
|
|
348539
|
+
const wireGraphs = builds.map((build) => ({
|
|
348540
|
+
connectionName: build.connectionName,
|
|
348541
|
+
nodes: [
|
|
348542
|
+
build.order.map((sourceID) => ({
|
|
348543
|
+
sourceID,
|
|
348544
|
+
dependsOn: build.dependsOn[sourceID] ?? []
|
|
348545
|
+
}))
|
|
348546
|
+
]
|
|
348516
348547
|
}));
|
|
348517
348548
|
const wireSources = {};
|
|
348518
348549
|
const refusedSources = {};
|
|
@@ -348601,7 +348632,7 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames, source
|
|
|
348601
348632
|
function deriveBuildPlanOutcome(compiled, materializationConfig) {
|
|
348602
348633
|
const droppedPersistSources = compiled.droppedPersistSources ?? [];
|
|
348603
348634
|
const incrementalDeclarations = collectIncrementalDeclarations(compiled.sources);
|
|
348604
|
-
const plan = compiled.
|
|
348635
|
+
const plan = compiled.builds.length === 0 ? null : deriveBuildPlan(compiled.builds, compiled.sources, compiled.connectionDigests, undefined, compiled.sourceModelPaths, materializationConfig, {
|
|
348605
348636
|
preaggregatePlans: compiled.preaggregatePlans,
|
|
348606
348637
|
sourceGateOutcomes: compiled.sourceGateOutcomes
|
|
348607
348638
|
});
|
|
@@ -350136,14 +350167,14 @@ async function buildDownstreamIntoStorage(params) {
|
|
|
350136
350167
|
message: `Chained build model did not compile over the rebound parents: ${errMessage(err)}`
|
|
350137
350168
|
});
|
|
350138
350169
|
}
|
|
350139
|
-
const
|
|
350140
|
-
|
|
350141
|
-
|
|
350142
|
-
|
|
350143
|
-
|
|
350144
|
-
|
|
350145
|
-
}
|
|
350170
|
+
const targets = await runtime.getBuildTargets(model, { virtualMap });
|
|
350171
|
+
const unbuildable = targets.unbuildable.find((u) => u.source.name === downstreamName);
|
|
350172
|
+
if (unbuildable) {
|
|
350173
|
+
throw new MaterializationEligibilityError({
|
|
350174
|
+
message: `Chained build model could not plan '${downstreamName}' over ` + `the rebound parents: ${unbuildable.problem.message}`
|
|
350175
|
+
});
|
|
350146
350176
|
}
|
|
350177
|
+
const downstream = plannedSources(targets).find((ps) => ps.name === downstreamName);
|
|
350147
350178
|
if (!downstream) {
|
|
350148
350179
|
throw new MaterializationEligibilityError({
|
|
350149
350180
|
message: `Chained build model did not yield a persist source named ` + `'${downstreamName}' (the downstream references something the ` + `rebound parents don't provide).`
|
|
@@ -359860,6 +359891,98 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
359860
359891
|
module.exports = readdir3;
|
|
359861
359892
|
});
|
|
359862
359893
|
|
|
359894
|
+
// src/service/preaggregation_coverage.ts
|
|
359895
|
+
function rollupMeasuresByMember(plans) {
|
|
359896
|
+
return new Map(plans.map((p2) => [
|
|
359897
|
+
p2.rollupSourceName,
|
|
359898
|
+
new Set(p2.measures.map((m3) => m3.name))
|
|
359899
|
+
]));
|
|
359900
|
+
}
|
|
359901
|
+
function rollupCoverageViolation(query, rollupMeasures) {
|
|
359902
|
+
const ir2 = query;
|
|
359903
|
+
const member = ir2?.compositeResolvedSourceDef?.as;
|
|
359904
|
+
if (!member)
|
|
359905
|
+
return;
|
|
359906
|
+
const merged = rollupMeasures.get(member);
|
|
359907
|
+
if (!merged)
|
|
359908
|
+
return;
|
|
359909
|
+
const memberFields = new Map((ir2?.compositeResolvedSourceDef?.fields ?? []).map((f5) => [
|
|
359910
|
+
f5.as ?? f5.name,
|
|
359911
|
+
f5
|
|
359912
|
+
]));
|
|
359913
|
+
const followed = new Set;
|
|
359914
|
+
let violation;
|
|
359915
|
+
const visitRef = (path16) => {
|
|
359916
|
+
if (!path16)
|
|
359917
|
+
return;
|
|
359918
|
+
if (path16.length !== 1) {
|
|
359919
|
+
violation = `reference ${path16.join(".")} reaches past the rollup's own fields`;
|
|
359920
|
+
return;
|
|
359921
|
+
}
|
|
359922
|
+
const name = path16[0];
|
|
359923
|
+
if (merged.has(name) || followed.has(name))
|
|
359924
|
+
return;
|
|
359925
|
+
followed.add(name);
|
|
359926
|
+
visitExpr(memberFields.get(name)?.e);
|
|
359927
|
+
};
|
|
359928
|
+
const visitExpr = (e2) => {
|
|
359929
|
+
if (violation || !e2 || typeof e2 !== "object")
|
|
359930
|
+
return;
|
|
359931
|
+
if (Array.isArray(e2)) {
|
|
359932
|
+
for (const item of e2)
|
|
359933
|
+
visitExpr(item);
|
|
359934
|
+
return;
|
|
359935
|
+
}
|
|
359936
|
+
const n = e2;
|
|
359937
|
+
if (n.node === "aggregate") {
|
|
359938
|
+
violation = `aggregate ${String(n.function)}() is computed over the rollup's rows, not from a stored partial`;
|
|
359939
|
+
return;
|
|
359940
|
+
}
|
|
359941
|
+
if (n.node === "function_call" && n.overload?.returnType?.expressionType === "aggregate") {
|
|
359942
|
+
violation = `aggregate function ${String(n.name)}() is computed over the rollup's rows, not from a stored partial`;
|
|
359943
|
+
return;
|
|
359944
|
+
}
|
|
359945
|
+
if (n.node === "field") {
|
|
359946
|
+
visitRef(n.path);
|
|
359947
|
+
return;
|
|
359948
|
+
}
|
|
359949
|
+
for (const [key, value] of Object.entries(n)) {
|
|
359950
|
+
if (key === "overload" || key === "at")
|
|
359951
|
+
continue;
|
|
359952
|
+
visitExpr(value);
|
|
359953
|
+
}
|
|
359954
|
+
};
|
|
359955
|
+
const visitStage = (stage) => {
|
|
359956
|
+
if (violation)
|
|
359957
|
+
return;
|
|
359958
|
+
if (stage?.type === "project" || stage?.type === "index") {
|
|
359959
|
+
violation = `${stage.type === "project" ? "a select:" : "an index:"} stage over the rollup returns one row per grain group, not the source's rows`;
|
|
359960
|
+
return;
|
|
359961
|
+
}
|
|
359962
|
+
for (const field of stage?.queryFields ?? []) {
|
|
359963
|
+
if (field.type === "fieldref")
|
|
359964
|
+
visitRef(field.path);
|
|
359965
|
+
else if (field.type === "turtle")
|
|
359966
|
+
visitStage(field.pipeline?.[0]);
|
|
359967
|
+
else
|
|
359968
|
+
visitExpr(field.e);
|
|
359969
|
+
}
|
|
359970
|
+
for (const filter2 of stage?.filterList ?? [])
|
|
359971
|
+
visitExpr(filter2.e);
|
|
359972
|
+
};
|
|
359973
|
+
visitStage(ir2?.pipeline?.[0]);
|
|
359974
|
+
return violation ? `${member}: ${violation}` : undefined;
|
|
359975
|
+
}
|
|
359976
|
+
var RollupCoverageError;
|
|
359977
|
+
var init_preaggregation_coverage = __esm(() => {
|
|
359978
|
+
RollupCoverageError = class RollupCoverageError extends Error {
|
|
359979
|
+
constructor(message) {
|
|
359980
|
+
super(message);
|
|
359981
|
+
this.name = "RollupCoverageError";
|
|
359982
|
+
}
|
|
359983
|
+
};
|
|
359984
|
+
});
|
|
359985
|
+
|
|
359863
359986
|
// src/service/render_tag_targets.ts
|
|
359864
359987
|
function renderTagTargets(queries, sources) {
|
|
359865
359988
|
const targets = [];
|
|
@@ -363064,6 +363187,11 @@ class StorageServeShape {
|
|
|
363064
363187
|
await this.host.ensureSharedQueryModel(this.serveShapeCache.materializer);
|
|
363065
363188
|
const runnable = this.serveShapeCache.materializer.loadRestrictedQuery(queryString);
|
|
363066
363189
|
const probeSQL = await runnable.getSQL({ virtualMap, givens });
|
|
363190
|
+
if (groups.length > 0) {
|
|
363191
|
+
const uncovered = rollupCoverageViolation((await runnable.getPreparedQuery())._query, rollupMeasuresByMember(this.host.rollupPlans()));
|
|
363192
|
+
if (uncovered)
|
|
363193
|
+
throw new RollupCoverageError(uncovered);
|
|
363194
|
+
}
|
|
363067
363195
|
const rollupPaths = [
|
|
363068
363196
|
...buildVirtualMap(groups.flatMap((g2) => g2.members))
|
|
363069
363197
|
].flatMap(([, byHandle]) => [...byHandle.values()]);
|
|
@@ -363264,6 +363392,7 @@ var init_model_serve_shape = __esm(() => {
|
|
|
363264
363392
|
init_materialization_metrics();
|
|
363265
363393
|
init_connection();
|
|
363266
363394
|
init_materialization_serve_transform();
|
|
363395
|
+
init_preaggregation_coverage();
|
|
363267
363396
|
init_preaggregation_serve_bindings();
|
|
363268
363397
|
staleBindingsWithheld = lazyCounter("publisher_storage_bindings_withheld_total", "Storage serve bindings withheld from a routed query because the freshness gate found them stale; each one is read live instead. Until now staleness was visible only as a later live read.");
|
|
363269
363398
|
});
|
|
@@ -363306,6 +363435,7 @@ var init_model = __esm(() => {
|
|
|
363306
363435
|
init_config();
|
|
363307
363436
|
init_preaggregation_synthesis();
|
|
363308
363437
|
init_preaggregation_compile();
|
|
363438
|
+
init_preaggregation_coverage();
|
|
363309
363439
|
init_render_tag_targets();
|
|
363310
363440
|
init_model_info();
|
|
363311
363441
|
init_logger();
|
|
@@ -365487,13 +365617,17 @@ run: ${sourceName ? `${quoteMalloyIdentifier2(sourceName)} -> ` : ""}${quoteMall
|
|
|
365487
365617
|
});
|
|
365488
365618
|
} catch (shapeErr) {
|
|
365489
365619
|
recordStorageServeRouting("live_fallback");
|
|
365490
|
-
|
|
365491
|
-
|
|
365492
|
-
|
|
365493
|
-
|
|
365494
|
-
|
|
365495
|
-
|
|
365496
|
-
|
|
365620
|
+
if (shapeErr instanceof RollupCoverageError) {
|
|
365621
|
+
logger.debug("storage rollup cannot answer this query; serving live", { modelPath: this.modelPath, error: shapeErr.message });
|
|
365622
|
+
} else {
|
|
365623
|
+
const { shapeSources, staleSources } = serveShapeDiagnostics(this.storageServe.serveBindings, this.storageServe.freshServeBindings(Date.now()));
|
|
365624
|
+
logger.info("storage serve-shape ineligible for this query; serving live", {
|
|
365625
|
+
modelPath: this.modelPath,
|
|
365626
|
+
error: shapeErr instanceof Error ? shapeErr.message : String(shapeErr),
|
|
365627
|
+
shapeSources,
|
|
365628
|
+
staleSources
|
|
365629
|
+
});
|
|
365630
|
+
}
|
|
365497
365631
|
}
|
|
365498
365632
|
}
|
|
365499
365633
|
const everyRollupIsStorageBound = this.preaggregateRollupPlans.length > 0 && this.preaggregateRollupPlans.every((plan) => !!plan.storage);
|
|
@@ -365504,10 +365638,13 @@ run: ${sourceName ? `${quoteMalloyIdentifier2(sourceName)} -> ` : ""}${quoteMall
|
|
|
365504
365638
|
givens: querySurfaceGivens,
|
|
365505
365639
|
buildManifest
|
|
365506
365640
|
});
|
|
365641
|
+
const uncovered = rollupCoverageViolation((await candidate.getPreparedQuery())._query, rollupMeasuresByMember(this.preaggregateRollupPlans));
|
|
365642
|
+
if (uncovered)
|
|
365643
|
+
throw new RollupCoverageError(uncovered);
|
|
365507
365644
|
runnable = candidate;
|
|
365508
365645
|
preaggRouted = true;
|
|
365509
365646
|
} catch (preaggErr) {
|
|
365510
|
-
logger.debug("query
|
|
365647
|
+
logger.debug("query cannot be served from the pre-aggregation model; serving live", {
|
|
365511
365648
|
modelPath: this.modelPath,
|
|
365512
365649
|
error: preaggErr instanceof Error ? preaggErr.message : String(preaggErr)
|
|
365513
365650
|
});
|
|
@@ -366140,18 +366277,19 @@ async function compilePackageBuildPlan(pkg, signal) {
|
|
|
366140
366277
|
packagePath: pkg.getPackagePath(),
|
|
366141
366278
|
materializer,
|
|
366142
366279
|
malloyModel,
|
|
366280
|
+
runtime,
|
|
366143
366281
|
getRuntime: (overlay) => Model.getModelRuntime(pkg.getPackagePath(), modelPath, pkg.getMalloyConfig(), { overlay })
|
|
366144
366282
|
});
|
|
366145
366283
|
}
|
|
366146
|
-
const connections = await resolvePackageConnections(pkg, parts.
|
|
366147
|
-
const connectionDigests = await resolveConnectionDigests(connections, parts.
|
|
366284
|
+
const connections = await resolvePackageConnections(pkg, parts.builds.map((b3) => b3.connectionName));
|
|
366285
|
+
const connectionDigests = await resolveConnectionDigests(connections, parts.builds, (connectionName) => {
|
|
366148
366286
|
recordConnectionDigestSkipped();
|
|
366149
366287
|
logger.warn("Skipping connection digest; connection did not resolve", {
|
|
366150
366288
|
connectionName
|
|
366151
366289
|
});
|
|
366152
366290
|
});
|
|
366153
366291
|
return {
|
|
366154
|
-
|
|
366292
|
+
builds: parts.builds,
|
|
366155
366293
|
sources: parts.sources,
|
|
366156
366294
|
connectionDigests,
|
|
366157
366295
|
connections,
|
|
@@ -374591,11 +374729,11 @@ function resolveInstruction(index, persistSource, connectionDigests) {
|
|
|
374591
374729
|
return;
|
|
374592
374730
|
return { instruction, orchestrated: false, sourceEntityId };
|
|
374593
374731
|
}
|
|
374594
|
-
function assertNoTableCollisions(
|
|
374732
|
+
function assertNoTableCollisions(builds, sources, connectionDigests, index) {
|
|
374595
374733
|
const claimedBy = new Map;
|
|
374596
374734
|
const collisions = [];
|
|
374597
|
-
for (const
|
|
374598
|
-
for (const persistSource of
|
|
374735
|
+
for (const build of builds) {
|
|
374736
|
+
for (const persistSource of sourcesInBuildOrder(build, sources)) {
|
|
374599
374737
|
let address;
|
|
374600
374738
|
try {
|
|
374601
374739
|
address = computeSourceEntityId(persistSource, connectionDigests);
|
|
@@ -374605,7 +374743,7 @@ function assertNoTableCollisions(graphs, sources, connectionDigests, index) {
|
|
|
374605
374743
|
const instruction = index.bySourceID.get(persistSource.sourceID) ?? index.bySourceEntityId.get(address);
|
|
374606
374744
|
if (!instruction)
|
|
374607
374745
|
continue;
|
|
374608
|
-
const target = physicalTargetKey(instruction,
|
|
374746
|
+
const target = physicalTargetKey(instruction, build.connectionName);
|
|
374609
374747
|
const claim = claimedBy.get(target);
|
|
374610
374748
|
if (!claim) {
|
|
374611
374749
|
claimedBy.set(target, {
|
|
@@ -374896,8 +375034,8 @@ class MaterializationService {
|
|
|
374896
375034
|
const refusals = [];
|
|
374897
375035
|
const cleared = new Map;
|
|
374898
375036
|
const seen = new Set;
|
|
374899
|
-
for (const
|
|
374900
|
-
for (const persistSource of
|
|
375037
|
+
for (const build of compiled.builds) {
|
|
375038
|
+
for (const persistSource of sourcesInBuildOrder(build, compiled.sources)) {
|
|
374901
375039
|
if (include && !include.has(persistSource.name))
|
|
374902
375040
|
continue;
|
|
374903
375041
|
if (getPersistStorageMode() === "off" && declaredStorage(persistSource)) {
|
|
@@ -375153,7 +375291,7 @@ class MaterializationService {
|
|
|
375153
375291
|
return incrementalRunContext(compiled, run, this.repository);
|
|
375154
375292
|
}
|
|
375155
375293
|
async executeInstructedBuild(compiled, environment, instructions, seedEntries, signal, strict = false, owner, buildMetadata, incremental, priorRefusals = [], clearedTiers) {
|
|
375156
|
-
const {
|
|
375294
|
+
const { builds, sources, connectionDigests, connections } = compiled;
|
|
375157
375295
|
const index = indexInstructions(instructions);
|
|
375158
375296
|
const run = {
|
|
375159
375297
|
compiled,
|
|
@@ -375173,17 +375311,17 @@ class MaterializationService {
|
|
|
375173
375311
|
builtSources: [],
|
|
375174
375312
|
writtenTargets: new Map
|
|
375175
375313
|
};
|
|
375176
|
-
assertNoTableCollisions(
|
|
375314
|
+
assertNoTableCollisions(builds, sources, connectionDigests, index);
|
|
375177
375315
|
try {
|
|
375178
|
-
for (const
|
|
375179
|
-
const connection = connections.get(
|
|
375316
|
+
for (const build of builds) {
|
|
375317
|
+
const connection = connections.get(build.connectionName);
|
|
375180
375318
|
if (!connection) {
|
|
375181
|
-
throw new BadRequestError(`Connection '${
|
|
375319
|
+
throw new BadRequestError(`Connection '${build.connectionName}' not found`);
|
|
375182
375320
|
}
|
|
375183
|
-
for (const persistSource of
|
|
375321
|
+
for (const persistSource of sourcesInBuildOrder(build, sources)) {
|
|
375184
375322
|
if (signal.aborted)
|
|
375185
375323
|
throw new Error("Build cancelled");
|
|
375186
|
-
await this.buildInstructedSource(run, persistSource,
|
|
375324
|
+
await this.buildInstructedSource(run, persistSource, build.connectionName, connection);
|
|
375187
375325
|
}
|
|
375188
375326
|
}
|
|
375189
375327
|
if (run.builtSources.length === 0 && run.failedReasons.length > 0) {
|
package/package.json
CHANGED