Skip to content

Commit 12beef1

Browse files
committed
perf(schema-compiler): Reduce JS compilation memory usage 3x-5x times.
1 parent db487ac commit 12beef1

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ export class DataSchemaCompiler {
140140

141141
private readonly compiledScriptCache: LRUCache<string, vm.Script>;
142142

143+
private compileV8ContextCache: vm.Context;
144+
143145
// FIXME: Is public only because of tests, should be private
144146
public compilePromise: any;
145147

@@ -172,6 +174,7 @@ export class DataSchemaCompiler {
172174
this.workerPool = null;
173175
this.compilerId = options.compilerId || 'default';
174176
this.compiledScriptCache = options.compiledScriptCache;
177+
this.compileV8ContextCache = vm.createContext({});
175178
}
176179

177180
public compileObjects(compileServices, objects, errorsReport: ErrorReporter) {
@@ -563,7 +566,18 @@ export class DataSchemaCompiler {
563566
return this.compiledScriptCache.get(cacheKey)!;
564567
}
565568

566-
const script = new vm.Script(file.content, { filename: file.fileName });
569+
// As we run all data model files in the same context,
570+
// we need to wrap the code in an IIFE to avoid polluting and modifying the global scope,
571+
// and to provide a controlled environment for the code execution.
572+
const wrappedCode = `
573+
(function(globals) {
574+
"use strict";
575+
const { view, cube, context, addExport, setExport, asyncModule, require, COMPILE_CONTEXT } = globals;
576+
${file.content}
577+
})(sandboxLocals);
578+
`;
579+
580+
const script = new vm.Script(wrappedCode, { filename: file.fileName });
567581
this.compiledScriptCache.set(cacheKey, script);
568582
return script;
569583
}
@@ -582,18 +596,17 @@ export class DataSchemaCompiler {
582596
try {
583597
const script = this.getJsScript(file);
584598

585-
script.runInNewContext({
599+
const sandboxLocals = Object.freeze({
586600
view: (name, cube) => (
587601
!cube ?
588602
this.cubeFactory({ ...name, fileName: file.fileName, isView: true }) :
589603
cubes.push({ ...cube, name, fileName: file.fileName, isView: true })
590604
),
591-
cube:
592-
(name, cube) => (
593-
!cube ?
594-
this.cubeFactory({ ...name, fileName: file.fileName }) :
595-
cubes.push({ ...cube, name, fileName: file.fileName })
596-
),
605+
cube: (name, cube) => (
606+
!cube ?
607+
this.cubeFactory({ ...name, fileName: file.fileName }) :
608+
cubes.push({ ...cube, name, fileName: file.fileName })
609+
),
597610
context: (name, context) => contexts.push({ ...context, name, fileName: file.fileName }),
598611
addExport: (obj) => {
599612
exports[file.fileName] = exports[file.fileName] || {};
@@ -637,9 +650,15 @@ export class DataSchemaCompiler {
637650
}
638651
},
639652
COMPILE_CONTEXT: this.standalone ? this.standaloneCompileContextProxy() : this.cloneCompileContextWithGetterAlias(this.compileContext || {}),
640-
}, { filename: file.fileName, timeout: 15000 });
653+
});
654+
655+
this.compileV8ContextCache.sandboxLocals = sandboxLocals;
656+
657+
script.runInContext(this.compileV8ContextCache, { timeout: 15000 });
641658
} catch (e) {
642659
errorsReport.error(e);
660+
} finally {
661+
delete this.compileV8ContextCache.sandboxLocals;
643662
}
644663
}
645664

0 commit comments

Comments
 (0)