@malloy-publisher/server 0.0.198-dev1 → 0.0.198-dev3
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/build.ts +12 -22
- package/dist/instrumentation.mjs +57 -36
- package/dist/server.mjs +2259 -3180
- package/dist/service/schema_worker.mjs +61 -0
- package/package.json +2 -3
- package/src/health.ts +0 -13
- package/src/instrumentation.ts +50 -0
- package/src/server.ts +5 -0
- package/src/service/environment_store.ts +33 -3
- package/src/service/model.ts +3 -226
- package/src/service/package.spec.ts +11 -7
- package/src/service/package.ts +49 -53
- package/src/service/process_stats_reporter.ts +169 -0
- package/src/service/schema_worker.ts +123 -0
- package/src/service/schema_worker_pool.ts +287 -0
- package/tests/integration/concurrent_environment/concurrent_environment.integration.spec.ts +235 -0
- package/dist/compile_worker.mjs +0 -628
- package/src/compile/compile_pool.spec.ts +0 -227
- package/src/compile/compile_pool.ts +0 -729
- package/src/compile/compile_worker.ts +0 -683
- package/src/compile/protocol.ts +0 -251
- package/src/service/model_worker_path.spec.ts +0 -125
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit + light integration tests for the CompileWorkerPool.
|
|
3
|
-
*
|
|
4
|
-
* The pool runs real `worker_threads` workers under the hood. These
|
|
5
|
-
* tests intentionally exercise that path so we catch regressions in
|
|
6
|
-
* RPC routing, lifecycle, and error propagation that wouldn't surface
|
|
7
|
-
* in a pure mock.
|
|
8
|
-
*/
|
|
9
|
-
import {
|
|
10
|
-
afterAll,
|
|
11
|
-
afterEach,
|
|
12
|
-
beforeEach,
|
|
13
|
-
describe,
|
|
14
|
-
expect,
|
|
15
|
-
it,
|
|
16
|
-
} from "bun:test";
|
|
17
|
-
import * as fs from "fs";
|
|
18
|
-
import * as os from "os";
|
|
19
|
-
import * as path from "path";
|
|
20
|
-
import {
|
|
21
|
-
CompileWorkerPool,
|
|
22
|
-
__setCompilePoolForTests,
|
|
23
|
-
getCompileWorkerCount,
|
|
24
|
-
} from "./compile_pool";
|
|
25
|
-
|
|
26
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
27
|
-
// getCompileWorkerCount — env var parsing
|
|
28
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
describe("getCompileWorkerCount", () => {
|
|
31
|
-
const ORIGINAL = process.env.MALLOY_COMPILE_WORKERS;
|
|
32
|
-
|
|
33
|
-
afterEach(() => {
|
|
34
|
-
if (ORIGINAL === undefined) {
|
|
35
|
-
delete process.env.MALLOY_COMPILE_WORKERS;
|
|
36
|
-
} else {
|
|
37
|
-
process.env.MALLOY_COMPILE_WORKERS = ORIGINAL;
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("defaults to 0 when env unset (ship-dark default)", () => {
|
|
42
|
-
delete process.env.MALLOY_COMPILE_WORKERS;
|
|
43
|
-
expect(getCompileWorkerCount()).toBe(0);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("returns 0 when env value is invalid", () => {
|
|
47
|
-
process.env.MALLOY_COMPILE_WORKERS = "not-a-number";
|
|
48
|
-
expect(getCompileWorkerCount()).toBe(0);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("returns 0 when env value is negative", () => {
|
|
52
|
-
process.env.MALLOY_COMPILE_WORKERS = "-2";
|
|
53
|
-
expect(getCompileWorkerCount()).toBe(0);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("honors positive overrides", () => {
|
|
57
|
-
process.env.MALLOY_COMPILE_WORKERS = "4";
|
|
58
|
-
expect(getCompileWorkerCount()).toBe(4);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("treats 0 as a kill switch", () => {
|
|
62
|
-
process.env.MALLOY_COMPILE_WORKERS = "0";
|
|
63
|
-
expect(getCompileWorkerCount()).toBe(0);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
68
|
-
// CompileWorkerPool — disabled pool
|
|
69
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
describe("CompileWorkerPool (disabled)", () => {
|
|
72
|
-
it("reports enabled=false when size is 0", () => {
|
|
73
|
-
const pool = new CompileWorkerPool(0);
|
|
74
|
-
expect(pool.enabled).toBe(false);
|
|
75
|
-
expect(pool.size).toBe(0);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("throws if compile() is called while disabled", async () => {
|
|
79
|
-
const pool = new CompileWorkerPool(0);
|
|
80
|
-
await expect(
|
|
81
|
-
pool.compile({
|
|
82
|
-
packagePath: "/tmp/nowhere",
|
|
83
|
-
modelPath: "x.malloy",
|
|
84
|
-
malloyConfig: undefined as never,
|
|
85
|
-
defaultConnectionName: "duckdb",
|
|
86
|
-
}),
|
|
87
|
-
).rejects.toThrow("disabled");
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("shutdown is a no-op on a disabled pool", async () => {
|
|
91
|
-
const pool = new CompileWorkerPool(0);
|
|
92
|
-
await pool.shutdown();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
97
|
-
// CompileWorkerPool — real worker compiles a trivial Malloy file
|
|
98
|
-
//
|
|
99
|
-
// We spin up a real worker and a real (in-memory) DuckDB connection
|
|
100
|
-
// the worker proxies back to. The model is intentionally tiny so the
|
|
101
|
-
// test runs fast even on cold-start; the goal is to validate the
|
|
102
|
-
// wiring (RPC routing, error propagation, modelDef serialization),
|
|
103
|
-
// not Malloy's compile semantics.
|
|
104
|
-
// ──────────────────────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
describe("CompileWorkerPool (real worker)", () => {
|
|
107
|
-
let pool: CompileWorkerPool | null = null;
|
|
108
|
-
let tempDir: string | null = null;
|
|
109
|
-
|
|
110
|
-
beforeEach(() => {
|
|
111
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "publisher-compile-"));
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
afterEach(async () => {
|
|
115
|
-
if (pool) {
|
|
116
|
-
await pool.shutdown();
|
|
117
|
-
pool = null;
|
|
118
|
-
}
|
|
119
|
-
if (tempDir) {
|
|
120
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
121
|
-
tempDir = null;
|
|
122
|
-
}
|
|
123
|
-
await __setCompilePoolForTests(null);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
afterAll(async () => {
|
|
127
|
-
await __setCompilePoolForTests(null);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("compiles a trivial Malloy model and returns a modelDef", async () => {
|
|
131
|
-
// Lazy-import malloy + duckdb so the disabled-pool tests above
|
|
132
|
-
// don't pay this load cost. Inside this it() we know we're
|
|
133
|
-
// committing to an integration-style run.
|
|
134
|
-
const { MalloyConfig, FixedConnectionMap } = await import(
|
|
135
|
-
"@malloydata/malloy"
|
|
136
|
-
);
|
|
137
|
-
const { DuckDBConnection } = await import("@malloydata/db-duckdb");
|
|
138
|
-
|
|
139
|
-
if (!tempDir) throw new Error("tempDir not initialized");
|
|
140
|
-
|
|
141
|
-
// Trivial Malloy: defines a single source from an in-memory
|
|
142
|
-
// table. No `table('...')` reference means the compiler only
|
|
143
|
-
// does a single `lookupConnection` lookup (for the digest); no
|
|
144
|
-
// schema RPCs round-trip. This keeps the test deterministic
|
|
145
|
-
// and fast.
|
|
146
|
-
const modelSrc = `
|
|
147
|
-
source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
148
|
-
measure: total is a.sum()
|
|
149
|
-
}
|
|
150
|
-
`.trim();
|
|
151
|
-
fs.writeFileSync(path.join(tempDir, "trivial.malloy"), modelSrc);
|
|
152
|
-
|
|
153
|
-
const duckdb = new DuckDBConnection("duckdb", ":memory:");
|
|
154
|
-
const connections = new FixedConnectionMap(
|
|
155
|
-
new Map([["duckdb", duckdb]]),
|
|
156
|
-
"duckdb",
|
|
157
|
-
);
|
|
158
|
-
const malloyConfig = new MalloyConfig({ connections: {} });
|
|
159
|
-
malloyConfig.wrapConnections(() => connections);
|
|
160
|
-
|
|
161
|
-
pool = new CompileWorkerPool(1);
|
|
162
|
-
const outcome = await pool.compile({
|
|
163
|
-
packagePath: tempDir,
|
|
164
|
-
modelPath: "trivial.malloy",
|
|
165
|
-
malloyConfig,
|
|
166
|
-
defaultConnectionName: "duckdb",
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
expect(outcome.modelDef).toBeDefined();
|
|
170
|
-
// Sanity-check that sources/queries were extracted, since
|
|
171
|
-
// these go through the same code as the in-process path.
|
|
172
|
-
expect(Array.isArray(outcome.sources)).toBe(true);
|
|
173
|
-
expect(outcome.compileDurationMs).toBeGreaterThan(0);
|
|
174
|
-
|
|
175
|
-
await duckdb.close();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("propagates compilation errors from the worker as Error instances", async () => {
|
|
179
|
-
const { MalloyConfig, FixedConnectionMap } = await import(
|
|
180
|
-
"@malloydata/malloy"
|
|
181
|
-
);
|
|
182
|
-
const { DuckDBConnection } = await import("@malloydata/db-duckdb");
|
|
183
|
-
if (!tempDir) throw new Error("tempDir not initialized");
|
|
184
|
-
|
|
185
|
-
// Syntactically broken Malloy — should produce a MalloyError
|
|
186
|
-
// inside the worker that the pool re-wraps into a
|
|
187
|
-
// ModelCompilationError on the main thread.
|
|
188
|
-
fs.writeFileSync(
|
|
189
|
-
path.join(tempDir, "broken.malloy"),
|
|
190
|
-
"source: bogus is duckdb.sql('not really sql')\n measure: missing_close_brace is 1",
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const duckdb = new DuckDBConnection("duckdb", ":memory:");
|
|
194
|
-
const connections = new FixedConnectionMap(
|
|
195
|
-
new Map([["duckdb", duckdb]]),
|
|
196
|
-
"duckdb",
|
|
197
|
-
);
|
|
198
|
-
const malloyConfig = new MalloyConfig({ connections: {} });
|
|
199
|
-
malloyConfig.wrapConnections(() => connections);
|
|
200
|
-
|
|
201
|
-
pool = new CompileWorkerPool(1);
|
|
202
|
-
await expect(
|
|
203
|
-
pool.compile({
|
|
204
|
-
packagePath: tempDir,
|
|
205
|
-
modelPath: "broken.malloy",
|
|
206
|
-
malloyConfig,
|
|
207
|
-
defaultConnectionName: "duckdb",
|
|
208
|
-
}),
|
|
209
|
-
).rejects.toBeInstanceOf(Error);
|
|
210
|
-
|
|
211
|
-
await duckdb.close();
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("rejects compile() after shutdown()", async () => {
|
|
215
|
-
const { MalloyConfig } = await import("@malloydata/malloy");
|
|
216
|
-
pool = new CompileWorkerPool(1);
|
|
217
|
-
await pool.shutdown();
|
|
218
|
-
await expect(
|
|
219
|
-
pool.compile({
|
|
220
|
-
packagePath: "/tmp/nowhere",
|
|
221
|
-
modelPath: "x.malloy",
|
|
222
|
-
malloyConfig: new MalloyConfig({ connections: {} }),
|
|
223
|
-
defaultConnectionName: "duckdb",
|
|
224
|
-
}),
|
|
225
|
-
).rejects.toThrow("shutting down");
|
|
226
|
-
});
|
|
227
|
-
});
|