@milaboratories/pl-middle-layer 1.71.1 → 1.71.2
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/js_render/index.cjs +43 -25
- package/dist/js_render/index.cjs.map +1 -1
- package/dist/js_render/index.js +43 -25
- package/dist/js_render/index.js.map +1 -1
- package/dist/middle_layer/build_stamp.cjs +1 -1
- package/dist/middle_layer/build_stamp.js +1 -1
- package/dist/middle_layer/driver_kit.cjs +18 -11
- package/dist/middle_layer/driver_kit.cjs.map +1 -1
- package/dist/middle_layer/driver_kit.d.ts +7 -0
- package/dist/middle_layer/driver_kit.d.ts.map +1 -1
- package/dist/middle_layer/driver_kit.js +18 -11
- package/dist/middle_layer/driver_kit.js.map +1 -1
- package/package.json +11 -10
- package/src/js_render/index.ts +81 -44
- package/src/middle_layer/driver_kit.ts +29 -1
package/dist/js_render/index.cjs
CHANGED
|
@@ -2,7 +2,10 @@ const require_index = require("../debug/index.cjs");
|
|
|
2
2
|
const require_context = require("./context.cjs");
|
|
3
3
|
let _milaboratories_computable = require("@milaboratories/computable");
|
|
4
4
|
let quickjs_emscripten = require("quickjs-emscripten");
|
|
5
|
+
let _milaboratories_pl_flight_recorder = require("@milaboratories/pl-flight-recorder");
|
|
5
6
|
//#region src/js_render/index.ts
|
|
7
|
+
/** Memory ceiling applied to every QuickJS runtime that evaluates model code. */
|
|
8
|
+
const QUICK_JS_MEMORY_LIMIT = 1024 * 1024 * 8;
|
|
6
9
|
function logOutputStatus(handle, renderedResult, stable, recalculationCounter, unstableMarker) {
|
|
7
10
|
if (require_index.getDebugFlags().logOutputStatus && (require_index.getDebugFlags().logOutputStatus === "any" || !stable)) if (stable) console.log(`Stable output ${handle} calculated ${renderedResult !== void 0 ? "defined" : "undefined"}; (#${recalculationCounter})`);
|
|
8
11
|
else console.log(`Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== void 0 ? "defined" : "undefined"} (#${recalculationCounter})`);
|
|
@@ -72,7 +75,7 @@ function computableFromRF(env, ctx, fh, codeWithInfo, configKey, ops = {}) {
|
|
|
72
75
|
});
|
|
73
76
|
try {
|
|
74
77
|
const runtime = scope.manage(env.quickJs.newRuntime());
|
|
75
|
-
runtime.setMemoryLimit(
|
|
78
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
76
79
|
runtime.setMaxStackSize(1024 * 320);
|
|
77
80
|
let deadlineSettings;
|
|
78
81
|
runtime.setInterruptHandler(() => {
|
|
@@ -87,30 +90,45 @@ function computableFromRF(env, ctx, fh, codeWithInfo, configKey, ops = {}) {
|
|
|
87
90
|
blockCtx: ctx,
|
|
88
91
|
mlEnv: env
|
|
89
92
|
});
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logOutputStatus(fh.handle, importedResult, cCtx.unstableMarker === void 0, -1, cCtx.unstableMarker);
|
|
98
|
-
return { ir: importedResult };
|
|
99
|
-
}
|
|
100
|
-
let recalculationCounter = 0;
|
|
101
|
-
if (require_index.getDebugFlags().logOutputStatus) console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
|
|
102
|
-
keepVmAlive = true;
|
|
103
|
-
return {
|
|
104
|
-
ir: toBeResolved,
|
|
105
|
-
postprocessValue: (resolved, { unstableMarker, stable }) => {
|
|
106
|
-
for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
|
|
107
|
-
const renderedResult = rCtx.importObjectUniversal(result);
|
|
108
|
-
recalculationCounter++;
|
|
109
|
-
if (require_index.getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);
|
|
110
|
-
logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
|
|
111
|
-
return renderedResult;
|
|
112
|
-
}
|
|
93
|
+
const flightRecorder = env.driverKit.flightRecorder;
|
|
94
|
+
const renderInfo = {
|
|
95
|
+
blockId: ctx.blockId,
|
|
96
|
+
key,
|
|
97
|
+
lambda: fh.handle,
|
|
98
|
+
sandboxMemoryLimit: QUICK_JS_MEMORY_LIMIT,
|
|
99
|
+
getStats: () => ({ ...rCtx.stats })
|
|
113
100
|
};
|
|
101
|
+
return (0, _milaboratories_pl_flight_recorder.recordModelRenderSync)(flightRecorder, renderInfo, () => {
|
|
102
|
+
rCtx.evaluateBundle(code.content);
|
|
103
|
+
const result = rCtx.runCallback(fh.handle);
|
|
104
|
+
rCtx.resetComputableCtx();
|
|
105
|
+
const toBeResolved = rCtx.computableHelper.computablesToResolve;
|
|
106
|
+
if (Object.keys(toBeResolved).length === 0) {
|
|
107
|
+
const importedResult = rCtx.importObjectUniversal(result);
|
|
108
|
+
if (require_index.getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);
|
|
109
|
+
logOutputStatus(fh.handle, importedResult, cCtx.unstableMarker === void 0, -1, cCtx.unstableMarker);
|
|
110
|
+
return { ir: importedResult };
|
|
111
|
+
}
|
|
112
|
+
let recalculationCounter = 0;
|
|
113
|
+
if (require_index.getDebugFlags().logOutputStatus) console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
|
|
114
|
+
keepVmAlive = true;
|
|
115
|
+
return {
|
|
116
|
+
ir: toBeResolved,
|
|
117
|
+
postprocessValue: (resolved, { unstableMarker, stable }) => {
|
|
118
|
+
recalculationCounter++;
|
|
119
|
+
return (0, _milaboratories_pl_flight_recorder.recordModelRenderSync)(flightRecorder, {
|
|
120
|
+
...renderInfo,
|
|
121
|
+
recalculation: recalculationCounter
|
|
122
|
+
}, () => {
|
|
123
|
+
for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
|
|
124
|
+
const renderedResult = rCtx.importObjectUniversal(result);
|
|
125
|
+
if (require_index.getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);
|
|
126
|
+
logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
|
|
127
|
+
return renderedResult;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
});
|
|
114
132
|
} catch (e) {
|
|
115
133
|
keepVmAlive = false;
|
|
116
134
|
throw e;
|
|
@@ -124,7 +142,7 @@ function executeSingleLambda(quickJs, fh, codeWithInfo, ...args) {
|
|
|
124
142
|
const scope = new quickjs_emscripten.Scope();
|
|
125
143
|
try {
|
|
126
144
|
const runtime = scope.manage(quickJs.newRuntime());
|
|
127
|
-
runtime.setMemoryLimit(
|
|
145
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
128
146
|
runtime.setMaxStackSize(1024 * 320);
|
|
129
147
|
let deadlineSettings;
|
|
130
148
|
runtime.setInterruptHandler(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["getDebugFlags","Computable","Scope","JsExecutionContext"],"sources":["../../src/js_render/index.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport type { BlockCodeWithInfo, ConfigRenderLambda } from \"@platforma-sdk/model\";\nimport type { ComputableRenderingOps } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { Scope } from \"quickjs-emscripten\";\nimport type { DeadlineSettings } from \"./context\";\nimport { JsExecutionContext } from \"./context\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport { getDebugFlags } from \"../debug\";\n\nfunction logOutputStatus(\n handle: string,\n renderedResult: unknown,\n stable: boolean,\n recalculationCounter: number,\n unstableMarker?: string,\n) {\n if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === \"any\" || !stable)) {\n if (stable)\n console.log(\n `Stable output ${handle} calculated ${renderedResult !== undefined ? \"defined\" : \"undefined\"}; (#${recalculationCounter})`,\n );\n else\n console.log(\n `Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? \"defined\" : \"undefined\"} (#${recalculationCounter})`,\n );\n }\n}\n\n/**\n * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.\n * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.\n *\n * The overall data flow is as follows:\n * 1. A QuickJS VM is initialized.\n * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a\n * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the\n * platform (e.g., to request data).\n * 3. The block's Javascript bundle is evaluated in the VM.\n * 4. The specified render function (`fh.handle`) is executed.\n *\n * Two execution paths are possible depending on the behavior of the render function:\n *\n * ### Synchronous Path\n * If the render function computes its result without requesting any external data requiring asynchronous calculations,\n * it executes synchronously.\n * - The `computablesToResolve` map in `JsExecutionContext` remains empty.\n * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).\n * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.\n * - The QuickJS VM is disposed of immediately as it's no longer needed.\n *\n * ### Asynchronous Path (with `postprocessValue`)\n * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls\n * one of the injected helper methods. These methods don't return data directly. Instead, they:\n * a. Create a new `Computable` for the data request (e.g., to fetch a blob).\n * b. Register this new `Computable` in the `computablesToResolve` map.\n * c. Return a handle (string) to the sandboxed code.\n *\n * In this case:\n * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending\n * computables.\n * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.\n * - The `postprocessValue` function is provided to handle the results once the computables are resolved.\n * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.\n * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.\n * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final\n * result.\n * - The VM is eventually disposed of when the host Computable is destroyed.\n *\n * @param env The middle layer environment.\n * @param ctx The block context.\n * @param fh The config render lambda to execute.\n * @param codeWithInfo The block's code and feature flags.\n * @param configKey A key for the configuration, used for cache busting.\n * @param ops Options for the computable.\n * @returns A `Computable` that will resolve to the result of the lambda execution.\n */\nexport function computableFromRF(\n env: MiddleLayerEnvironment,\n ctx: BlockContextAny,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n configKey: string,\n ops: Partial<ComputableRenderingOps> = {},\n): Computable<unknown> {\n // adding configKey to reload all outputs on block-pack update\n const key = `${ctx.blockId}#lambda#${configKey}#${fh.handle}`;\n ops = { ...ops, key };\n if (ops.mode === undefined && fh.retentive === true) ops.mode = \"StableOnlyRetentive\";\n return Computable.makeRaw((cCtx) => {\n const { code, featureFlags } = codeWithInfo;\n\n if (getDebugFlags().logOutputRecalculations)\n console.log(\n `Block lambda recalculation : ${key} (${cCtx.changeSourceMarker}; ${cCtx.bodyInvocations} invocations)`,\n );\n\n const scope = new Scope();\n let keepVmAlive = false;\n cCtx.addOnDestroy(() => {\n // If keepVmAlive is false, the scope will be disposed by the finally block,\n // no need to dispose it here.\n if (keepVmAlive) scope.dispose();\n });\n\n try {\n const runtime = scope.manage(env.quickJs.newRuntime());\n runtime.setMemoryLimit(1024 * 1024 * 8);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n { computableCtx: cCtx, blockCtx: ctx, mlEnv: env },\n );\n\n rCtx.evaluateBundle(code.content);\n const result = rCtx.runCallback(fh.handle);\n\n rCtx.resetComputableCtx();\n\n const toBeResolved = rCtx.computableHelper!.computablesToResolve;\n\n if (Object.keys(toBeResolved).length === 0) {\n const importedResult = rCtx.importObjectUniversal(result);\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(\n fh.handle,\n importedResult,\n cCtx.unstableMarker === undefined,\n -1,\n cCtx.unstableMarker,\n );\n return { ir: importedResult };\n }\n\n let recalculationCounter = 0;\n if (getDebugFlags().logOutputStatus)\n console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);\n keepVmAlive = true;\n\n return {\n ir: toBeResolved,\n postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {\n // resolving futures\n for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);\n\n // rendering result\n const renderedResult = rCtx.importObjectUniversal(result);\n\n // logging\n recalculationCounter++;\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);\n\n return renderedResult;\n },\n };\n } catch (e) {\n keepVmAlive = false;\n throw e;\n } finally {\n if (!keepVmAlive) scope.dispose();\n }\n }, ops);\n}\n\nexport function executeSingleLambda(\n quickJs: QuickJSWASMModule,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n ...args: unknown[]\n): unknown {\n const { code, featureFlags } = codeWithInfo;\n const scope = new Scope();\n try {\n const runtime = scope.manage(quickJs.newRuntime());\n runtime.setMemoryLimit(1024 * 1024 * 8);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n );\n\n // Initializing the model\n rCtx.evaluateBundle(code.content);\n\n // Running the lambda with arguments (e.g., state for args(), args for enrichmentTargets())\n const importedResult = rCtx.importObjectUniversal(rCtx.runCallback(fh.handle, ...args));\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${fh.handle}: ${JSON.stringify(rCtx.stats)}`);\n return importedResult;\n } finally {\n scope.dispose();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["getDebugFlags","Computable","Scope","JsExecutionContext"],"sources":["../../src/js_render/index.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport type { BlockCodeWithInfo, ConfigRenderLambda } from \"@platforma-sdk/model\";\nimport type { ComputableRenderingOps } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { Scope } from \"quickjs-emscripten\";\nimport type { DeadlineSettings } from \"./context\";\nimport { JsExecutionContext } from \"./context\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport { getDebugFlags } from \"../debug\";\nimport { recordModelRenderSync } from \"@milaboratories/pl-flight-recorder\";\n\n/** Memory ceiling applied to every QuickJS runtime that evaluates model code. */\nconst QUICK_JS_MEMORY_LIMIT = 1024 * 1024 * 8;\n\nfunction logOutputStatus(\n handle: string,\n renderedResult: unknown,\n stable: boolean,\n recalculationCounter: number,\n unstableMarker?: string,\n) {\n if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === \"any\" || !stable)) {\n if (stable)\n console.log(\n `Stable output ${handle} calculated ${renderedResult !== undefined ? \"defined\" : \"undefined\"}; (#${recalculationCounter})`,\n );\n else\n console.log(\n `Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? \"defined\" : \"undefined\"} (#${recalculationCounter})`,\n );\n }\n}\n\n/**\n * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.\n * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.\n *\n * The overall data flow is as follows:\n * 1. A QuickJS VM is initialized.\n * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a\n * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the\n * platform (e.g., to request data).\n * 3. The block's Javascript bundle is evaluated in the VM.\n * 4. The specified render function (`fh.handle`) is executed.\n *\n * Two execution paths are possible depending on the behavior of the render function:\n *\n * ### Synchronous Path\n * If the render function computes its result without requesting any external data requiring asynchronous calculations,\n * it executes synchronously.\n * - The `computablesToResolve` map in `JsExecutionContext` remains empty.\n * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).\n * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.\n * - The QuickJS VM is disposed of immediately as it's no longer needed.\n *\n * ### Asynchronous Path (with `postprocessValue`)\n * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls\n * one of the injected helper methods. These methods don't return data directly. Instead, they:\n * a. Create a new `Computable` for the data request (e.g., to fetch a blob).\n * b. Register this new `Computable` in the `computablesToResolve` map.\n * c. Return a handle (string) to the sandboxed code.\n *\n * In this case:\n * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending\n * computables.\n * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.\n * - The `postprocessValue` function is provided to handle the results once the computables are resolved.\n * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.\n * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.\n * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final\n * result.\n * - The VM is eventually disposed of when the host Computable is destroyed.\n *\n * @param env The middle layer environment.\n * @param ctx The block context.\n * @param fh The config render lambda to execute.\n * @param codeWithInfo The block's code and feature flags.\n * @param configKey A key for the configuration, used for cache busting.\n * @param ops Options for the computable.\n * @returns A `Computable` that will resolve to the result of the lambda execution.\n */\nexport function computableFromRF(\n env: MiddleLayerEnvironment,\n ctx: BlockContextAny,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n configKey: string,\n ops: Partial<ComputableRenderingOps> = {},\n): Computable<unknown> {\n // adding configKey to reload all outputs on block-pack update\n const key = `${ctx.blockId}#lambda#${configKey}#${fh.handle}`;\n ops = { ...ops, key };\n if (ops.mode === undefined && fh.retentive === true) ops.mode = \"StableOnlyRetentive\";\n return Computable.makeRaw((cCtx) => {\n const { code, featureFlags } = codeWithInfo;\n\n if (getDebugFlags().logOutputRecalculations)\n console.log(\n `Block lambda recalculation : ${key} (${cCtx.changeSourceMarker}; ${cCtx.bodyInvocations} invocations)`,\n );\n\n const scope = new Scope();\n let keepVmAlive = false;\n cCtx.addOnDestroy(() => {\n // If keepVmAlive is false, the scope will be disposed by the finally block,\n // no need to dispose it here.\n if (keepVmAlive) scope.dispose();\n });\n\n try {\n const runtime = scope.manage(env.quickJs.newRuntime());\n runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n { computableCtx: cCtx, blockCtx: ctx, mlEnv: env },\n );\n\n const flightRecorder = env.driverKit.flightRecorder;\n const renderInfo = {\n blockId: ctx.blockId,\n key,\n lambda: fh.handle,\n // The sandbox has its own 8 MB ceiling, so the model's own objects can\n // never exhaust the host heap. What can is the volume crossing out of\n // it, which `stats` counts.\n sandboxMemoryLimit: QUICK_JS_MEMORY_LIMIT,\n getStats: () => ({ ...rCtx.stats }),\n };\n\n return recordModelRenderSync(flightRecorder, renderInfo, () => {\n rCtx.evaluateBundle(code.content);\n const result = rCtx.runCallback(fh.handle);\n\n rCtx.resetComputableCtx();\n\n const toBeResolved = rCtx.computableHelper!.computablesToResolve;\n\n if (Object.keys(toBeResolved).length === 0) {\n const importedResult = rCtx.importObjectUniversal(result);\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(\n fh.handle,\n importedResult,\n cCtx.unstableMarker === undefined,\n -1,\n cCtx.unstableMarker,\n );\n return { ir: importedResult };\n }\n\n let recalculationCounter = 0;\n if (getDebugFlags().logOutputStatus)\n console.log(\n `Output ${fh.handle} scaffold calculated (not all computables resolved yet).`,\n );\n keepVmAlive = true;\n\n return {\n ir: toBeResolved,\n postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {\n recalculationCounter++;\n // A deferred render resumes here, potentially many times, and each\n // resumption can build joins of its own, so each gets its own span.\n return recordModelRenderSync(\n flightRecorder,\n { ...renderInfo, recalculation: recalculationCounter },\n () => {\n // resolving futures\n for (const [handle, value] of Object.entries(resolved))\n rCtx.runCallback(handle, value);\n\n // rendering result\n const renderedResult = rCtx.importObjectUniversal(result);\n\n // logging\n if (getDebugFlags().logJsExecStat)\n console.log(\n `[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`,\n );\n logOutputStatus(\n fh.handle,\n renderedResult,\n stable,\n recalculationCounter,\n unstableMarker,\n );\n\n return renderedResult;\n },\n );\n },\n };\n });\n } catch (e) {\n keepVmAlive = false;\n throw e;\n } finally {\n if (!keepVmAlive) scope.dispose();\n }\n }, ops);\n}\n\nexport function executeSingleLambda(\n quickJs: QuickJSWASMModule,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n ...args: unknown[]\n): unknown {\n const { code, featureFlags } = codeWithInfo;\n const scope = new Scope();\n try {\n const runtime = scope.manage(quickJs.newRuntime());\n runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n );\n\n // Initializing the model\n rCtx.evaluateBundle(code.content);\n\n // Running the lambda with arguments (e.g., state for args(), args for enrichmentTargets())\n const importedResult = rCtx.importObjectUniversal(rCtx.runCallback(fh.handle, ...args));\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${fh.handle}: ${JSON.stringify(rCtx.stats)}`);\n return importedResult;\n } finally {\n scope.dispose();\n }\n}\n"],"mappings":"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"}
|
package/dist/js_render/index.js
CHANGED
|
@@ -2,7 +2,10 @@ import { getDebugFlags } from "../debug/index.js";
|
|
|
2
2
|
import { JsExecutionContext } from "./context.js";
|
|
3
3
|
import { Computable } from "@milaboratories/computable";
|
|
4
4
|
import { Scope } from "quickjs-emscripten";
|
|
5
|
+
import { recordModelRenderSync } from "@milaboratories/pl-flight-recorder";
|
|
5
6
|
//#region src/js_render/index.ts
|
|
7
|
+
/** Memory ceiling applied to every QuickJS runtime that evaluates model code. */
|
|
8
|
+
const QUICK_JS_MEMORY_LIMIT = 1024 * 1024 * 8;
|
|
6
9
|
function logOutputStatus(handle, renderedResult, stable, recalculationCounter, unstableMarker) {
|
|
7
10
|
if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === "any" || !stable)) if (stable) console.log(`Stable output ${handle} calculated ${renderedResult !== void 0 ? "defined" : "undefined"}; (#${recalculationCounter})`);
|
|
8
11
|
else console.log(`Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== void 0 ? "defined" : "undefined"} (#${recalculationCounter})`);
|
|
@@ -72,7 +75,7 @@ function computableFromRF(env, ctx, fh, codeWithInfo, configKey, ops = {}) {
|
|
|
72
75
|
});
|
|
73
76
|
try {
|
|
74
77
|
const runtime = scope.manage(env.quickJs.newRuntime());
|
|
75
|
-
runtime.setMemoryLimit(
|
|
78
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
76
79
|
runtime.setMaxStackSize(1024 * 320);
|
|
77
80
|
let deadlineSettings;
|
|
78
81
|
runtime.setInterruptHandler(() => {
|
|
@@ -87,30 +90,45 @@ function computableFromRF(env, ctx, fh, codeWithInfo, configKey, ops = {}) {
|
|
|
87
90
|
blockCtx: ctx,
|
|
88
91
|
mlEnv: env
|
|
89
92
|
});
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logOutputStatus(fh.handle, importedResult, cCtx.unstableMarker === void 0, -1, cCtx.unstableMarker);
|
|
98
|
-
return { ir: importedResult };
|
|
99
|
-
}
|
|
100
|
-
let recalculationCounter = 0;
|
|
101
|
-
if (getDebugFlags().logOutputStatus) console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
|
|
102
|
-
keepVmAlive = true;
|
|
103
|
-
return {
|
|
104
|
-
ir: toBeResolved,
|
|
105
|
-
postprocessValue: (resolved, { unstableMarker, stable }) => {
|
|
106
|
-
for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
|
|
107
|
-
const renderedResult = rCtx.importObjectUniversal(result);
|
|
108
|
-
recalculationCounter++;
|
|
109
|
-
if (getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);
|
|
110
|
-
logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
|
|
111
|
-
return renderedResult;
|
|
112
|
-
}
|
|
93
|
+
const flightRecorder = env.driverKit.flightRecorder;
|
|
94
|
+
const renderInfo = {
|
|
95
|
+
blockId: ctx.blockId,
|
|
96
|
+
key,
|
|
97
|
+
lambda: fh.handle,
|
|
98
|
+
sandboxMemoryLimit: QUICK_JS_MEMORY_LIMIT,
|
|
99
|
+
getStats: () => ({ ...rCtx.stats })
|
|
113
100
|
};
|
|
101
|
+
return recordModelRenderSync(flightRecorder, renderInfo, () => {
|
|
102
|
+
rCtx.evaluateBundle(code.content);
|
|
103
|
+
const result = rCtx.runCallback(fh.handle);
|
|
104
|
+
rCtx.resetComputableCtx();
|
|
105
|
+
const toBeResolved = rCtx.computableHelper.computablesToResolve;
|
|
106
|
+
if (Object.keys(toBeResolved).length === 0) {
|
|
107
|
+
const importedResult = rCtx.importObjectUniversal(result);
|
|
108
|
+
if (getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);
|
|
109
|
+
logOutputStatus(fh.handle, importedResult, cCtx.unstableMarker === void 0, -1, cCtx.unstableMarker);
|
|
110
|
+
return { ir: importedResult };
|
|
111
|
+
}
|
|
112
|
+
let recalculationCounter = 0;
|
|
113
|
+
if (getDebugFlags().logOutputStatus) console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
|
|
114
|
+
keepVmAlive = true;
|
|
115
|
+
return {
|
|
116
|
+
ir: toBeResolved,
|
|
117
|
+
postprocessValue: (resolved, { unstableMarker, stable }) => {
|
|
118
|
+
recalculationCounter++;
|
|
119
|
+
return recordModelRenderSync(flightRecorder, {
|
|
120
|
+
...renderInfo,
|
|
121
|
+
recalculation: recalculationCounter
|
|
122
|
+
}, () => {
|
|
123
|
+
for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
|
|
124
|
+
const renderedResult = rCtx.importObjectUniversal(result);
|
|
125
|
+
if (getDebugFlags().logJsExecStat) console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);
|
|
126
|
+
logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
|
|
127
|
+
return renderedResult;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
});
|
|
114
132
|
} catch (e) {
|
|
115
133
|
keepVmAlive = false;
|
|
116
134
|
throw e;
|
|
@@ -124,7 +142,7 @@ function executeSingleLambda(quickJs, fh, codeWithInfo, ...args) {
|
|
|
124
142
|
const scope = new Scope();
|
|
125
143
|
try {
|
|
126
144
|
const runtime = scope.manage(quickJs.newRuntime());
|
|
127
|
-
runtime.setMemoryLimit(
|
|
145
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
128
146
|
runtime.setMaxStackSize(1024 * 320);
|
|
129
147
|
let deadlineSettings;
|
|
130
148
|
runtime.setInterruptHandler(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/js_render/index.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport type { BlockCodeWithInfo, ConfigRenderLambda } from \"@platforma-sdk/model\";\nimport type { ComputableRenderingOps } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { Scope } from \"quickjs-emscripten\";\nimport type { DeadlineSettings } from \"./context\";\nimport { JsExecutionContext } from \"./context\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport { getDebugFlags } from \"../debug\";\n\nfunction logOutputStatus(\n handle: string,\n renderedResult: unknown,\n stable: boolean,\n recalculationCounter: number,\n unstableMarker?: string,\n) {\n if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === \"any\" || !stable)) {\n if (stable)\n console.log(\n `Stable output ${handle} calculated ${renderedResult !== undefined ? \"defined\" : \"undefined\"}; (#${recalculationCounter})`,\n );\n else\n console.log(\n `Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? \"defined\" : \"undefined\"} (#${recalculationCounter})`,\n );\n }\n}\n\n/**\n * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.\n * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.\n *\n * The overall data flow is as follows:\n * 1. A QuickJS VM is initialized.\n * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a\n * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the\n * platform (e.g., to request data).\n * 3. The block's Javascript bundle is evaluated in the VM.\n * 4. The specified render function (`fh.handle`) is executed.\n *\n * Two execution paths are possible depending on the behavior of the render function:\n *\n * ### Synchronous Path\n * If the render function computes its result without requesting any external data requiring asynchronous calculations,\n * it executes synchronously.\n * - The `computablesToResolve` map in `JsExecutionContext` remains empty.\n * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).\n * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.\n * - The QuickJS VM is disposed of immediately as it's no longer needed.\n *\n * ### Asynchronous Path (with `postprocessValue`)\n * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls\n * one of the injected helper methods. These methods don't return data directly. Instead, they:\n * a. Create a new `Computable` for the data request (e.g., to fetch a blob).\n * b. Register this new `Computable` in the `computablesToResolve` map.\n * c. Return a handle (string) to the sandboxed code.\n *\n * In this case:\n * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending\n * computables.\n * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.\n * - The `postprocessValue` function is provided to handle the results once the computables are resolved.\n * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.\n * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.\n * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final\n * result.\n * - The VM is eventually disposed of when the host Computable is destroyed.\n *\n * @param env The middle layer environment.\n * @param ctx The block context.\n * @param fh The config render lambda to execute.\n * @param codeWithInfo The block's code and feature flags.\n * @param configKey A key for the configuration, used for cache busting.\n * @param ops Options for the computable.\n * @returns A `Computable` that will resolve to the result of the lambda execution.\n */\nexport function computableFromRF(\n env: MiddleLayerEnvironment,\n ctx: BlockContextAny,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n configKey: string,\n ops: Partial<ComputableRenderingOps> = {},\n): Computable<unknown> {\n // adding configKey to reload all outputs on block-pack update\n const key = `${ctx.blockId}#lambda#${configKey}#${fh.handle}`;\n ops = { ...ops, key };\n if (ops.mode === undefined && fh.retentive === true) ops.mode = \"StableOnlyRetentive\";\n return Computable.makeRaw((cCtx) => {\n const { code, featureFlags } = codeWithInfo;\n\n if (getDebugFlags().logOutputRecalculations)\n console.log(\n `Block lambda recalculation : ${key} (${cCtx.changeSourceMarker}; ${cCtx.bodyInvocations} invocations)`,\n );\n\n const scope = new Scope();\n let keepVmAlive = false;\n cCtx.addOnDestroy(() => {\n // If keepVmAlive is false, the scope will be disposed by the finally block,\n // no need to dispose it here.\n if (keepVmAlive) scope.dispose();\n });\n\n try {\n const runtime = scope.manage(env.quickJs.newRuntime());\n runtime.setMemoryLimit(1024 * 1024 * 8);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n { computableCtx: cCtx, blockCtx: ctx, mlEnv: env },\n );\n\n rCtx.evaluateBundle(code.content);\n const result = rCtx.runCallback(fh.handle);\n\n rCtx.resetComputableCtx();\n\n const toBeResolved = rCtx.computableHelper!.computablesToResolve;\n\n if (Object.keys(toBeResolved).length === 0) {\n const importedResult = rCtx.importObjectUniversal(result);\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(\n fh.handle,\n importedResult,\n cCtx.unstableMarker === undefined,\n -1,\n cCtx.unstableMarker,\n );\n return { ir: importedResult };\n }\n\n let recalculationCounter = 0;\n if (getDebugFlags().logOutputStatus)\n console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);\n keepVmAlive = true;\n\n return {\n ir: toBeResolved,\n postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {\n // resolving futures\n for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);\n\n // rendering result\n const renderedResult = rCtx.importObjectUniversal(result);\n\n // logging\n recalculationCounter++;\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);\n\n return renderedResult;\n },\n };\n } catch (e) {\n keepVmAlive = false;\n throw e;\n } finally {\n if (!keepVmAlive) scope.dispose();\n }\n }, ops);\n}\n\nexport function executeSingleLambda(\n quickJs: QuickJSWASMModule,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n ...args: unknown[]\n): unknown {\n const { code, featureFlags } = codeWithInfo;\n const scope = new Scope();\n try {\n const runtime = scope.manage(quickJs.newRuntime());\n runtime.setMemoryLimit(1024 * 1024 * 8);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n );\n\n // Initializing the model\n rCtx.evaluateBundle(code.content);\n\n // Running the lambda with arguments (e.g., state for args(), args for enrichmentTargets())\n const importedResult = rCtx.importObjectUniversal(rCtx.runCallback(fh.handle, ...args));\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${fh.handle}: ${JSON.stringify(rCtx.stats)}`);\n return importedResult;\n } finally {\n scope.dispose();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/js_render/index.ts"],"sourcesContent":["import type { MiddleLayerEnvironment } from \"../middle_layer/middle_layer\";\nimport type { BlockCodeWithInfo, ConfigRenderLambda } from \"@platforma-sdk/model\";\nimport type { ComputableRenderingOps } from \"@milaboratories/computable\";\nimport { Computable } from \"@milaboratories/computable\";\nimport type { QuickJSWASMModule } from \"quickjs-emscripten\";\nimport { Scope } from \"quickjs-emscripten\";\nimport type { DeadlineSettings } from \"./context\";\nimport { JsExecutionContext } from \"./context\";\nimport type { BlockContextAny } from \"../middle_layer/block_ctx\";\nimport { getDebugFlags } from \"../debug\";\nimport { recordModelRenderSync } from \"@milaboratories/pl-flight-recorder\";\n\n/** Memory ceiling applied to every QuickJS runtime that evaluates model code. */\nconst QUICK_JS_MEMORY_LIMIT = 1024 * 1024 * 8;\n\nfunction logOutputStatus(\n handle: string,\n renderedResult: unknown,\n stable: boolean,\n recalculationCounter: number,\n unstableMarker?: string,\n) {\n if (getDebugFlags().logOutputStatus && (getDebugFlags().logOutputStatus === \"any\" || !stable)) {\n if (stable)\n console.log(\n `Stable output ${handle} calculated ${renderedResult !== undefined ? \"defined\" : \"undefined\"}; (#${recalculationCounter})`,\n );\n else\n console.log(\n `Unstable output ${handle}; marker = ${unstableMarker}; ${renderedResult !== undefined ? \"defined\" : \"undefined\"} (#${recalculationCounter})`,\n );\n }\n}\n\n/**\n * Creates a Computable that executes a render function (`fh`) from a block's code in a QuickJS virtual machine.\n * This function handles both synchronous and asynchronous execution patterns of the sandboxed JS code.\n *\n * The overall data flow is as follows:\n * 1. A QuickJS VM is initialized.\n * 2. A `JsExecutionContext` is created to bridge the host (TypeScript) and guest (QuickJS) environments. It injects a\n * context object (`cfgRenderCtx`) into the VM, providing helper methods for the sandboxed code to interact with the\n * platform (e.g., to request data).\n * 3. The block's Javascript bundle is evaluated in the VM.\n * 4. The specified render function (`fh.handle`) is executed.\n *\n * Two execution paths are possible depending on the behavior of the render function:\n *\n * ### Synchronous Path\n * If the render function computes its result without requesting any external data requiring asynchronous calculations,\n * it executes synchronously.\n * - The `computablesToResolve` map in `JsExecutionContext` remains empty.\n * - The function returns an object with an `ir` field holding the result (`{ ir: importedResult }`).\n * Since `postprocessValue` is not specified, `ir` is treated as the final resolved value of the Computable.\n * - The QuickJS VM is disposed of immediately as it's no longer needed.\n *\n * ### Asynchronous Path (with `postprocessValue`)\n * If the render function needs external data requiring asynchronous calculations (e.g., fetching a file), it calls\n * one of the injected helper methods. These methods don't return data directly. Instead, they:\n * a. Create a new `Computable` for the data request (e.g., to fetch a blob).\n * b. Register this new `Computable` in the `computablesToResolve` map.\n * c. Return a handle (string) to the sandboxed code.\n *\n * In this case:\n * - The initial execution of the render function returns a scaffold of the final result, which depends on the pending\n * computables.\n * - The `computablesToResolve` map is passed as the `ir` (initial result) to `Computable.makeRaw`.\n * - The `postprocessValue` function is provided to handle the results once the computables are resolved.\n * - The QuickJS VM is kept alive (`keepVmAlive = true`) because its state is needed in `postprocessValue`.\n * - Once the `computable` framework resolves all dependencies, it calls `postprocessValue` with the resolved data.\n * - `postprocessValue` feeds the resolved data back into the VM, allowing the sandboxed code to compute the final\n * result.\n * - The VM is eventually disposed of when the host Computable is destroyed.\n *\n * @param env The middle layer environment.\n * @param ctx The block context.\n * @param fh The config render lambda to execute.\n * @param codeWithInfo The block's code and feature flags.\n * @param configKey A key for the configuration, used for cache busting.\n * @param ops Options for the computable.\n * @returns A `Computable` that will resolve to the result of the lambda execution.\n */\nexport function computableFromRF(\n env: MiddleLayerEnvironment,\n ctx: BlockContextAny,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n configKey: string,\n ops: Partial<ComputableRenderingOps> = {},\n): Computable<unknown> {\n // adding configKey to reload all outputs on block-pack update\n const key = `${ctx.blockId}#lambda#${configKey}#${fh.handle}`;\n ops = { ...ops, key };\n if (ops.mode === undefined && fh.retentive === true) ops.mode = \"StableOnlyRetentive\";\n return Computable.makeRaw((cCtx) => {\n const { code, featureFlags } = codeWithInfo;\n\n if (getDebugFlags().logOutputRecalculations)\n console.log(\n `Block lambda recalculation : ${key} (${cCtx.changeSourceMarker}; ${cCtx.bodyInvocations} invocations)`,\n );\n\n const scope = new Scope();\n let keepVmAlive = false;\n cCtx.addOnDestroy(() => {\n // If keepVmAlive is false, the scope will be disposed by the finally block,\n // no need to dispose it here.\n if (keepVmAlive) scope.dispose();\n });\n\n try {\n const runtime = scope.manage(env.quickJs.newRuntime());\n runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n { computableCtx: cCtx, blockCtx: ctx, mlEnv: env },\n );\n\n const flightRecorder = env.driverKit.flightRecorder;\n const renderInfo = {\n blockId: ctx.blockId,\n key,\n lambda: fh.handle,\n // The sandbox has its own 8 MB ceiling, so the model's own objects can\n // never exhaust the host heap. What can is the volume crossing out of\n // it, which `stats` counts.\n sandboxMemoryLimit: QUICK_JS_MEMORY_LIMIT,\n getStats: () => ({ ...rCtx.stats }),\n };\n\n return recordModelRenderSync(flightRecorder, renderInfo, () => {\n rCtx.evaluateBundle(code.content);\n const result = rCtx.runCallback(fh.handle);\n\n rCtx.resetComputableCtx();\n\n const toBeResolved = rCtx.computableHelper!.computablesToResolve;\n\n if (Object.keys(toBeResolved).length === 0) {\n const importedResult = rCtx.importObjectUniversal(result);\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);\n logOutputStatus(\n fh.handle,\n importedResult,\n cCtx.unstableMarker === undefined,\n -1,\n cCtx.unstableMarker,\n );\n return { ir: importedResult };\n }\n\n let recalculationCounter = 0;\n if (getDebugFlags().logOutputStatus)\n console.log(\n `Output ${fh.handle} scaffold calculated (not all computables resolved yet).`,\n );\n keepVmAlive = true;\n\n return {\n ir: toBeResolved,\n postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {\n recalculationCounter++;\n // A deferred render resumes here, potentially many times, and each\n // resumption can build joins of its own, so each gets its own span.\n return recordModelRenderSync(\n flightRecorder,\n { ...renderInfo, recalculation: recalculationCounter },\n () => {\n // resolving futures\n for (const [handle, value] of Object.entries(resolved))\n rCtx.runCallback(handle, value);\n\n // rendering result\n const renderedResult = rCtx.importObjectUniversal(result);\n\n // logging\n if (getDebugFlags().logJsExecStat)\n console.log(\n `[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`,\n );\n logOutputStatus(\n fh.handle,\n renderedResult,\n stable,\n recalculationCounter,\n unstableMarker,\n );\n\n return renderedResult;\n },\n );\n },\n };\n });\n } catch (e) {\n keepVmAlive = false;\n throw e;\n } finally {\n if (!keepVmAlive) scope.dispose();\n }\n }, ops);\n}\n\nexport function executeSingleLambda(\n quickJs: QuickJSWASMModule,\n fh: ConfigRenderLambda,\n codeWithInfo: BlockCodeWithInfo,\n ...args: unknown[]\n): unknown {\n const { code, featureFlags } = codeWithInfo;\n const scope = new Scope();\n try {\n const runtime = scope.manage(quickJs.newRuntime());\n runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);\n runtime.setMaxStackSize(1024 * 320);\n\n let deadlineSettings: DeadlineSettings | undefined;\n runtime.setInterruptHandler(() => {\n if (deadlineSettings === undefined) return false;\n if (Date.now() > deadlineSettings.deadline) return true;\n return false;\n });\n const vm = scope.manage(runtime.newContext());\n const rCtx = new JsExecutionContext(\n scope,\n vm,\n (s) => {\n deadlineSettings = s;\n },\n featureFlags,\n );\n\n // Initializing the model\n rCtx.evaluateBundle(code.content);\n\n // Running the lambda with arguments (e.g., state for args(), args for enrichmentTargets())\n const importedResult = rCtx.importObjectUniversal(rCtx.runCallback(fh.handle, ...args));\n if (getDebugFlags().logJsExecStat)\n console.log(`[jsExec] ${fh.handle}: ${JSON.stringify(rCtx.stats)}`);\n return importedResult;\n } finally {\n scope.dispose();\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ require("../pool/index.cjs");
|
|
|
3
3
|
const require_ops = require("./ops.cjs");
|
|
4
4
|
let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
|
|
5
5
|
let _milaboratories_pl_drivers = require("@milaboratories/pl-drivers");
|
|
6
|
+
let _milaboratories_pl_flight_recorder = require("@milaboratories/pl-flight-recorder");
|
|
6
7
|
let _milaboratories_helpers = require("@milaboratories/helpers");
|
|
7
8
|
//#region src/middle_layer/driver_kit.ts
|
|
8
9
|
async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
|
|
@@ -20,26 +21,32 @@ async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
|
|
|
20
21
|
const blobToURLDriver = new _milaboratories_pl_drivers.DownloadBlobToURLDriver(ops.logger, signer, downloadClient, ops.downloadBlobToURLPath, ops.downloadBlobToURLDriverOps);
|
|
21
22
|
const uploadDriver = new _milaboratories_pl_drivers.UploadDriver(ops.logger, signer, uploadBlobClient, uploadProgressClient, ops.uploadDriverOps);
|
|
22
23
|
const logsStreamDriver = new _milaboratories_pl_drivers.LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);
|
|
24
|
+
const logDriver = new _milaboratories_pl_drivers.LogsDriver(ops.logger, logsStreamDriver, blobDriver);
|
|
25
|
+
const lsDriver = await _milaboratories_pl_drivers.LsDriver.init(ops.logger, pl, signer, ops.localProjections, ops.openFileDialogCallback, ops.virtualLocalStoragesOverride);
|
|
26
|
+
const rawPFrameDriver = await require_driver.createPFrameDriver({
|
|
27
|
+
blobDriver,
|
|
28
|
+
logger: ops.logger,
|
|
29
|
+
spillPath: ops.pframesSpillPath,
|
|
30
|
+
cachePath: ops.parquetCachePath,
|
|
31
|
+
options: ops.pFrameDriverOps,
|
|
32
|
+
cacheOps: ops.parquetCacheOps
|
|
33
|
+
});
|
|
34
|
+
const flightSession = (0, _milaboratories_pl_flight_recorder.openFlightSession)({ role: "middle-layer" });
|
|
23
35
|
const driverKit = {
|
|
24
36
|
blobDriver,
|
|
25
37
|
blobToURLDriver,
|
|
26
|
-
logDriver
|
|
27
|
-
lsDriver
|
|
38
|
+
logDriver,
|
|
39
|
+
lsDriver,
|
|
28
40
|
signer,
|
|
29
41
|
uploadDriver,
|
|
30
|
-
pFrameDriver:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
spillPath: ops.pframesSpillPath,
|
|
34
|
-
cachePath: ops.parquetCachePath,
|
|
35
|
-
options: ops.pFrameDriverOps,
|
|
36
|
-
cacheOps: ops.parquetCacheOps
|
|
37
|
-
}),
|
|
38
|
-
frontendDriver: new _milaboratories_pl_drivers.DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps)
|
|
42
|
+
pFrameDriver: flightSession ? (0, _milaboratories_pl_flight_recorder.wrapDataDriver)((0, _milaboratories_pl_flight_recorder.wrapModelDriver)(rawPFrameDriver, flightSession.recorder, flightSession.registry), flightSession.recorder, flightSession.registry) : rawPFrameDriver,
|
|
43
|
+
frontendDriver: new _milaboratories_pl_drivers.DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps),
|
|
44
|
+
flightRecorder: flightSession?.recorder
|
|
39
45
|
};
|
|
40
46
|
const dispose = async () => {
|
|
41
47
|
const disposePromises = Object.values(driverKit).flatMap((driver) => (0, _milaboratories_helpers.isAsyncDisposable)(driver) ? [driver[Symbol.asyncDispose]()] : []);
|
|
42
48
|
await Promise.all(disposePromises);
|
|
49
|
+
flightSession?.close("driver-kit-disposed");
|
|
43
50
|
};
|
|
44
51
|
return {
|
|
45
52
|
...driverKit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver_kit.cjs","names":["DefaultDriverKitOpsSettings","DefaultDriverKitOpsPaths","HmacSha256Signer","DownloadDriver","DownloadBlobToURLDriver","UploadDriver","LogsStreamDriver","LogsDriver","LsDriver","createPFrameDriver","DownloadUrlDriver"],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const
|
|
1
|
+
{"version":3,"file":"driver_kit.cjs","names":["DefaultDriverKitOpsSettings","DefaultDriverKitOpsPaths","HmacSha256Signer","DownloadDriver","DownloadBlobToURLDriver","UploadDriver","LogsStreamDriver","LogsDriver","LsDriver","createPFrameDriver","DownloadUrlDriver"],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { Recorder } from \"@milaboratories/pl-flight-recorder\";\nimport {\n openFlightSession,\n wrapDataDriver,\n wrapModelDriver,\n} from \"@milaboratories/pl-flight-recorder\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Diagnostics log for the model layer, present only when flight recording is\n * switched on. Render spans are written through it so that a join recorded by\n * the instrumented pFrame driver can be attributed to the block that built it.\n * */\n readonly flightRecorder?: Recorder;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const rawPFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n cachePath: ops.parquetCachePath,\n options: ops.pFrameDriverOps,\n cacheOps: ops.parquetCacheOps,\n });\n\n // Every join a block model builds and every row it reads back passes through\n // this one driver, so instrumenting it here covers the whole model layer\n // without touching the call sites. The session is undefined unless recording\n // is switched on, in which case the driver is used exactly as before.\n const flightSession = openFlightSession({ role: \"middle-layer\" });\n const pFrameDriver: InternalPFrameDriver = flightSession\n ? wrapDataDriver(\n wrapModelDriver(rawPFrameDriver, flightSession.recorder, flightSession.registry),\n flightSession.recorder,\n flightSession.registry,\n )\n : rawPFrameDriver;\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n flightRecorder: flightSession?.recorder,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n flightSession?.close(\"driver-kit-disposed\");\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;;AA0EA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAGA,YAAAA;EACH,GAAGC,YAAAA,yBAAyB,OAAO;EACnC,GAAG;CACL;CAEA,MAAM,SAAS,IAAIC,2BAAAA,iBAAiB,IAAI,WAAW;CAEnD,MAAM,kBAAA,GAAA,2BAAA,qBAAA,CAAsC,IAAI,QAAQ,IAAI,IAAI,gBAAgB;CAChF,MAAM,cAAA,GAAA,2BAAA,iBAAA,CAA8B,IAAI,IAAI,MAAM;CAClD,MAAM,oBAAA,GAAA,2BAAA,uBAAA,CAA0C,IAAI,IAAI,MAAM;CAC9D,MAAM,wBAAA,GAAA,2BAAA,2BAAA,CAAkD,IAAI,IAAI,MAAM;CAEtE,MAAM,aAAa,MAAMC,2BAAAA,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,aACN;CAEA,MAAM,kBAAkB,IAAIC,2BAAAA,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,0BACN;CAEA,MAAM,eAAe,IAAIC,2BAAAA,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,eACN;CACA,MAAM,mBAAmB,IAAIC,2BAAAA,iBAAiB,IAAI,QAAQ,YAAY,IAAI,kBAAkB;CAC5F,MAAM,YAAY,IAAIC,2BAAAA,WAAW,IAAI,QAAQ,kBAAkB,UAAU;CACzE,MAAM,WAAW,MAAMC,2BAAAA,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,4BACN;CAEA,MAAM,kBAAkB,MAAMC,eAAAA,mBAAmB;EAC/C;EACA,QAAQ,IAAI;EACZ,WAAW,IAAI;EACf,WAAW,IAAI;EACf,SAAS,IAAI;EACb,UAAU,IAAI;CAChB,CAAC;CAMD,MAAM,iBAAA,GAAA,mCAAA,kBAAA,CAAkC,EAAE,MAAM,eAAe,CAAC;CAiBhE,MAAM,YAAY;EAChB;EACiB;EACjB;EACA;EACA;EACA;EACA,cAvByC,iBAAA,GAAA,mCAAA,eAAA,EAAA,GAAA,mCAAA,gBAAA,CAErB,iBAAiB,cAAc,UAAU,cAAc,QAAQ,GAC/E,cAAc,UACd,cAAc,QAChB,IACA;EAkBF,gBAAgB,IAhBiBC,2BAAAA,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,yBAWiC;EACrC,gBAAgB,eAAe;CACjC;CAEA,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,SAAS,CAAC,CAAC,SAAS,YAAA,GAAA,wBAAA,kBAAA,CACtC,MAAM,IAAI,CAAC,OAAO,OAAO,aAAa,CAAC,CAAC,IAAI,CAAC,CACjE;EACA,MAAM,QAAQ,IAAI,eAAe;EACjC,eAAe,MAAM,qBAAqB;CAC5C;CAEA,OAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;CACzB;AACF"}
|
|
@@ -5,6 +5,7 @@ import { Signer } from "@milaboratories/ts-helpers";
|
|
|
5
5
|
import * as Sdk from "@milaboratories/pl-model-common";
|
|
6
6
|
import { PlClient } from "@milaboratories/pl-client";
|
|
7
7
|
import { DownloadBlobToURLDriver, DownloadDriver, DownloadUrlDriver, InternalLsDriver, LogsDriver, UploadDriver } from "@milaboratories/pl-drivers";
|
|
8
|
+
import { Recorder } from "@milaboratories/pl-flight-recorder";
|
|
8
9
|
//#region src/middle_layer/driver_kit.d.ts
|
|
9
10
|
/**
|
|
10
11
|
* Drivers offered by the middle-layer for internal consumers,
|
|
@@ -22,6 +23,12 @@ export interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {
|
|
|
22
23
|
readonly lsDriver: InternalLsDriver;
|
|
23
24
|
readonly pFrameDriver: InternalPFrameDriver;
|
|
24
25
|
readonly frontendDriver: DownloadUrlDriver;
|
|
26
|
+
/**
|
|
27
|
+
* Diagnostics log for the model layer, present only when flight recording is
|
|
28
|
+
* switched on. Render spans are written through it so that a join recorded by
|
|
29
|
+
* the instrumented pFrame driver can be attributed to the block that built it.
|
|
30
|
+
* */
|
|
31
|
+
readonly flightRecorder?: Recorder;
|
|
25
32
|
/**
|
|
26
33
|
* Signer is initialized from local secret in drivers initialization routine,
|
|
27
34
|
* so constitutes a part of the driver kit
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver_kit.d.ts","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"driver_kit.d.ts","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"mappings":";;;;;;;;;;;;;;;;iBAqCiB,6BAA6B,IAAI,WAAW;;EAE3D,WAAW;WAGF,YAAY;WAEZ,iBAAiB;WAEjB,WAAW;WAEX,UAAU;WAEV,cAAc;WAEd,gBAAgB;;;;;;WAOhB,iBAAiB;;;;;WAMjB,QAAQ;;;;;WAMR,cAAc;;wBAGH,cACpB,IAAI,UACJ,iBACA,8BACA,MAAM,0BACL,QAAQ"}
|
|
@@ -3,6 +3,7 @@ import "../pool/index.js";
|
|
|
3
3
|
import { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from "./ops.js";
|
|
4
4
|
import { HmacSha256Signer } from "@milaboratories/ts-helpers";
|
|
5
5
|
import { DownloadBlobToURLDriver, DownloadDriver, DownloadUrlDriver, LogsDriver, LogsStreamDriver, LsDriver, UploadDriver, createDownloadClient, createLogsClient, createUploadBlobClient, createUploadProgressClient } from "@milaboratories/pl-drivers";
|
|
6
|
+
import { openFlightSession, wrapDataDriver, wrapModelDriver } from "@milaboratories/pl-flight-recorder";
|
|
6
7
|
import { isAsyncDisposable } from "@milaboratories/helpers";
|
|
7
8
|
//#region src/middle_layer/driver_kit.ts
|
|
8
9
|
async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
|
|
@@ -20,26 +21,32 @@ async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
|
|
|
20
21
|
const blobToURLDriver = new DownloadBlobToURLDriver(ops.logger, signer, downloadClient, ops.downloadBlobToURLPath, ops.downloadBlobToURLDriverOps);
|
|
21
22
|
const uploadDriver = new UploadDriver(ops.logger, signer, uploadBlobClient, uploadProgressClient, ops.uploadDriverOps);
|
|
22
23
|
const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);
|
|
24
|
+
const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);
|
|
25
|
+
const lsDriver = await LsDriver.init(ops.logger, pl, signer, ops.localProjections, ops.openFileDialogCallback, ops.virtualLocalStoragesOverride);
|
|
26
|
+
const rawPFrameDriver = await createPFrameDriver({
|
|
27
|
+
blobDriver,
|
|
28
|
+
logger: ops.logger,
|
|
29
|
+
spillPath: ops.pframesSpillPath,
|
|
30
|
+
cachePath: ops.parquetCachePath,
|
|
31
|
+
options: ops.pFrameDriverOps,
|
|
32
|
+
cacheOps: ops.parquetCacheOps
|
|
33
|
+
});
|
|
34
|
+
const flightSession = openFlightSession({ role: "middle-layer" });
|
|
23
35
|
const driverKit = {
|
|
24
36
|
blobDriver,
|
|
25
37
|
blobToURLDriver,
|
|
26
|
-
logDriver
|
|
27
|
-
lsDriver
|
|
38
|
+
logDriver,
|
|
39
|
+
lsDriver,
|
|
28
40
|
signer,
|
|
29
41
|
uploadDriver,
|
|
30
|
-
pFrameDriver:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
spillPath: ops.pframesSpillPath,
|
|
34
|
-
cachePath: ops.parquetCachePath,
|
|
35
|
-
options: ops.pFrameDriverOps,
|
|
36
|
-
cacheOps: ops.parquetCacheOps
|
|
37
|
-
}),
|
|
38
|
-
frontendDriver: new DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps)
|
|
42
|
+
pFrameDriver: flightSession ? wrapDataDriver(wrapModelDriver(rawPFrameDriver, flightSession.recorder, flightSession.registry), flightSession.recorder, flightSession.registry) : rawPFrameDriver,
|
|
43
|
+
frontendDriver: new DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps),
|
|
44
|
+
flightRecorder: flightSession?.recorder
|
|
39
45
|
};
|
|
40
46
|
const dispose = async () => {
|
|
41
47
|
const disposePromises = Object.values(driverKit).flatMap((driver) => isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : []);
|
|
42
48
|
await Promise.all(disposePromises);
|
|
49
|
+
flightSession?.close("driver-kit-disposed");
|
|
43
50
|
};
|
|
44
51
|
return {
|
|
45
52
|
...driverKit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver_kit.js","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const
|
|
1
|
+
{"version":3,"file":"driver_kit.js","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { Recorder } from \"@milaboratories/pl-flight-recorder\";\nimport {\n openFlightSession,\n wrapDataDriver,\n wrapModelDriver,\n} from \"@milaboratories/pl-flight-recorder\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Diagnostics log for the model layer, present only when flight recording is\n * switched on. Render spans are written through it so that a join recorded by\n * the instrumented pFrame driver can be attributed to the block that built it.\n * */\n readonly flightRecorder?: Recorder;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const rawPFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n cachePath: ops.parquetCachePath,\n options: ops.pFrameDriverOps,\n cacheOps: ops.parquetCacheOps,\n });\n\n // Every join a block model builds and every row it reads back passes through\n // this one driver, so instrumenting it here covers the whole model layer\n // without touching the call sites. The session is undefined unless recording\n // is switched on, in which case the driver is used exactly as before.\n const flightSession = openFlightSession({ role: \"middle-layer\" });\n const pFrameDriver: InternalPFrameDriver = flightSession\n ? wrapDataDriver(\n wrapModelDriver(rawPFrameDriver, flightSession.recorder, flightSession.registry),\n flightSession.recorder,\n flightSession.registry,\n )\n : rawPFrameDriver;\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n flightRecorder: flightSession?.recorder,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n flightSession?.close(\"driver-kit-disposed\");\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;;AA0EA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAG;EACH,GAAG,yBAAyB,OAAO;EACnC,GAAG;CACL;CAEA,MAAM,SAAS,IAAI,iBAAiB,IAAI,WAAW;CAEnD,MAAM,iBAAiB,qBAAqB,IAAI,QAAQ,IAAI,IAAI,gBAAgB;CAChF,MAAM,aAAa,iBAAiB,IAAI,IAAI,MAAM;CAClD,MAAM,mBAAmB,uBAAuB,IAAI,IAAI,MAAM;CAC9D,MAAM,uBAAuB,2BAA2B,IAAI,IAAI,MAAM;CAEtE,MAAM,aAAa,MAAM,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,aACN;CAEA,MAAM,kBAAkB,IAAI,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,0BACN;CAEA,MAAM,eAAe,IAAI,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,eACN;CACA,MAAM,mBAAmB,IAAI,iBAAiB,IAAI,QAAQ,YAAY,IAAI,kBAAkB;CAC5F,MAAM,YAAY,IAAI,WAAW,IAAI,QAAQ,kBAAkB,UAAU;CACzE,MAAM,WAAW,MAAM,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,4BACN;CAEA,MAAM,kBAAkB,MAAM,mBAAmB;EAC/C;EACA,QAAQ,IAAI;EACZ,WAAW,IAAI;EACf,WAAW,IAAI;EACf,SAAS,IAAI;EACb,UAAU,IAAI;CAChB,CAAC;CAMD,MAAM,gBAAgB,kBAAkB,EAAE,MAAM,eAAe,CAAC;CAiBhE,MAAM,YAAY;EAChB;EACiB;EACjB;EACA;EACA;EACA;EACA,cAvByC,gBACvC,eACE,gBAAgB,iBAAiB,cAAc,UAAU,cAAc,QAAQ,GAC/E,cAAc,UACd,cAAc,QAChB,IACA;EAkBF,gBAAgB,IAhBiB,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,yBAWiC;EACrC,gBAAgB,eAAe;CACjC;CAEA,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,SAAS,CAAC,CAAC,SAAS,WACxD,kBAAkB,MAAM,IAAI,CAAC,OAAO,OAAO,aAAa,CAAC,CAAC,IAAI,CAAC,CACjE;EACA,MAAM,QAAQ,IAAI,eAAe;EACjC,eAAe,MAAM,qBAAqB;CAC5C;CAEA,OAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;CACzB;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-middle-layer",
|
|
3
|
-
"version": "1.71.
|
|
3
|
+
"version": "1.71.2",
|
|
4
4
|
"description": "Pl Middle Layer",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -33,21 +33,22 @@
|
|
|
33
33
|
"@milaboratories/columns-collection-driver": "0.2.4",
|
|
34
34
|
"@milaboratories/computable": "2.9.8",
|
|
35
35
|
"@milaboratories/pf-driver": "1.9.1",
|
|
36
|
-
"@milaboratories/helpers": "1.14.5",
|
|
37
36
|
"@milaboratories/pf-spec-driver": "1.5.1",
|
|
38
|
-
"@milaboratories/pl-drivers": "1.16.19",
|
|
39
37
|
"@milaboratories/pl-client": "3.16.0",
|
|
40
38
|
"@milaboratories/pl-deployments": "3.0.16",
|
|
41
|
-
"@milaboratories/
|
|
39
|
+
"@milaboratories/helpers": "1.14.5",
|
|
42
40
|
"@milaboratories/pl-errors": "1.4.38",
|
|
43
|
-
"@milaboratories/pl-
|
|
41
|
+
"@milaboratories/pl-drivers": "1.16.19",
|
|
42
|
+
"@milaboratories/pl-flight-recorder": "0.2.0",
|
|
44
43
|
"@milaboratories/pl-model-backend": "1.4.23",
|
|
44
|
+
"@milaboratories/pl-http": "1.2.4",
|
|
45
|
+
"@milaboratories/pl-model-common": "1.48.0",
|
|
45
46
|
"@milaboratories/pl-tree": "1.14.2",
|
|
46
|
-
"@milaboratories/pl-model-middle-layer": "1.32.0",
|
|
47
|
-
"@milaboratories/ts-helpers": "1.8.6",
|
|
48
|
-
"@platforma-sdk/block-tools": "2.15.0",
|
|
49
47
|
"@milaboratories/resolve-helper": "1.1.3",
|
|
48
|
+
"@milaboratories/ts-helpers": "1.8.6",
|
|
49
|
+
"@milaboratories/pl-model-middle-layer": "1.32.0",
|
|
50
50
|
"@platforma-sdk/model": "1.83.9",
|
|
51
|
+
"@platforma-sdk/block-tools": "2.15.0",
|
|
51
52
|
"@platforma-sdk/workflow-tengo": "6.9.0"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
@@ -57,8 +58,8 @@
|
|
|
57
58
|
"typescript": "7.0.2",
|
|
58
59
|
"vitest": "^4.1.3",
|
|
59
60
|
"@milaboratories/build-configs": "2.0.1",
|
|
60
|
-
"@milaboratories/ts-
|
|
61
|
-
"@milaboratories/ts-
|
|
61
|
+
"@milaboratories/ts-configs": "1.4.0",
|
|
62
|
+
"@milaboratories/ts-builder": "1.7.2"
|
|
62
63
|
},
|
|
63
64
|
"engines": {
|
|
64
65
|
"node": ">=22.19.0"
|
package/src/js_render/index.ts
CHANGED
|
@@ -8,6 +8,10 @@ import type { DeadlineSettings } from "./context";
|
|
|
8
8
|
import { JsExecutionContext } from "./context";
|
|
9
9
|
import type { BlockContextAny } from "../middle_layer/block_ctx";
|
|
10
10
|
import { getDebugFlags } from "../debug";
|
|
11
|
+
import { recordModelRenderSync } from "@milaboratories/pl-flight-recorder";
|
|
12
|
+
|
|
13
|
+
/** Memory ceiling applied to every QuickJS runtime that evaluates model code. */
|
|
14
|
+
const QUICK_JS_MEMORY_LIMIT = 1024 * 1024 * 8;
|
|
11
15
|
|
|
12
16
|
function logOutputStatus(
|
|
13
17
|
handle: string,
|
|
@@ -106,7 +110,7 @@ export function computableFromRF(
|
|
|
106
110
|
|
|
107
111
|
try {
|
|
108
112
|
const runtime = scope.manage(env.quickJs.newRuntime());
|
|
109
|
-
runtime.setMemoryLimit(
|
|
113
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
110
114
|
runtime.setMaxStackSize(1024 * 320);
|
|
111
115
|
|
|
112
116
|
let deadlineSettings: DeadlineSettings | undefined;
|
|
@@ -126,50 +130,83 @@ export function computableFromRF(
|
|
|
126
130
|
{ computableCtx: cCtx, blockCtx: ctx, mlEnv: env },
|
|
127
131
|
);
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (getDebugFlags().logOutputStatus)
|
|
152
|
-
console.log(`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`);
|
|
153
|
-
keepVmAlive = true;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
ir: toBeResolved,
|
|
157
|
-
postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {
|
|
158
|
-
// resolving futures
|
|
159
|
-
for (const [handle, value] of Object.entries(resolved)) rCtx.runCallback(handle, value);
|
|
160
|
-
|
|
161
|
-
// rendering result
|
|
162
|
-
const renderedResult = rCtx.importObjectUniversal(result);
|
|
163
|
-
|
|
164
|
-
// logging
|
|
165
|
-
recalculationCounter++;
|
|
133
|
+
const flightRecorder = env.driverKit.flightRecorder;
|
|
134
|
+
const renderInfo = {
|
|
135
|
+
blockId: ctx.blockId,
|
|
136
|
+
key,
|
|
137
|
+
lambda: fh.handle,
|
|
138
|
+
// The sandbox has its own 8 MB ceiling, so the model's own objects can
|
|
139
|
+
// never exhaust the host heap. What can is the volume crossing out of
|
|
140
|
+
// it, which `stats` counts.
|
|
141
|
+
sandboxMemoryLimit: QUICK_JS_MEMORY_LIMIT,
|
|
142
|
+
getStats: () => ({ ...rCtx.stats }),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return recordModelRenderSync(flightRecorder, renderInfo, () => {
|
|
146
|
+
rCtx.evaluateBundle(code.content);
|
|
147
|
+
const result = rCtx.runCallback(fh.handle);
|
|
148
|
+
|
|
149
|
+
rCtx.resetComputableCtx();
|
|
150
|
+
|
|
151
|
+
const toBeResolved = rCtx.computableHelper!.computablesToResolve;
|
|
152
|
+
|
|
153
|
+
if (Object.keys(toBeResolved).length === 0) {
|
|
154
|
+
const importedResult = rCtx.importObjectUniversal(result);
|
|
166
155
|
if (getDebugFlags().logJsExecStat)
|
|
167
|
-
console.log(`[jsExec] ${key}
|
|
168
|
-
logOutputStatus(
|
|
156
|
+
console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);
|
|
157
|
+
logOutputStatus(
|
|
158
|
+
fh.handle,
|
|
159
|
+
importedResult,
|
|
160
|
+
cCtx.unstableMarker === undefined,
|
|
161
|
+
-1,
|
|
162
|
+
cCtx.unstableMarker,
|
|
163
|
+
);
|
|
164
|
+
return { ir: importedResult };
|
|
165
|
+
}
|
|
169
166
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
let recalculationCounter = 0;
|
|
168
|
+
if (getDebugFlags().logOutputStatus)
|
|
169
|
+
console.log(
|
|
170
|
+
`Output ${fh.handle} scaffold calculated (not all computables resolved yet).`,
|
|
171
|
+
);
|
|
172
|
+
keepVmAlive = true;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
ir: toBeResolved,
|
|
176
|
+
postprocessValue: (resolved: Record<string, unknown>, { unstableMarker, stable }) => {
|
|
177
|
+
recalculationCounter++;
|
|
178
|
+
// A deferred render resumes here, potentially many times, and each
|
|
179
|
+
// resumption can build joins of its own, so each gets its own span.
|
|
180
|
+
return recordModelRenderSync(
|
|
181
|
+
flightRecorder,
|
|
182
|
+
{ ...renderInfo, recalculation: recalculationCounter },
|
|
183
|
+
() => {
|
|
184
|
+
// resolving futures
|
|
185
|
+
for (const [handle, value] of Object.entries(resolved))
|
|
186
|
+
rCtx.runCallback(handle, value);
|
|
187
|
+
|
|
188
|
+
// rendering result
|
|
189
|
+
const renderedResult = rCtx.importObjectUniversal(result);
|
|
190
|
+
|
|
191
|
+
// logging
|
|
192
|
+
if (getDebugFlags().logJsExecStat)
|
|
193
|
+
console.log(
|
|
194
|
+
`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`,
|
|
195
|
+
);
|
|
196
|
+
logOutputStatus(
|
|
197
|
+
fh.handle,
|
|
198
|
+
renderedResult,
|
|
199
|
+
stable,
|
|
200
|
+
recalculationCounter,
|
|
201
|
+
unstableMarker,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return renderedResult;
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
});
|
|
173
210
|
} catch (e) {
|
|
174
211
|
keepVmAlive = false;
|
|
175
212
|
throw e;
|
|
@@ -189,7 +226,7 @@ export function executeSingleLambda(
|
|
|
189
226
|
const scope = new Scope();
|
|
190
227
|
try {
|
|
191
228
|
const runtime = scope.manage(quickJs.newRuntime());
|
|
192
|
-
runtime.setMemoryLimit(
|
|
229
|
+
runtime.setMemoryLimit(QUICK_JS_MEMORY_LIMIT);
|
|
193
230
|
runtime.setMaxStackSize(1024 * 320);
|
|
194
231
|
|
|
195
232
|
let deadlineSettings: DeadlineSettings | undefined;
|
|
@@ -19,6 +19,12 @@ import { isAsyncDisposable } from "@milaboratories/helpers";
|
|
|
19
19
|
import { HmacSha256Signer } from "@milaboratories/ts-helpers";
|
|
20
20
|
import type { InternalPFrameDriver } from "../pool";
|
|
21
21
|
import { createPFrameDriver } from "../pool";
|
|
22
|
+
import type { Recorder } from "@milaboratories/pl-flight-recorder";
|
|
23
|
+
import {
|
|
24
|
+
openFlightSession,
|
|
25
|
+
wrapDataDriver,
|
|
26
|
+
wrapModelDriver,
|
|
27
|
+
} from "@milaboratories/pl-flight-recorder";
|
|
22
28
|
import type { DriverKitOps, DriverKitOpsConstructor } from "./ops";
|
|
23
29
|
import { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from "./ops";
|
|
24
30
|
|
|
@@ -46,6 +52,13 @@ export interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {
|
|
|
46
52
|
// override with wider interface
|
|
47
53
|
readonly frontendDriver: DownloadUrlDriver;
|
|
48
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Diagnostics log for the model layer, present only when flight recording is
|
|
57
|
+
* switched on. Render spans are written through it so that a join recorded by
|
|
58
|
+
* the instrumented pFrame driver can be attributed to the block that built it.
|
|
59
|
+
* */
|
|
60
|
+
readonly flightRecorder?: Recorder;
|
|
61
|
+
|
|
49
62
|
/**
|
|
50
63
|
* Signer is initialized from local secret in drivers initialization routine,
|
|
51
64
|
* so constitutes a part of the driver kit
|
|
@@ -114,7 +127,7 @@ export async function initDriverKit(
|
|
|
114
127
|
ops.virtualLocalStoragesOverride,
|
|
115
128
|
);
|
|
116
129
|
|
|
117
|
-
const
|
|
130
|
+
const rawPFrameDriver = await createPFrameDriver({
|
|
118
131
|
blobDriver,
|
|
119
132
|
logger: ops.logger,
|
|
120
133
|
spillPath: ops.pframesSpillPath,
|
|
@@ -123,6 +136,19 @@ export async function initDriverKit(
|
|
|
123
136
|
cacheOps: ops.parquetCacheOps,
|
|
124
137
|
});
|
|
125
138
|
|
|
139
|
+
// Every join a block model builds and every row it reads back passes through
|
|
140
|
+
// this one driver, so instrumenting it here covers the whole model layer
|
|
141
|
+
// without touching the call sites. The session is undefined unless recording
|
|
142
|
+
// is switched on, in which case the driver is used exactly as before.
|
|
143
|
+
const flightSession = openFlightSession({ role: "middle-layer" });
|
|
144
|
+
const pFrameDriver: InternalPFrameDriver = flightSession
|
|
145
|
+
? wrapDataDriver(
|
|
146
|
+
wrapModelDriver(rawPFrameDriver, flightSession.recorder, flightSession.registry),
|
|
147
|
+
flightSession.recorder,
|
|
148
|
+
flightSession.registry,
|
|
149
|
+
)
|
|
150
|
+
: rawPFrameDriver;
|
|
151
|
+
|
|
126
152
|
const frontendDownloadDriver = new DownloadUrlDriver(
|
|
127
153
|
ops.logger,
|
|
128
154
|
pl.httpDispatcher,
|
|
@@ -140,6 +166,7 @@ export async function initDriverKit(
|
|
|
140
166
|
uploadDriver,
|
|
141
167
|
pFrameDriver,
|
|
142
168
|
frontendDriver: frontendDownloadDriver,
|
|
169
|
+
flightRecorder: flightSession?.recorder,
|
|
143
170
|
};
|
|
144
171
|
|
|
145
172
|
const dispose = async () => {
|
|
@@ -147,6 +174,7 @@ export async function initDriverKit(
|
|
|
147
174
|
isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],
|
|
148
175
|
);
|
|
149
176
|
await Promise.all(disposePromises);
|
|
177
|
+
flightSession?.close("driver-kit-disposed");
|
|
150
178
|
};
|
|
151
179
|
|
|
152
180
|
return {
|