1use super::{
4 ObjectResidency, PromotionAdmission, PromotionCandidate, PromotionDestination,
5 PromotionMetrics, PromotionSpeculator, PromotionSpeculatorError, PromotionSpeculatorOptions,
6 ResidencyAdmission, ResidencyCandidate, ResidencyHint, ResidencyMetrics, ResidencyPermit,
7 ResidencySpeculator, ResidencySpeculatorError, ResidencySpeculatorOptions, StorageTier,
8};
9use crate::foundation::{GenerationId, OperationId, VolumeId};
10use thiserror::Error;
11
12#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
14pub struct SpeculationOptions {
15 pub residency: ResidencySpeculatorOptions,
17 pub promotion: PromotionSpeculatorOptions,
19}
20
21#[derive(Clone, Debug, Default, Eq, PartialEq)]
23pub struct SpeculationPreemption {
24 pub residency: Vec<OperationId>,
26 pub promotion: Vec<OperationId>,
28}
29
30#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
32pub struct SpeculationMetrics {
33 pub residency: ResidencyMetrics,
35 pub promotion: PromotionMetrics,
37}
38
39#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
42pub enum SpeculationControllerError {
43 #[error(transparent)]
45 Residency(#[from] ResidencySpeculatorError),
46 #[error(transparent)]
48 Promotion(#[from] PromotionSpeculatorError),
49}
50
51#[derive(Clone)]
58pub struct SpeculationController {
59 residency: ResidencySpeculator,
60 promotion: PromotionSpeculator,
61}
62
63impl SpeculationController {
64 pub fn new(
70 options: SpeculationOptions,
71 volume_id: VolumeId,
72 generation_id: GenerationId,
73 ) -> Result<Self, SpeculationControllerError> {
74 let residency = ResidencySpeculator::new(options.residency, volume_id, generation_id)?;
75 let promotion = PromotionSpeculator::new(options.promotion, volume_id, generation_id)?;
76 Ok(Self {
77 residency,
78 promotion,
79 })
80 }
81
82 pub fn observe_hint(
90 &mut self,
91 operation_id: OperationId,
92 volume_id: VolumeId,
93 generation_id: GenerationId,
94 foreground_bytes: u64,
95 hint: ResidencyHint,
96 ) -> Result<ResidencyAdmission, SpeculationControllerError> {
97 let mut residency = self.residency.clone();
98 residency.record_foreground(foreground_bytes)?;
99 let admission = residency.admit(ResidencyCandidate::from_hint(
100 operation_id,
101 volume_id,
102 generation_id,
103 hint,
104 ))?;
105 self.residency = residency;
106 Ok(admission)
107 }
108
109 pub fn plan_promotion(
115 &mut self,
116 permit: ResidencyPermit,
117 accepted_tiers: Vec<StorageTier>,
118 residency: &[ObjectResidency],
119 destinations: &[PromotionDestination],
120 ) -> Result<PromotionAdmission, SpeculationControllerError> {
121 Ok(self.promotion.plan(
122 PromotionCandidate::from_residency(permit, accepted_tiers),
123 residency,
124 destinations,
125 )?)
126 }
127
128 pub fn finish_residency(
134 &mut self,
135 operation_id: OperationId,
136 useful: bool,
137 ) -> Result<(), SpeculationControllerError> {
138 Ok(self.residency.finish(operation_id, useful)?)
139 }
140
141 pub fn finish_promotion(
147 &mut self,
148 operation_id: OperationId,
149 useful: bool,
150 ) -> Result<(), SpeculationControllerError> {
151 Ok(self.promotion.finish(operation_id, useful)?)
152 }
153
154 pub fn preempt_for_foreground(
161 &mut self,
162 foreground_bytes: u64,
163 ) -> Result<SpeculationPreemption, SpeculationControllerError> {
164 let mut residency = self.residency.clone();
165 let mut promotion = self.promotion.clone();
166 let residency_operations = residency.preempt_for_foreground()?;
167 let promotion_operations = promotion.preempt_for_foreground()?;
168 residency.record_foreground(foreground_bytes)?;
169 self.residency = residency;
170 self.promotion = promotion;
171 Ok(SpeculationPreemption {
172 residency: residency_operations,
173 promotion: promotion_operations,
174 })
175 }
176
177 pub fn replace_generation(
183 &mut self,
184 generation_id: GenerationId,
185 ) -> Result<SpeculationPreemption, SpeculationControllerError> {
186 let mut residency = self.residency.clone();
187 let mut promotion = self.promotion.clone();
188 let residency_operations = residency.replace_generation(generation_id)?;
189 let promotion_operations = promotion.replace_generation(generation_id)?;
190 self.residency = residency;
191 self.promotion = promotion;
192 Ok(SpeculationPreemption {
193 residency: residency_operations,
194 promotion: promotion_operations,
195 })
196 }
197
198 #[must_use]
200 pub const fn metrics(&self) -> SpeculationMetrics {
201 SpeculationMetrics {
202 residency: self.residency.metrics(),
203 promotion: self.promotion.metrics(),
204 }
205 }
206
207 #[must_use]
209 pub const fn residency(&self) -> &ResidencySpeculator {
210 &self.residency
211 }
212
213 #[must_use]
215 pub const fn promotion(&self) -> &PromotionSpeculator {
216 &self.promotion
217 }
218
219 #[must_use]
221 pub fn active_residency_permit(&self, operation_id: OperationId) -> Option<ResidencyPermit> {
222 self.residency.active_permit(operation_id)
223 }
224}
225
226#[cfg(test)]
227#[path = "tests/controller.rs"]
228mod tests;