Skip to content

Commit 5b048b2

Browse files
authored
fix(cubeorchestrator): Increase VerifierOptions max_tables to 10M (#10121)
* fix flatbuffer codegen script (paths) * add flatbuffers as deps * fix(cubeorchestrator): Increase VerifierOptions max_tables to 10M And add parser tests
1 parent 8afa799 commit 5b048b2

File tree

5 files changed

+218
-9
lines changed

5 files changed

+218
-9
lines changed

packages/cubejs-backend-native/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22

3-
flatc --ts ../../../rust/cubestore/cubestore/src/codegen/http_message.fbs --ts-flat-files
3+
flatc --ts ../../../rust/cubeshared/src/codegen/http_message.fbs --ts-flat-files
44
mv http_message.ts index.ts

rust/cubeorchestrator/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/cubeorchestrator/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ serde_json = "1.0.133"
1111
anyhow = "1.0"
1212
itertools = "0.13.0"
1313
indexmap = { version = "2.0", features = ["serde"] }
14+
flatbuffers = "23.5.26"
1415

1516
[dependencies.neon]
1617
version = "=1"

rust/cubeorchestrator/src/query_message_parser.rs

Lines changed: 214 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use crate::{
22
query_result_transform::{DBResponsePrimitive, DBResponseValue},
33
transport::JsRawData,
44
};
5-
use cubeshared::codegen::{root_as_http_message, HttpCommand};
5+
use cubeshared::codegen::{root_as_http_message_with_opts, HttpCommand};
6+
use flatbuffers::VerifierOptions;
67
use indexmap::IndexMap;
78
use neon::prelude::Finalize;
89

@@ -12,7 +13,7 @@ pub enum ParseError {
1213
EmptyResultSet,
1314
NullRow,
1415
ColumnNameNotDefined,
15-
FlatBufferError,
16+
FlatBufferError(String),
1617
ErrorMessage(String),
1718
}
1819

@@ -23,7 +24,7 @@ impl std::fmt::Display for ParseError {
2324
ParseError::EmptyResultSet => write!(f, "Empty resultSet"),
2425
ParseError::NullRow => write!(f, "Null row"),
2526
ParseError::ColumnNameNotDefined => write!(f, "Column name is not defined"),
26-
ParseError::FlatBufferError => write!(f, "FlatBuffer parsing error"),
27+
ParseError::FlatBufferError(msg) => write!(f, "FlatBuffer parsing error: {}", msg),
2728
ParseError::ErrorMessage(msg) => write!(f, "Error: {}", msg),
2829
}
2930
}
@@ -48,14 +49,18 @@ impl QueryResult {
4849
columns_pos: IndexMap::new(),
4950
};
5051

51-
let http_message =
52-
root_as_http_message(msg_data).map_err(|_| ParseError::FlatBufferError)?;
52+
let mut opts = VerifierOptions::default();
53+
opts.max_tables = 10_000_000; // Support up to 10M tables
54+
opts.max_apparent_size = 1 << 31; // 2GB limit for large datasets
55+
56+
let http_message = root_as_http_message_with_opts(&opts, msg_data)
57+
.map_err(|err| ParseError::FlatBufferError(err.to_string()))?;
5358

5459
match http_message.command_type() {
5560
HttpCommand::HttpError => {
56-
let http_error = http_message
57-
.command_as_http_error()
58-
.ok_or(ParseError::FlatBufferError)?;
61+
let http_error = http_message.command_as_http_error().ok_or_else(|| {
62+
ParseError::FlatBufferError("Failed to parse HttpError command".to_string())
63+
})?;
5964
let error_message = http_error.error().unwrap_or("Unknown error").to_string();
6065
Err(ParseError::ErrorMessage(error_message))
6166
}
@@ -145,3 +150,204 @@ impl QueryResult {
145150
})
146151
}
147152
}
153+
154+
#[cfg(test)]
155+
mod tests {
156+
use super::*;
157+
use cubeshared::codegen::{
158+
root_as_http_message_unchecked, HttpColumnValue, HttpColumnValueArgs, HttpCommand,
159+
HttpMessage, HttpMessageArgs, HttpResultSet, HttpResultSetArgs, HttpRow, HttpRowArgs,
160+
};
161+
use flatbuffers::FlatBufferBuilder;
162+
163+
/// Helper function to create a test HttpMessage with a given number of rows and columns
164+
fn create_test_message(num_rows: usize, num_columns: usize) -> Vec<u8> {
165+
let mut builder = FlatBufferBuilder::new();
166+
167+
// Create column names
168+
let column_names: Vec<_> = (0..num_columns)
169+
.map(|i| builder.create_string(&format!("column_{}", i)))
170+
.collect();
171+
172+
// Create rows with values
173+
let mut rows_vec = Vec::with_capacity(num_rows);
174+
for row_idx in 0..num_rows {
175+
// Create column values for this row
176+
let mut values_vec = Vec::with_capacity(num_columns);
177+
for col_idx in 0..num_columns {
178+
let value_str = builder.create_string(&format!("row_{}_col_{}", row_idx, col_idx));
179+
let col_value = HttpColumnValue::create(
180+
&mut builder,
181+
&HttpColumnValueArgs {
182+
string_value: Some(value_str),
183+
},
184+
);
185+
values_vec.push(col_value);
186+
}
187+
188+
let values_vector = builder.create_vector(&values_vec);
189+
let row = HttpRow::create(
190+
&mut builder,
191+
&HttpRowArgs {
192+
values: Some(values_vector),
193+
},
194+
);
195+
rows_vec.push(row);
196+
}
197+
198+
// Create the result set
199+
let columns_vector = builder.create_vector(&column_names);
200+
let rows_vector = builder.create_vector(&rows_vec);
201+
202+
let result_set = HttpResultSet::create(
203+
&mut builder,
204+
&HttpResultSetArgs {
205+
columns: Some(columns_vector),
206+
rows: Some(rows_vector),
207+
},
208+
);
209+
210+
// Create the message
211+
let connection_id = builder.create_string("test_connection");
212+
let message = HttpMessage::create(
213+
&mut builder,
214+
&HttpMessageArgs {
215+
message_id: 1,
216+
command_type: HttpCommand::HttpResultSet,
217+
command: Some(result_set.as_union_value()),
218+
connection_id: Some(connection_id),
219+
},
220+
);
221+
222+
builder.finish(message, None);
223+
builder.finished_data().to_vec()
224+
}
225+
226+
#[test]
227+
fn test_parse_small_result_set() {
228+
// Small result set should work fine
229+
let msg_data = create_test_message(10, 5);
230+
let result = QueryResult::from_cubestore_fb(&msg_data);
231+
assert!(result.is_ok());
232+
233+
let query_result = result.unwrap();
234+
assert_eq!(query_result.columns.len(), 5);
235+
assert_eq!(query_result.rows.len(), 10);
236+
}
237+
238+
#[test]
239+
fn test_parse_medium_result_set() {
240+
// Medium result set: 1000 rows, 20 columns
241+
let msg_data = create_test_message(1000, 20);
242+
let result = QueryResult::from_cubestore_fb(&msg_data);
243+
assert!(result.is_ok());
244+
245+
let query_result = result.unwrap();
246+
assert_eq!(query_result.columns.len(), 20);
247+
assert_eq!(query_result.rows.len(), 1000);
248+
}
249+
250+
#[test]
251+
fn test_parse_large_result_set() {
252+
// Large result set: 10,000 rows, 30 columns
253+
// This should start showing verification issues
254+
let msg_data = create_test_message(10_000, 30);
255+
let result = QueryResult::from_cubestore_fb(&msg_data);
256+
assert!(result.is_ok());
257+
258+
let query_result = result.unwrap();
259+
assert_eq!(query_result.columns.len(), 30);
260+
assert_eq!(query_result.rows.len(), 10_000);
261+
}
262+
263+
#[test]
264+
fn test_parse_very_large_result_set() {
265+
// Very large result set: 33,000 rows, 40 columns
266+
let msg_data = create_test_message(33_000, 40);
267+
let result = QueryResult::from_cubestore_fb(&msg_data);
268+
assert!(result.is_ok());
269+
270+
let query_result = result.unwrap();
271+
assert_eq!(query_result.columns.len(), 40);
272+
assert_eq!(query_result.rows.len(), 33_000);
273+
}
274+
275+
#[test]
276+
fn test_parse_huge_result_set() {
277+
// Huge result set: 50,000 rows, 100 columns
278+
let msg_data = create_test_message(50_000, 100);
279+
let result = QueryResult::from_cubestore_fb(&msg_data);
280+
assert!(result.is_ok());
281+
282+
let query_result = result.unwrap();
283+
assert_eq!(query_result.columns.len(), 100);
284+
assert_eq!(query_result.rows.len(), 50_000);
285+
}
286+
287+
#[test]
288+
fn test_compare_with_unchecked_parse() {
289+
// Test to demonstrate that unchecked parsing would work
290+
let msg_data = create_test_message(33_000, 40);
291+
292+
// Checked version (current implementation)
293+
let checked_result = QueryResult::from_cubestore_fb(&msg_data);
294+
295+
// Try unchecked version to verify the data itself is valid
296+
let unchecked_result = unsafe {
297+
let http_message = root_as_http_message_unchecked(&msg_data);
298+
match http_message.command_type() {
299+
HttpCommand::HttpResultSet => {
300+
let result_set = http_message.command_as_http_result_set();
301+
if let Some(rs) = result_set {
302+
if let Some(rows) = rs.rows() {
303+
println!("Unchecked parse found {} rows", rows.len());
304+
Ok(rows.len())
305+
} else {
306+
Err("No rows")
307+
}
308+
} else {
309+
Err("No result set")
310+
}
311+
}
312+
_ => Err("Wrong command type"),
313+
}
314+
};
315+
316+
assert!(checked_result.is_ok());
317+
assert!(unchecked_result.is_ok());
318+
}
319+
320+
#[test]
321+
fn test_parse_with_custom_verifier_options() {
322+
use cubeshared::codegen::root_as_http_message_with_opts;
323+
use flatbuffers::VerifierOptions;
324+
325+
// Test that custom verifier options can handle large datasets
326+
let msg_data = create_test_message(33_000, 40);
327+
328+
// Create custom verifier options with increased limits
329+
let mut opts = VerifierOptions::default();
330+
opts.max_tables = 10_000_000; // Support up to 10M tables
331+
opts.max_apparent_size = 1 << 31; // 2GB limit
332+
333+
// This should succeed with custom options
334+
let result = root_as_http_message_with_opts(&opts, &msg_data);
335+
336+
match result {
337+
Ok(http_message) => match http_message.command_type() {
338+
HttpCommand::HttpResultSet => {
339+
let result_set = http_message.command_as_http_result_set();
340+
if let Some(rs) = result_set {
341+
if let Some(rows) = rs.rows() {
342+
assert_eq!(rows.len(), 33_000);
343+
}
344+
}
345+
}
346+
_ => panic!("Wrong command type"),
347+
},
348+
Err(e) => {
349+
panic!("Failed to parse with custom verifier options: {:?}", e);
350+
}
351+
}
352+
}
353+
}

0 commit comments

Comments
 (0)