use crate::claim_time::ClaimTime;
use crate::parallel_budget::{BudgetRequest, HostResources, worker_budget};
use crate::parallel_runner::{Event, OrderedRun, run_ordered};
use crate::push_retry::{RetryChange, Retrying};
use crate::service_work::{CLAIM_LEASE, Drained, WorkHandlers, common_directory_of, drain_common_records_planned, drain_planned};
use std::path::{Path, PathBuf};
use std::time::Duration;
pub const MEASURED_REPOSITORY_PEAK: u64 = 130_482_176;
pub const REPOSITORY_MEMORY: u64 = MEASURED_REPOSITORY_PEAK.next_power_of_two();
#[derive(Clone, Debug)]
pub struct Assignment {
pub directory: PathBuf,
pub retrying: Retrying,
}
impl Assignment {
pub fn plain(directory: PathBuf) -> Self {
return Self { directory, retrying: Retrying::none() };
}
}
#[derive(Debug, Default)]
pub struct RoundAnswer {
pub waits: Vec<(PathBuf, Duration)>,
pub changes: Vec<RetryChange>,
}
pub struct RoundDrain<'a, H: WorkHandlers + Sync> {
pub directories: &'a [Assignment],
pub handlers: &'a H,
pub now: &'a ClaimTime,
pub host: &'a HostResources,
}
fn lane_key(directory: &Path) -> PathBuf {
return common_directory_of(directory).unwrap_or_else(|| { return directory.to_path_buf(); });
}
pub fn drain_directories<H: WorkHandlers + Sync>(request: &RoundDrain<'_, H>) -> RoundAnswer {
let RoundDrain { directories, handlers, now, host } = *request;
let mut lanes: Vec<Vec<usize>> = Vec::new();
let mut lane_of: std::collections::HashMap<PathBuf, usize> = std::collections::HashMap::new();
for (index, assignment) in directories.iter().enumerate() {
match lane_of.get(&lane_key(assignment.directory.as_path())) {
Some(&lane) => lanes[lane].push(index),
None => {
lane_of.insert(lane_key(assignment.directory.as_path()), lanes.len());
lanes.push(vec![index]);
}
}
}
let workers: usize = worker_budget(&BudgetRequest { items: lanes.len(), per_item_memory: REPOSITORY_MEMORY, ceiling: None, cap: None, host });
let mut answer: RoundAnswer = RoundAnswer::default();
run_ordered(OrderedRun {
items: directories,
lanes: lanes.as_slice(),
workers,
work: &|_index: usize, assignment: &Assignment, _say: &mut dyn FnMut(&str)| {
let own: Drained = drain_planned(assignment.directory.as_path(), handlers, now, &assignment.retrying);
let common: Drained = drain_common_records_planned(assignment.directory.as_path(), handlers, now, CLAIM_LEASE, assignment.retrying.first);
let wait: Option<Duration> = [own.wait, common.wait].into_iter().flatten().min();
return (wait, own.changes.into_iter().chain(common.changes).collect::<Vec<RetryChange>>());
},
on_event: &mut |event: Event<'_, (Option<Duration>, Vec<RetryChange>)>| {
if let Event::End(index, (wait, changes)) = event {
if let Some(lease_wait) = wait {
answer.waits.push((directories[index].directory.clone(), lease_wait));
}
answer.changes.extend(changes);
}
},
});
return answer;
}
#[cfg(test)]
#[path = "service_round_tests.rs"]
mod tests;