Skip to content

Commit 47fd8f9

Browse files
test: add coverage for parallel json reads
1 parent 2885449 commit 47fd8f9

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

default-engine/src/json.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ mod tests {
425425
use itertools::Itertools;
426426
use serde_json::json;
427427
use test_utils::engine_contract::test_json_handler_file_path_contract;
428+
use test_utils::TestCancellationToken;
428429
use tracing::info;
429430

430431
use super::*;
@@ -1129,6 +1130,132 @@ mod tests {
11291130
}
11301131
}
11311132

1133+
#[tokio::test(flavor = "multi_thread")]
1134+
async fn test_read_json_files_parallel_empty_files() {
1135+
let store = Arc::new(InMemory::new());
1136+
let handler = DefaultJsonHandler::new(
1137+
store,
1138+
Arc::new(TokioMultiThreadExecutor::new(
1139+
tokio::runtime::Handle::current(),
1140+
)),
1141+
)
1142+
.with_parallel_chunks(NonZero::new(4));
1143+
let physical_schema = schema_ref! { nullable "val": INTEGER };
1144+
let result: Vec<_> = handler
1145+
.read_json_files(&[], physical_schema, None)
1146+
.unwrap()
1147+
.try_collect()
1148+
.unwrap();
1149+
assert!(result.is_empty(), "empty file list must yield no batches");
1150+
}
1151+
1152+
#[tokio::test(flavor = "multi_thread")]
1153+
async fn test_read_json_files_parallel_missing_file_errors() {
1154+
let store = Arc::new(InMemory::new());
1155+
let missing_path = Path::from("test/missing");
1156+
let url = Url::parse(&format!("memory:/{missing_path}")).unwrap();
1157+
let files = vec![FileMeta {
1158+
location: url,
1159+
last_modified: 0,
1160+
size: 100,
1161+
}];
1162+
let handler = DefaultJsonHandler::new(
1163+
store,
1164+
Arc::new(TokioMultiThreadExecutor::new(
1165+
tokio::runtime::Handle::current(),
1166+
)),
1167+
)
1168+
.with_parallel_chunks(NonZero::new(4));
1169+
let physical_schema = schema_ref! { nullable "val": INTEGER };
1170+
let result: DeltaResult<Vec<_>> = handler
1171+
.read_json_files(&files, physical_schema, None)
1172+
.unwrap()
1173+
.try_collect();
1174+
assert!(result.is_err(), "missing file must produce an error");
1175+
}
1176+
1177+
#[tokio::test(flavor = "multi_thread")]
1178+
async fn test_read_json_files_parallel_with_cancelled_token() {
1179+
let store = Arc::new(InMemory::new());
1180+
store
1181+
.put(
1182+
&Path::from("test/0"),
1183+
Bytes::from(r#"{"val": 0}"#).into(),
1184+
)
1185+
.await
1186+
.unwrap();
1187+
let url = Url::parse("memory:///test/0").unwrap();
1188+
let files = vec![FileMeta {
1189+
location: url,
1190+
last_modified: 0,
1191+
size: 12,
1192+
}];
1193+
let executor = Arc::new(TokioMultiThreadExecutor::new(
1194+
tokio::runtime::Handle::current(),
1195+
));
1196+
let handler = DefaultJsonHandler::new(store, executor)
1197+
.with_parallel_chunks(NonZero::new(4));
1198+
let physical_schema = schema_ref! { nullable "val": INTEGER };
1199+
let token: CancellationTokenRef =
1200+
Arc::new(TestCancellationToken::cancelled());
1201+
let result = handler
1202+
.read_json_files_with_cancellation(&files, physical_schema, None, Some(token));
1203+
assert!(
1204+
matches!(result, Err(Error::Cancelled)),
1205+
"pre-cancelled token must yield Cancelled, not data"
1206+
);
1207+
}
1208+
1209+
#[tokio::test(flavor = "multi_thread")]
1210+
async fn test_read_json_files_parallel_via_builder() {
1211+
let store = Arc::new(InMemory::new());
1212+
for i in 0..100 {
1213+
store
1214+
.put(
1215+
&Path::from(format!("test/{i}")),
1216+
Bytes::from(format!("{{\"val\": {i}}}")).into(),
1217+
)
1218+
.await
1219+
.unwrap();
1220+
}
1221+
let files: Vec<FileMeta> = (0..100)
1222+
.map(|i| {
1223+
let url = Url::parse(&format!("memory:///test/{i}")).unwrap();
1224+
FileMeta {
1225+
location: url,
1226+
last_modified: 0,
1227+
size: 12,
1228+
}
1229+
})
1230+
.collect();
1231+
1232+
let executor = Arc::new(TokioMultiThreadExecutor::new(
1233+
tokio::runtime::Handle::current(),
1234+
));
1235+
let engine = crate::DefaultEngineBuilder::new(store)
1236+
.with_task_executor(executor)
1237+
.with_parallel_chunks(NonZero::new(10))
1238+
.build();
1239+
1240+
let json_handler = engine.json_handler();
1241+
let physical_schema = schema_ref! { nullable "val": INTEGER };
1242+
let data: Vec<RecordBatch> = json_handler
1243+
.read_json_files(&files, physical_schema, None)
1244+
.unwrap()
1245+
.map_ok(into_record_batch)
1246+
.try_collect()
1247+
.unwrap();
1248+
1249+
let all_values: Vec<i32> = data
1250+
.iter()
1251+
.flat_map(|batch| {
1252+
let val_col: &Int32Array = batch.column(0).as_primitive();
1253+
(0..val_col.len()).map(|i| val_col.value(i)).collect_vec()
1254+
})
1255+
.collect();
1256+
assert_eq!(all_values, (0..100).collect_vec());
1257+
}
1258+
11321259
// Helper function to create test data
11331260
fn create_test_data(values: Vec<&str>) -> DeltaResult<Box<dyn EngineData>> {
11341261
let schema = Arc::new(ArrowSchema::new(vec![Field::new(

default-engine/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ mod tests {
537537
.with_task_executor(executor)
538538
.with_buffer_size(NonZero::new(4).unwrap())
539539
.with_batch_size(NonZero::new(8).unwrap())
540+
.with_parallel_chunks(NonZero::new(4))
540541
.build();
541542
test_arrow_engine(&engine, &url);
542543
}

0 commit comments

Comments
 (0)