use super::{
ObjectResidency, PromotionAdmission, PromotionCandidate, PromotionDestination,
PromotionMetrics, PromotionSpeculator, PromotionSpeculatorError, PromotionSpeculatorOptions,
ResidencyAdmission, ResidencyCandidate, ResidencyHint, ResidencyMetrics, ResidencyPermit,
ResidencySpeculator, ResidencySpeculatorError, ResidencySpeculatorOptions, StorageTier,
};
use crate::foundation::{GenerationId, OperationId, VolumeId};
use thiserror::Error;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct SpeculationOptions {
pub residency: ResidencySpeculatorOptions,
pub promotion: PromotionSpeculatorOptions,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SpeculationPreemption {
pub residency: Vec<OperationId>,
pub promotion: Vec<OperationId>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct SpeculationMetrics {
pub residency: ResidencyMetrics,
pub promotion: PromotionMetrics,
}
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
pub enum SpeculationControllerError {
#[error(transparent)]
Residency(#[from] ResidencySpeculatorError),
#[error(transparent)]
Promotion(#[from] PromotionSpeculatorError),
}
#[derive(Clone)]
pub struct SpeculationController {
residency: ResidencySpeculator,
promotion: PromotionSpeculator,
}
impl SpeculationController {
pub fn new(
options: SpeculationOptions,
volume_id: VolumeId,
generation_id: GenerationId,
) -> Result<Self, SpeculationControllerError> {
let residency = ResidencySpeculator::new(options.residency, volume_id, generation_id)?;
let promotion = PromotionSpeculator::new(options.promotion, volume_id, generation_id)?;
Ok(Self {
residency,
promotion,
})
}
pub fn observe_hint(
&mut self,
operation_id: OperationId,
volume_id: VolumeId,
generation_id: GenerationId,
foreground_bytes: u64,
hint: ResidencyHint,
) -> Result<ResidencyAdmission, SpeculationControllerError> {
let mut residency = self.residency.clone();
residency.record_foreground(foreground_bytes)?;
let admission = residency.admit(ResidencyCandidate::from_hint(
operation_id,
volume_id,
generation_id,
hint,
))?;
self.residency = residency;
Ok(admission)
}
pub fn plan_promotion(
&mut self,
permit: ResidencyPermit,
accepted_tiers: Vec<StorageTier>,
residency: &[ObjectResidency],
destinations: &[PromotionDestination],
) -> Result<PromotionAdmission, SpeculationControllerError> {
Ok(self.promotion.plan(
PromotionCandidate::from_residency(permit, accepted_tiers),
residency,
destinations,
)?)
}
pub fn finish_residency(
&mut self,
operation_id: OperationId,
useful: bool,
) -> Result<(), SpeculationControllerError> {
Ok(self.residency.finish(operation_id, useful)?)
}
pub fn finish_promotion(
&mut self,
operation_id: OperationId,
useful: bool,
) -> Result<(), SpeculationControllerError> {
Ok(self.promotion.finish(operation_id, useful)?)
}
pub fn preempt_for_foreground(
&mut self,
foreground_bytes: u64,
) -> Result<SpeculationPreemption, SpeculationControllerError> {
let mut residency = self.residency.clone();
let mut promotion = self.promotion.clone();
let residency_operations = residency.preempt_for_foreground()?;
let promotion_operations = promotion.preempt_for_foreground()?;
residency.record_foreground(foreground_bytes)?;
self.residency = residency;
self.promotion = promotion;
Ok(SpeculationPreemption {
residency: residency_operations,
promotion: promotion_operations,
})
}
pub fn replace_generation(
&mut self,
generation_id: GenerationId,
) -> Result<SpeculationPreemption, SpeculationControllerError> {
let mut residency = self.residency.clone();
let mut promotion = self.promotion.clone();
let residency_operations = residency.replace_generation(generation_id)?;
let promotion_operations = promotion.replace_generation(generation_id)?;
self.residency = residency;
self.promotion = promotion;
Ok(SpeculationPreemption {
residency: residency_operations,
promotion: promotion_operations,
})
}
#[must_use]
pub const fn metrics(&self) -> SpeculationMetrics {
SpeculationMetrics {
residency: self.residency.metrics(),
promotion: self.promotion.metrics(),
}
}
#[must_use]
pub const fn residency(&self) -> &ResidencySpeculator {
&self.residency
}
#[must_use]
pub const fn promotion(&self) -> &PromotionSpeculator {
&self.promotion
}
#[must_use]
pub fn active_residency_permit(&self, operation_id: OperationId) -> Option<ResidencyPermit> {
self.residency.active_permit(operation_id)
}
}
#[cfg(test)]
#[path = "tests/controller.rs"]
mod tests;