Skip to content

Commit 8e4026b

Browse files
committed
fix(core): limit parallel persistent state parts import
1 parent de20dc4 commit 8e4026b

1 file changed

Lines changed: 61 additions & 14 deletions

File tree

core/src/storage/shard_state/store_state_raw.rs

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use std::collections::VecDeque;
12
use std::fs::File;
23
use std::io::{BufWriter, Read, Seek, Write};
3-
use std::sync::Arc;
4+
use std::sync::{Arc, Mutex};
45

56
use anyhow::{Context, Result};
67
use tycho_storage::fs::TempFileStorage;
@@ -46,24 +47,70 @@ impl StoreStateContext {
4647
self.clear_temp_cells()?;
4748

4849
// import main and parts to temp in parallel
50+
const MAX_PARALLEL_PART_IMPORTS: usize = 4;
51+
let parts_count = parts.len();
52+
let workers_count = std::cmp::min(MAX_PARALLEL_PART_IMPORTS, parts_count);
53+
let part_jobs = Mutex::new(parts.into_iter().collect::<VecDeque<_>>());
4954
let (main, parts) = std::thread::scope(|scope| {
5055
let main_handle = scope.spawn(move || self.import_to_temp(main, true));
51-
let mut part_handles = Vec::with_capacity(parts.len());
52-
for part in parts {
53-
part_handles.push(scope.spawn(move || self.import_to_temp(part, false)));
54-
}
55-
let mut parts = Vec::with_capacity(part_handles.len());
56-
for part_handle in part_handles {
57-
match part_handle.join() {
58-
Ok(part) => parts.push(part?),
59-
Err(_) => anyhow::bail!("persistent state part import thread failed"),
60-
}
56+
57+
// no workers are spawned when there are no parts
58+
let mut workers = Vec::with_capacity(workers_count);
59+
for _ in 0..workers_count {
60+
let jobs = &part_jobs;
61+
workers.push(scope.spawn(move || -> Result<Vec<TempState>> {
62+
let mut results = Vec::new();
63+
loop {
64+
let job = jobs
65+
.lock()
66+
.map_err(|e| {
67+
anyhow::anyhow!(
68+
"persistent state import queue lock poisoned: {e:?}"
69+
)
70+
})?
71+
.pop_front();
72+
let Some(reader) = job else {
73+
break;
74+
};
75+
results.push(self.import_to_temp(reader, false)?);
76+
}
77+
Ok(results)
78+
}));
6179
}
80+
81+
// join main result
6282
let main = match main_handle.join() {
63-
Ok(main) => main?,
64-
Err(_) => anyhow::bail!("persistent state main import thread failed"),
83+
Ok(main) => main,
84+
Err(_) => Err(anyhow::anyhow!(
85+
"persistent state main import thread panicked"
86+
)),
6587
};
66-
Ok::<_, anyhow::Error>((main, parts))
88+
89+
// join parts results
90+
let mut parts = Vec::with_capacity(parts_count);
91+
let mut worker_error = None;
92+
for worker in workers {
93+
let error = match worker.join() {
94+
Ok(Ok(results)) => {
95+
parts.extend(results);
96+
None
97+
}
98+
Ok(Err(e)) => Some(e),
99+
Err(_) => Some(anyhow::anyhow!(
100+
"persistent state import worker thread panicked"
101+
)),
102+
};
103+
if worker_error.is_none() {
104+
worker_error = error;
105+
}
106+
}
107+
108+
// join all workers before throwing any error
109+
if let Some(e) = worker_error {
110+
return Err(e);
111+
}
112+
113+
Ok::<_, anyhow::Error>((main?, parts))
67114
})?;
68115

69116
let mut imported_part_roots = FastHashSet::default();

0 commit comments

Comments
 (0)