Skip to content

Commit 84f90c0

Browse files
authored
fix(schema-compiler): Correct models transpilation in native in multitenant environments (#9234)
* add types to ErrorReporter * update native error reporter to use RefCells instead of Arc Mutex * Revert "update native error reporter to use RefCells instead of Arc Mutex" This reverts commit 406ee85. * pass compiler Id to transpilation native * Fix warn * use cargo insta for snapshot testing * introduce LruCache for transpilation
1 parent 9c6153d commit 84f90c0

26 files changed

+631
-271
lines changed

packages/cubejs-backend-native/Cargo.lock

Lines changed: 15 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cubejs-backend-native/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ tokio = { version = "1", features = ["full", "rt"] }
4848
uuid = { version = "1", features = ["v4"] }
4949
bytes = "1.5.0"
5050
regex = "1.10.2"
51+
lru = "0.13.0"
5152

5253
[dependencies.neon]
5354
version = "=1"

packages/cubejs-backend-native/js/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export type SQLInterfaceOptions = {
104104
export interface TransformConfig {
105105
fileName: string;
106106
transpilers: string[];
107+
compilerId: string;
107108
metaData?: {
108109
cubeNames: string[];
109110
cubeSymbols: Record<string, Record<string, boolean>>;

packages/cubejs-backend-native/src/transpilers.rs

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ use crate::node_obj_deserializer::JsValueDeserializer;
22
use crate::node_obj_serializer::NodeObjSerializer;
33
use anyhow::anyhow;
44
use cubetranspilers::{run_transpilers, TransformConfig, Transpilers};
5+
use lru::LruCache;
56
use neon::context::{Context, FunctionContext, ModuleContext};
67
use neon::prelude::{JsPromise, JsResult, JsValue, NeonResult};
78
use neon::types::JsString;
89
use serde::Deserialize;
910
use std::collections::{HashMap, HashSet};
10-
use std::sync::RwLock;
11+
use std::env;
12+
use std::num::NonZeroUsize;
13+
use std::sync::{LazyLock, Mutex};
1114

1215
#[derive(Deserialize, Default, Clone, Debug)]
1316
#[serde(rename_all = "camelCase")]
@@ -22,10 +25,25 @@ pub struct TransformMetaData {
2225
pub struct TransformRequestConfig {
2326
pub file_name: String,
2427
pub transpilers: Vec<Transpilers>,
28+
pub compiler_id: String,
2529
pub meta_data: Option<TransformMetaData>,
2630
}
2731

28-
static METADATA_CACHE: RwLock<Option<TransformMetaData>> = RwLock::new(None);
32+
/// It should be equal or more then number of internal libuv threads used by Neon
33+
/// By 01.2025 it defaults to 4. But maybe changed via `UV_THREADPOOL_SIZE` env var.
34+
/// `CUBEJS_TRANSPILER_METADATA_CACHE_SIZE` env var is provided for fine tuning.
35+
/// @see https://docs.libuv.org/en/v1.x/threadpool.html
36+
/// @see https://nodejs.org/api/cli.html#cli_uv_threadpool_size_size
37+
static DEFAULT_CACHE_SIZE: usize = 16;
38+
39+
static METADATA_CACHE: LazyLock<Mutex<LruCache<String, TransformMetaData>>> = LazyLock::new(|| {
40+
let cache_size = env::var("CUBEJS_TRANSPILER_METADATA_CACHE_SIZE")
41+
.ok()
42+
.and_then(|s| s.parse::<usize>().ok())
43+
.and_then(NonZeroUsize::new)
44+
.unwrap_or(NonZeroUsize::new(DEFAULT_CACHE_SIZE).unwrap());
45+
Mutex::new(LruCache::new(cache_size))
46+
});
2947

3048
pub fn register_module(cx: &mut ModuleContext) -> NeonResult<()> {
3149
cx.export_function("transpileJs", transpile_js)?;
@@ -44,7 +62,7 @@ pub fn transpile_js(mut cx: FunctionContext) -> JsResult<JsPromise> {
4462
let transform_config: TransformConfig = match transform_request_config {
4563
Ok(data) => match data.meta_data {
4664
Some(meta_data) => {
47-
let mut config_lock = METADATA_CACHE.write().unwrap();
65+
let mut config_lock = METADATA_CACHE.lock().unwrap();
4866
let cache = TransformMetaData {
4967
cube_names: meta_data.cube_names,
5068
cube_symbols: meta_data.cube_symbols,
@@ -57,17 +75,27 @@ pub fn transpile_js(mut cx: FunctionContext) -> JsResult<JsPromise> {
5775
cube_symbols: cache.cube_symbols.clone(),
5876
context_symbols: cache.context_symbols.clone(),
5977
};
60-
*config_lock = Some(cache);
78+
config_lock.put(data.compiler_id.clone(), cache);
6179
cfg
6280
}
6381
None => {
64-
let cache = METADATA_CACHE.read().unwrap().clone().unwrap_or_default();
65-
TransformConfig {
66-
file_name: data.file_name,
67-
transpilers: data.transpilers,
68-
cube_names: cache.cube_names.clone(),
69-
cube_symbols: cache.cube_symbols.clone(),
70-
context_symbols: cache.context_symbols.clone(),
82+
let mut config_lock = METADATA_CACHE.lock().unwrap();
83+
84+
match config_lock.get(&data.compiler_id) {
85+
Some(cached) => TransformConfig {
86+
file_name: data.file_name,
87+
transpilers: data.transpilers,
88+
cube_names: cached.cube_names.clone(),
89+
cube_symbols: cached.cube_symbols.clone(),
90+
context_symbols: cached.context_symbols.clone(),
91+
},
92+
None => TransformConfig {
93+
file_name: data.file_name,
94+
transpilers: data.transpilers,
95+
cube_names: HashSet::new(),
96+
cube_symbols: HashMap::new(),
97+
context_symbols: HashMap::new(),
98+
},
7199
}
72100
}
73101
},

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class DataSchemaCompiler {
4646
this.yamlCompiler.dataSchemaCompiler = this;
4747
this.pythonContext = null;
4848
this.workerPool = null;
49+
this.compilerId = options.compilerId;
4950
}
5051

5152
compileObjects(compileServices, objects, errorsReport) {
@@ -97,6 +98,7 @@ export class DataSchemaCompiler {
9798

9899
const transpilationWorkerThreads = getEnv('transpilationWorkerThreads');
99100
const transpilationNative = getEnv('transpilationNative');
101+
const { compilerId } = this;
100102

101103
if (!transpilationNative && transpilationWorkerThreads) {
102104
const wc = getEnv('transpilationWorkerThreadsCount');
@@ -141,9 +143,9 @@ export class DataSchemaCompiler {
141143
content: ';',
142144
};
143145

144-
await this.transpileJsFile(dummyFile, errorsReport, { cubeNames, cubeSymbols, transpilerNames, contextSymbols: CONTEXT_SYMBOLS });
146+
await this.transpileJsFile(dummyFile, errorsReport, { cubeNames, cubeSymbols, transpilerNames, contextSymbols: CONTEXT_SYMBOLS, compilerId });
145147

146-
results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { transpilerNames })));
148+
results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { transpilerNames, compilerId })));
147149
} else if (transpilationWorkerThreads) {
148150
results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { cubeNames, cubeSymbols, transpilerNames })));
149151
} else {
@@ -172,7 +174,11 @@ export class DataSchemaCompiler {
172174
content: ';',
173175
};
174176

175-
return this.transpileJsFile(dummyFile, errorsReport, { cubeNames: [], cubeSymbols: {}, transpilerNames: [], contextSymbols: {} });
177+
return this.transpileJsFile(
178+
dummyFile,
179+
errorsReport,
180+
{ cubeNames: [], cubeSymbols: {}, transpilerNames: [], contextSymbols: {}, compilerId: this.compilerId }
181+
);
176182
} else if (transpilationWorkerThreads && this.workerPool) {
177183
this.workerPool.terminate();
178184
}
@@ -219,12 +225,13 @@ export class DataSchemaCompiler {
219225
}
220226
}
221227

222-
async transpileJsFile(file, errorsReport, { cubeNames, cubeSymbols, contextSymbols, transpilerNames }) {
228+
async transpileJsFile(file, errorsReport, { cubeNames, cubeSymbols, contextSymbols, transpilerNames, compilerId }) {
223229
try {
224230
if (getEnv('transpilationNative')) {
225231
const reqData = {
226232
fileName: file.fileName,
227233
transpilers: transpilerNames,
234+
compilerId,
228235
...(cubeNames && {
229236
metaData: {
230237
cubeNames,

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { codeFrameColumns, SourceLocation } from '@babel/code-frame';
33
import { UserError } from './UserError';
44
import { CompileError } from './CompileError';
55

6+
export type ErrorLikeObject = {
7+
message: string;
8+
};
9+
10+
export type PossibleError = Error | UserError | string | ErrorLikeObject;
11+
612
export interface CompilerErrorInterface {
713
message: string;
814
plainMessage?: string
@@ -141,16 +147,16 @@ export class ErrorReporter {
141147
return this.rootReporter().errors;
142148
}
143149

144-
public addErrors(errors: any[]) {
150+
public addErrors(errors: PossibleError[]) {
145151
errors.forEach((e: any) => { this.error(e); });
146152
}
147153

148154
public getWarnings() {
149155
return this.rootReporter().warnings;
150156
}
151157

152-
public addWarnings(warnings: any[]) {
153-
warnings.forEach((w: any) => { this.warning(w); });
158+
public addWarnings(warnings: SyntaxErrorInterface[]) {
159+
warnings.forEach(w => { this.warning(w); });
154160
}
155161

156162
protected rootReporter(): ErrorReporter {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp
5959
transpilers.push(new CubeCheckDuplicatePropTranspiler());
6060
}
6161

62+
const compilerId = uuidv4();
63+
6264
const compiler = new DataSchemaCompiler(repo, Object.assign({}, {
6365
cubeNameCompilers: [cubeDictionary],
6466
preTranspileCubeCompilers: [cubeSymbols, cubeValidator],
@@ -80,6 +82,7 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp
8082
standalone: options.standalone,
8183
nativeInstance,
8284
yamlCompiler,
85+
compilerId,
8386
}, options));
8487

8588
return {
@@ -90,7 +93,7 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp
9093
joinGraph,
9194
compilerCache,
9295
headCommitId: options.headCommitId,
93-
compilerId: uuidv4(),
96+
compilerId,
9497
};
9598
};
9699

rust/cubenativeutils/Cargo.lock

Lines changed: 20 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/cubesql/Cargo.lock

Lines changed: 20 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)