Skip to content

Commit a65deb0

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

File tree

1 file changed

+30
-9
lines changed

1 file changed

+30
-9
lines changed

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

Lines changed: 30 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,20 @@ 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 errors like:
571+
// Identifier 'xxx' has already been declared,
572+
// avoid polluting and modifying the global scope,
573+
// and to provide a controlled environment for the code execution.
574+
const wrappedCode = `
575+
(function(globals) {
576+
"use strict";
577+
const { view, cube, context, addExport, setExport, asyncModule, require, COMPILE_CONTEXT } = globals;
578+
${file.content}
579+
})(sandboxLocals);
580+
`;
581+
582+
const script = new vm.Script(wrappedCode, { filename: file.fileName });
567583
this.compiledScriptCache.set(cacheKey, script);
568584
return script;
569585
}
@@ -582,18 +598,17 @@ export class DataSchemaCompiler {
582598
try {
583599
const script = this.getJsScript(file);
584600

585-
script.runInNewContext({
601+
const sandboxLocals = Object.freeze({
586602
view: (name, cube) => (
587603
!cube ?
588604
this.cubeFactory({ ...name, fileName: file.fileName, isView: true }) :
589605
cubes.push({ ...cube, name, fileName: file.fileName, isView: true })
590606
),
591-
cube:
592-
(name, cube) => (
593-
!cube ?
594-
this.cubeFactory({ ...name, fileName: file.fileName }) :
595-
cubes.push({ ...cube, name, fileName: file.fileName })
596-
),
607+
cube: (name, cube) => (
608+
!cube ?
609+
this.cubeFactory({ ...name, fileName: file.fileName }) :
610+
cubes.push({ ...cube, name, fileName: file.fileName })
611+
),
597612
context: (name, context) => contexts.push({ ...context, name, fileName: file.fileName }),
598613
addExport: (obj) => {
599614
exports[file.fileName] = exports[file.fileName] || {};
@@ -637,9 +652,15 @@ export class DataSchemaCompiler {
637652
}
638653
},
639654
COMPILE_CONTEXT: this.standalone ? this.standaloneCompileContextProxy() : this.cloneCompileContextWithGetterAlias(this.compileContext || {}),
640-
}, { filename: file.fileName, timeout: 15000 });
655+
});
656+
657+
this.compileV8ContextCache.sandboxLocals = sandboxLocals;
658+
659+
script.runInContext(this.compileV8ContextCache, { timeout: 15000 });
641660
} catch (e) {
642661
errorsReport.error(e);
662+
} finally {
663+
delete this.compileV8ContextCache.sandboxLocals;
643664
}
644665
}
645666

0 commit comments

Comments
 (0)