Skip to main content

ferrum_interfaces/vnext/resource/
dynamic_pool_maintenance.rs

1use super::dynamic_pool::{DynamicDeviceCapacityBlocked, DynamicPoolGrowthIntent};
2use super::{
3    invalid_resource, AdmissionDeferred, CapacityEpochs, CapacityVector, CapacityWaitCondition,
4    DeviceRuntime, DynamicBackingBlocker, DynamicBackingDeferred, DynamicBackingPackingEnvelope,
5    DynamicBackingPoolId, DynamicChunkQuarantineReason, DynamicPoolGrowthBatchReceipt,
6    DynamicPoolGrowthReceipt, DynamicPoolGrowthRequest, DynamicPoolMaintenanceBoundaryReceipt,
7    DynamicPoolSet, DynamicPoolStatus, VNextError,
8};
9use crate::vnext::{
10    CapacityShortfallKind, CapacityWaitSnapshot, DeferredAction, DynamicBackingPressure,
11    DynamicPoolResidentPressure,
12};
13use serde::Serialize;
14use std::collections::BTreeMap;
15use std::sync::Arc;
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
18pub struct DynamicPoolMaintenanceStatus {
19    epochs: CapacityEpochs,
20    maximum_active_sequences: u32,
21    device_capacity_bytes: u64,
22    effective_device_usable_ceiling_bytes: u64,
23    process_claimed_bytes: u64,
24    budget_device_wide_usable_ceiling_bytes: u64,
25    budget_claimed_bytes: u64,
26    pools: Vec<DynamicPoolStatus>,
27}
28
29impl DynamicPoolMaintenanceStatus {
30    pub const fn epochs(&self) -> CapacityEpochs {
31        self.epochs
32    }
33
34    pub const fn maximum_active_sequences(&self) -> u32 {
35        self.maximum_active_sequences
36    }
37
38    pub const fn device_capacity_bytes(&self) -> u64 {
39        self.device_capacity_bytes
40    }
41
42    pub const fn effective_device_usable_ceiling_bytes(&self) -> u64 {
43        self.effective_device_usable_ceiling_bytes
44    }
45
46    pub const fn process_claimed_bytes(&self) -> u64 {
47        self.process_claimed_bytes
48    }
49
50    pub const fn budget_device_wide_usable_ceiling_bytes(&self) -> u64 {
51        self.budget_device_wide_usable_ceiling_bytes
52    }
53
54    pub const fn budget_claimed_bytes(&self) -> u64 {
55        self.budget_claimed_bytes
56    }
57
58    pub fn pools(&self) -> &[DynamicPoolStatus] {
59        &self.pools
60    }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
64pub enum DynamicDeferredMaintenanceOutcome {
65    RetryAdmission {
66        current_epochs: CapacityEpochs,
67    },
68    WaitForRelease {
69        current_epochs: CapacityEpochs,
70        wait_condition: CapacityWaitCondition,
71        pressure: DynamicBackingPressure,
72        #[serde(skip_serializing_if = "Option::is_none")]
73        maintenance_boundary: Option<DynamicPoolMaintenanceBoundaryReceipt>,
74    },
75    Maintained(DynamicPoolGrowthBatchReceipt),
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
79pub struct DynamicPoolQuarantineRelease {
80    pool_id: DynamicBackingPoolId,
81    released_chunks: usize,
82    released_bytes: u64,
83}
84
85impl DynamicPoolQuarantineRelease {
86    pub fn pool_id(&self) -> &DynamicBackingPoolId {
87        &self.pool_id
88    }
89
90    pub const fn released_chunks(&self) -> usize {
91        self.released_chunks
92    }
93
94    pub const fn released_bytes(&self) -> u64 {
95        self.released_bytes
96    }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
100pub struct DynamicPoolQuarantineReleaseReceipt {
101    pools: Vec<DynamicPoolQuarantineRelease>,
102    released_chunks: usize,
103    released_bytes: u64,
104}
105
106impl DynamicPoolQuarantineReleaseReceipt {
107    pub fn pools(&self) -> &[DynamicPoolQuarantineRelease] {
108        &self.pools
109    }
110
111    pub const fn released_chunks(&self) -> usize {
112        self.released_chunks
113    }
114
115    pub const fn released_bytes(&self) -> u64 {
116        self.released_bytes
117    }
118}
119
120/// Plan-owner capability for changing physical dynamic-pool residency. It is
121/// created once during provisioning, is intentionally not `Clone`, and cannot
122/// be derived from any request, sequence, step, invocation, or static lease.
123///
124/// ```compile_fail
125/// use ferrum_interfaces::vnext::{AdmittedRequestResources, DeviceRuntime};
126/// fn request_cannot_mint<R: DeviceRuntime>(request: &AdmittedRequestResources<R>) {
127///     let _ = request.dynamic_pool_maintenance_controller();
128/// }
129/// ```
130///
131/// ```compile_fail
132/// use ferrum_interfaces::vnext::{DeviceRuntime, StaticProvisioningLease};
133/// fn lease_cannot_mint<R: DeviceRuntime>(lease: &StaticProvisioningLease<R>) {
134///     let _ = lease.dynamic_pool_maintenance_controller();
135/// }
136/// ```
137#[must_use = "dynamic pool maintenance controller must be retained by the plan owner"]
138pub struct DynamicPoolMaintenanceController<R>
139where
140    R: DeviceRuntime,
141{
142    pools: Arc<DynamicPoolSet<R>>,
143}
144
145impl<R> DynamicPoolMaintenanceController<R>
146where
147    R: DeviceRuntime,
148{
149    pub(in crate::vnext::resource) fn new(pools: Arc<DynamicPoolSet<R>>) -> Self {
150        Self { pools }
151    }
152
153    pub fn pool_ids(&self) -> impl ExactSizeIterator<Item = &DynamicBackingPoolId> {
154        self.pools.pools.keys()
155    }
156
157    /// Returns one exact domain-to-pool and physical-capacity snapshot. The
158    /// process-wide usable ceiling is the conservative minimum across all live
159    /// plan budgets; plan claims remain separately visible.
160    pub fn status(&self) -> Result<DynamicPoolMaintenanceStatus, VNextError> {
161        let mut pools = Vec::with_capacity(self.pools.pools.len());
162        for pool in self.pools.pools.values() {
163            let mut state = pool
164                .state
165                .lock()
166                .map_err(|_| invalid_resource("dynamic backing pool is poisoned"))?;
167            let live_segments = state.chunks.values().try_fold(0_u64, |total, chunk| {
168                total
169                    .checked_add(chunk.live_segments)
170                    .ok_or_else(|| invalid_resource("dynamic live segment count overflows u64"))
171            })?;
172            let quarantined_bytes = state.quarantined.iter().try_fold(0_u64, |total, chunk| {
173                total
174                    .checked_add(chunk.backing._grant.bytes())
175                    .ok_or_else(|| invalid_resource("dynamic quarantine bytes overflow u64"))
176            })?;
177            let live_occupancy = state.live_occupancy;
178            let used_bytes = state
179                .resident_bytes
180                .checked_sub(state.allocator.free_bytes)
181                .ok_or_else(|| invalid_resource("dynamic pool free bytes exceed residency"))?;
182            if live_occupancy.total().physical_bytes() != used_bytes
183                || live_occupancy.total().segment_count() != live_segments
184            {
185                state.poisoned = true;
186                return Err(invalid_resource(
187                    "dynamic pool live-claim ledger differs from allocator occupancy",
188                ));
189            }
190            pools.push(DynamicPoolStatus {
191                pool_id: pool.domain.pool_id().clone(),
192                domain_id: pool.domain.domain_id,
193                contract: super::DynamicPoolContractStatus::from_domain(&pool.domain),
194                storage_profile: pool.domain.pool.compatibility().profile(),
195                resident_bytes: state.resident_bytes,
196                pending_growth_bytes: state.pending_growth_bytes,
197                free_bytes: state.allocator.free_bytes,
198                largest_contiguous_bytes: state.allocator.largest_contiguous_bytes(),
199                resident_chunks: state.chunks.len(),
200                live_segments,
201                live_occupancy,
202                quarantined_chunks: state.quarantined.len(),
203                quarantined_bytes,
204                descriptor_mismatch_chunks: state
205                    .quarantined
206                    .iter()
207                    .filter(|chunk| {
208                        chunk.reason == DynamicChunkQuarantineReason::DescriptorMismatch
209                    })
210                    .count(),
211                publication_rejected_chunks: state
212                    .quarantined
213                    .iter()
214                    .filter(|chunk| {
215                        chunk.reason == DynamicChunkQuarantineReason::PublicationRejected
216                    })
217                    .count(),
218                poisoned: state.poisoned,
219            });
220        }
221        let account = &self.pools.budget.account;
222        let state = account
223            .state
224            .lock()
225            .map_err(|_| invalid_resource("device capacity account is poisoned"))?;
226        let effective_device_usable_ceiling_bytes = state
227            .budgets
228            .values()
229            .map(|budget| budget.device_wide_usable_ceiling_bytes)
230            .min()
231            .ok_or_else(|| invalid_resource("device capacity account has no live budget"))?;
232        let budget_claimed_bytes = state
233            .budgets
234            .get(&self.pools.budget.budget_id)
235            .ok_or_else(|| invalid_resource("dynamic pool plan budget is stale"))?
236            .claimed_bytes;
237        Ok(DynamicPoolMaintenanceStatus {
238            epochs: self.pools.logical_admission.epochs()?,
239            maximum_active_sequences: self.pools.maximum_active_sequences(),
240            device_capacity_bytes: account.device_capacity_bytes,
241            effective_device_usable_ceiling_bytes,
242            process_claimed_bytes: state.claimed_bytes,
243            budget_device_wide_usable_ceiling_bytes: self
244                .pools
245                .budget
246                .device_wide_usable_ceiling_bytes,
247            budget_claimed_bytes,
248            pools,
249        })
250    }
251
252    /// Makes the core-derived runnable minimum resident. A second call is a
253    /// no-op and returns `None`; it never creates a duplicate initial chunk.
254    pub fn initialize_pool(
255        &self,
256        pool_id: &DynamicBackingPoolId,
257    ) -> Result<Option<DynamicPoolGrowthReceipt>, VNextError> {
258        let mut receipt = self
259            .pools
260            .maintain_pools(vec![DynamicPoolGrowthIntent::Minimum(pool_id.clone())])?;
261        Ok(receipt.growths.pop())
262    }
263
264    /// Initializes several pools in one canonical all-or-nothing publication.
265    pub fn initialize_pools(
266        &self,
267        pool_ids: &[DynamicBackingPoolId],
268    ) -> Result<DynamicPoolGrowthBatchReceipt, VNextError> {
269        self.pools.maintain_pools(
270            pool_ids
271                .iter()
272                .cloned()
273                .map(DynamicPoolGrowthIntent::Minimum)
274                .collect(),
275        )
276    }
277
278    /// Adds one explicitly-sized resident chunk. The physical allocation is
279    /// globally reserved before the runtime is called and published only after
280    /// allocation, descriptor validation, and installation all succeed.
281    pub fn grow_pool(
282        &self,
283        pool_id: &DynamicBackingPoolId,
284        requested_bytes: u64,
285    ) -> Result<DynamicPoolGrowthReceipt, VNextError> {
286        let request = DynamicPoolGrowthRequest::new(pool_id.clone(), requested_bytes)?;
287        let mut receipt = self.grow_pools(vec![request])?;
288        receipt
289            .growths
290            .pop()
291            .ok_or_else(|| invalid_resource("single-pool growth produced no receipt"))
292    }
293
294    /// Grows distinct pools under one global reservation and one capacity
295    /// epoch publication. Input order is ignored; duplicate pools are rejected.
296    pub fn grow_pools(
297        &self,
298        requests: Vec<DynamicPoolGrowthRequest>,
299    ) -> Result<DynamicPoolGrowthBatchReceipt, VNextError> {
300        self.pools.maintain_pools(
301            requests
302                .into_iter()
303                .map(DynamicPoolGrowthIntent::Additional)
304                .collect(),
305        )
306    }
307
308    fn wait_snapshot_for_pool_ids<'a>(
309        &self,
310        pool_ids: impl IntoIterator<Item = &'a DynamicBackingPoolId>,
311    ) -> Result<CapacityWaitSnapshot, VNextError> {
312        self.pools.logical_admission.wait_snapshot_for_domains(
313            pool_ids
314                .into_iter()
315                .map(|pool_id| {
316                    self.pools
317                        .pools
318                        .get(pool_id)
319                        .map(|pool| pool.domain.domain_id)
320                        .ok_or_else(|| {
321                            invalid_resource("dynamic maintenance references an unknown pool")
322                        })
323                })
324                .collect::<Result<Vec<_>, _>>()?,
325        )
326    }
327
328    fn capacity_wait_outcome(
329        &self,
330        logical_snapshot: CapacityWaitSnapshot,
331        blocked: DynamicDeviceCapacityBlocked,
332        maintenance_boundary: DynamicPoolMaintenanceBoundaryReceipt,
333    ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
334        if maintenance_boundary.pressure() != &blocked.pressure
335            || maintenance_boundary.plan_device_capacity_epoch()
336                != blocked.availability.plan_epoch()
337            || maintenance_boundary.process_device_capacity_epoch()
338                != blocked.availability.process_epoch()
339        {
340            return Err(invalid_resource(
341                "dynamic maintenance boundary differs from its capacity failure",
342            ));
343        }
344        let logical_snapshot = logical_snapshot.narrow_to_domains(blocked.planned_domains)?;
345        let mut observed = logical_snapshot.wait_condition().observed().to_vec();
346        observed.push(blocked.availability.epoch_for_pressure(&blocked.pressure));
347        let wait_condition = CapacityWaitCondition::new(
348            logical_snapshot.wait_condition().coordinator_id(),
349            observed,
350        )?;
351        Ok(DynamicDeferredMaintenanceOutcome::WaitForRelease {
352            current_epochs: logical_snapshot.epochs(),
353            wait_condition,
354            pressure: blocked.pressure.into(),
355            maintenance_boundary: Some(maintenance_boundary),
356        })
357    }
358
359    fn pool_resident_wait_outcome(
360        &self,
361        logical_snapshot: CapacityWaitSnapshot,
362        pressure: DynamicPoolResidentPressure,
363    ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
364        let domain = self
365            .pools
366            .pools
367            .get(pressure.pool_id())
368            .map(|pool| pool.domain.domain_id)
369            .ok_or_else(|| {
370                invalid_resource("dynamic pool resident pressure references an unknown pool")
371            })?;
372        let logical_snapshot = logical_snapshot.narrow_to_domains(vec![domain])?;
373        Ok(DynamicDeferredMaintenanceOutcome::WaitForRelease {
374            current_epochs: logical_snapshot.epochs(),
375            wait_condition: logical_snapshot.wait_condition().clone(),
376            pressure: pressure.into(),
377            maintenance_boundary: None,
378        })
379    }
380
381    fn maintain_deferred_pools(
382        &self,
383        intents: Vec<DynamicPoolGrowthIntent>,
384        capacity_blocked: &mut Option<DynamicDeviceCapacityBlocked>,
385        maintenance_boundary: &mut Option<DynamicPoolMaintenanceBoundaryReceipt>,
386        protected_immediate: &CapacityVector,
387        protected_packing_envelopes: &[DynamicBackingPackingEnvelope],
388    ) -> Result<DynamicPoolGrowthBatchReceipt, VNextError> {
389        let retry_intents = intents.clone();
390        match self
391            .pools
392            .maintain_pools_observed(intents, capacity_blocked)
393        {
394            Err(VNextError::DeviceCapacityUnavailable(pressure)) => {
395                let planned_domains = capacity_blocked
396                    .as_ref()
397                    .expect("typed capacity failure retains its exact observation")
398                    .planned_domains
399                    .clone();
400                let blocked = capacity_blocked
401                    .as_ref()
402                    .expect("typed capacity failure retains its exact observation");
403                let attempt = self.pools.reclaim_idle_chunks_for_pressure(
404                    &pressure,
405                    blocked.availability,
406                    &planned_domains,
407                    protected_immediate,
408                    protected_packing_envelopes,
409                )?;
410                *maintenance_boundary = Some(attempt.boundary);
411                let Some(rebalance) = attempt.rebalance else {
412                    return Err(VNextError::DeviceCapacityUnavailable(pressure));
413                };
414                *capacity_blocked = None;
415                let mut receipt = self
416                    .pools
417                    .maintain_pools_observed(retry_intents, capacity_blocked)?;
418                receipt.rebalance = Some(rebalance);
419                receipt.maintenance_boundary = maintenance_boundary.clone();
420                Ok(receipt)
421            }
422            outcome => outcome,
423        }
424    }
425
426    /// Revalidates a physical deferral while its exact typed owner remains
427    /// live. Unrelated capacity epochs may advance between admission and this
428    /// bounded maintenance attempt, so growth is recomputed from current pool
429    /// state instead of trusting the stale byte snapshot.
430    pub(super) fn maintain_for_live_deferred(
431        &self,
432        deferred: &DynamicBackingDeferred,
433    ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
434        let coordinator_id = self.pools.logical_admission.id();
435        if coordinator_id != deferred.epochs().coordinator_id()
436            || coordinator_id != deferred.wait_condition().coordinator_id()
437        {
438            return Err(invalid_resource(
439                "dynamic backing deferral belongs to another admission coordinator",
440            ));
441        }
442        if deferred.blockers().is_empty() {
443            return Err(invalid_resource(
444                "dynamic backing deferral contains no blocking pool",
445            ));
446        }
447        let logical_snapshot = self.wait_snapshot_for_pool_ids(
448            deferred
449                .blockers()
450                .iter()
451                .map(DynamicBackingBlocker::pool_id),
452        )?;
453        let mut capacity_blocked = None;
454        let mut maintenance_boundary = None;
455        let growth = self.maintain_deferred_pools(
456            deferred
457                .blockers()
458                .iter()
459                .cloned()
460                .map(DynamicPoolGrowthIntent::RevalidatedDeferral)
461                .collect(),
462            &mut capacity_blocked,
463            &mut maintenance_boundary,
464            deferred.protected_immediate(),
465            deferred.protected_packing_envelopes(),
466        );
467        match growth {
468            Ok(receipt) if receipt.growths().is_empty() => {
469                let current_epochs = self.pools.logical_admission.epochs()?;
470                if current_epochs == deferred.epochs() {
471                    return Err(invalid_resource(
472                        "dynamic backing maintenance made no progress on an unchanged deferral",
473                    ));
474                }
475                Ok(DynamicDeferredMaintenanceOutcome::RetryAdmission { current_epochs })
476            }
477            Ok(receipt) => Ok(DynamicDeferredMaintenanceOutcome::Maintained(receipt)),
478            Err(VNextError::DeviceCapacityUnavailable(_)) => self.capacity_wait_outcome(
479                logical_snapshot,
480                capacity_blocked.expect("typed capacity failure retains its exact observation"),
481                maintenance_boundary
482                    .expect("typed capacity rebalance failure retains its maintenance boundary"),
483            ),
484            Err(VNextError::DynamicPoolResidentUnavailable(pressure)) => {
485                self.pool_resident_wait_outcome(logical_snapshot, pressure)
486            }
487            Err(error) => Err(error),
488        }
489    }
490
491    /// Materializes fit capacity that logical admission identified as
492    /// growable but that immediate backing preparation does not yet claim.
493    pub fn maintain_for_admission_deferred(
494        &self,
495        deferred: &AdmissionDeferred,
496    ) -> Result<DynamicDeferredMaintenanceOutcome, VNextError> {
497        let coordinator_id = self.pools.logical_admission.id();
498        if coordinator_id != deferred.epochs().coordinator_id()
499            || coordinator_id != deferred.wait_condition().coordinator_id()
500        {
501            return Err(invalid_resource(
502                "logical admission deferral belongs to another coordinator",
503            ));
504        }
505        if deferred.action() != DeferredAction::AwaitBackingGrowth {
506            return Err(invalid_resource(
507                "logical admission deferral does not request backing growth",
508            ));
509        }
510        let pools_by_domain = self
511            .pools
512            .pools
513            .values()
514            .map(|pool| (pool.domain.domain_id, pool.domain.pool_id().clone()))
515            .collect::<BTreeMap<_, _>>();
516        let current = self.pools.logical_admission.snapshot()?;
517        let mut requested_by_pool = BTreeMap::<DynamicBackingPoolId, u64>::new();
518        for blocker in deferred
519            .blockers()
520            .iter()
521            .filter(|blocker| blocker.kind() == CapacityShortfallKind::BackingGrowthRequired)
522        {
523            let domain = blocker.domain().ok_or_else(|| {
524                invalid_resource("backing-growth blocker contains no capacity domain")
525            })?;
526            let pool_id = pools_by_domain.get(&domain).ok_or_else(|| {
527                invalid_resource("backing-growth blocker references a non-pool domain")
528            })?;
529            let current_total = current
530                .domains()
531                .iter()
532                .find(|snapshot| snapshot.domain() == domain)
533                .ok_or_else(|| {
534                    invalid_resource("backing-growth blocker references an unknown domain")
535                })?
536                .total()
537                .get();
538            let missing = blocker.requested().get().saturating_sub(current_total);
539            if missing == 0 {
540                continue;
541            }
542            requested_by_pool
543                .entry(pool_id.clone())
544                .and_modify(|bytes| *bytes = (*bytes).max(missing))
545                .or_insert(missing);
546        }
547        if requested_by_pool.is_empty() {
548            return Ok(DynamicDeferredMaintenanceOutcome::RetryAdmission {
549                current_epochs: self.pools.logical_admission.epochs()?,
550            });
551        }
552        let requests = requested_by_pool
553            .into_iter()
554            .map(|(pool_id, bytes)| DynamicPoolGrowthRequest::new(pool_id, bytes))
555            .collect::<Result<Vec<_>, _>>()?;
556        let logical_snapshot =
557            self.wait_snapshot_for_pool_ids(requests.iter().map(|request| request.pool_id()))?;
558        let mut capacity_blocked = None;
559        let mut maintenance_boundary = None;
560        let growth = self.maintain_deferred_pools(
561            requests
562                .into_iter()
563                .map(DynamicPoolGrowthIntent::Additional)
564                .collect(),
565            &mut capacity_blocked,
566            &mut maintenance_boundary,
567            deferred.immediate_requested(),
568            &[],
569        );
570        match growth {
571            Ok(receipt) => Ok(DynamicDeferredMaintenanceOutcome::Maintained(receipt)),
572            Err(VNextError::DeviceCapacityUnavailable(_)) => self.capacity_wait_outcome(
573                logical_snapshot,
574                capacity_blocked.expect("typed capacity failure retains its exact observation"),
575                maintenance_boundary
576                    .expect("typed capacity rebalance failure retains its maintenance boundary"),
577            ),
578            Err(VNextError::DynamicPoolResidentUnavailable(pressure)) => {
579                self.pool_resident_wait_outcome(logical_snapshot, pressure)
580            }
581            Err(error) => Err(error),
582        }
583    }
584
585    /// Explicitly releases only unclaimable quarantined chunks. Resident
586    /// chunks and logical totals are unchanged; the returned grants are
587    /// dropped after all pool locks have been released.
588    pub fn release_quarantined_chunks(
589        &self,
590    ) -> Result<DynamicPoolQuarantineReleaseReceipt, VNextError> {
591        let pools = self.pools.pools.values().cloned().collect::<Vec<_>>();
592        let _maintenance = pools
593            .iter()
594            .map(|pool| {
595                pool.maintenance
596                    .lock()
597                    .map_err(|_| invalid_resource("dynamic pool maintenance authority is poisoned"))
598            })
599            .collect::<Result<Vec<_>, _>>()?;
600        let mut states = pools
601            .iter()
602            .map(|pool| {
603                pool.state
604                    .lock()
605                    .map_err(|_| invalid_resource("dynamic backing pool is poisoned"))
606            })
607            .collect::<Result<Vec<_>, _>>()?;
608        let pool_totals = states
609            .iter()
610            .map(|state| {
611                let bytes = state.quarantined.iter().try_fold(0_u64, |total, chunk| {
612                    total
613                        .checked_add(chunk.backing._grant.bytes())
614                        .ok_or_else(|| invalid_resource("released quarantine bytes overflow u64"))
615                })?;
616                Ok((state.quarantined.len(), bytes))
617            })
618            .collect::<Result<Vec<_>, VNextError>>()?;
619        let released_chunks = pool_totals.iter().try_fold(0_usize, |total, (count, _)| {
620            total
621                .checked_add(*count)
622                .ok_or_else(|| invalid_resource("released quarantine count overflows usize"))
623        })?;
624        let released_bytes = pool_totals.iter().try_fold(0_u64, |total, (_, bytes)| {
625            total
626                .checked_add(*bytes)
627                .ok_or_else(|| invalid_resource("released quarantine bytes overflow u64"))
628        })?;
629        let mut released = Vec::with_capacity(pools.len());
630        let mut receipts = Vec::new();
631        for ((pool, state), (pool_chunks, pool_bytes)) in
632            pools.iter().zip(states.iter_mut()).zip(pool_totals)
633        {
634            if pool_chunks == 0 {
635                continue;
636            }
637            let chunks = std::mem::take(&mut state.quarantined);
638            debug_assert_eq!(chunks.len(), pool_chunks);
639            receipts.push(DynamicPoolQuarantineRelease {
640                pool_id: pool.domain.pool_id().clone(),
641                released_chunks: pool_chunks,
642                released_bytes: pool_bytes,
643            });
644            released.push(chunks);
645        }
646        drop(states);
647        drop(released);
648        Ok(DynamicPoolQuarantineReleaseReceipt {
649            pools: receipts,
650            released_chunks,
651            released_bytes,
652        })
653    }
654}