AWS SDK

AWS SDK

rev. 7a81c738582b03212f62160d093a63f8547fc0f5 (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/pool/admission/publication.rs

@@ -0,1 +0,1172 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! Cross-cell HTTP/2 generation publication for bounded origins.
           7  +
//!
           8  +
//! A connection cell advertises only its exact accepting generation. Admission
           9  +
//! pairs that identity with the oldest demand in the same eligibility group.
          10  +
//! The requesting cell stores an identity-only route; the connection-owning
          11  +
//! cell retains its sender, driver, socket, and capacity lease.
          12  +
//!
          13  +
//! ```text
          14  +
//! connection-cell report -> advertisement indexed by eligibility group
          15  +
//! group demand + peer advertisement -> publication fence
          16  +
//! connection generation validation -> requesting-cell route visibility
          17  +
//! requesting-cell visibility -> admission acknowledgement
          18  +
//! stale connection or requesting cell -> retry or retire fenced demand
          19  +
//! idle generation + H1-required demand -> exact-generation reclaim
          20  +
//! ```
          21  +
//!
          22  +
//! An accepting generation with no prospective or accepted requests also
          23  +
//! enters an origin-wide reclaim order. H1-required demand may reserve one
          24  +
//! exact idle generation, revalidate it under the connection-cell lock, and
          25  +
//! close it to return bounded capacity. Busy generations never enter that
          26  +
//! order.
          27  +
//!
          28  +
//! The connection-owning and requesting cell locks never nest.
          29  +
//! [`H2PublicationGuard`] owns the admission fence between those scopes and
          30  +
//! submits a terminal acknowledgement on drop. [`H2ReclaimAction`] owns an
          31  +
//! idle-generation reservation and repairs admission's view if the crossing is
          32  +
//! rejected or dropped.
          33  +
          34  +
use super::{
          35  +
    AdmissionAction, DeliveryAckResult, DeliveryId, DemandId, DemandSchedule, IntrusiveLinks,
          36  +
    IntrusiveOrder, OriginAdmission,
          37  +
};
          38  +
use crate::client::pool::cell::h2::{H2GenerationId, H2Route};
          39  +
use crate::client::pool::cell::OriginCell;
          40  +
use crate::client::pool::partition::{EligibilityGroup, PartitionId};
          41  +
use crate::sync::Arc;
          42  +
use std::collections::{BTreeSet, HashMap};
          43  +
use std::ops::Bound::{Excluded, Unbounded};
          44  +
          45  +
/// Complete connection-cell advertisement at one monotonic revision.
          46  +
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
          47  +
pub(in crate::client::pool) struct H2AdvertisementSnapshot {
          48  +
    /// Connection-cell revision used to reject stale reports.
          49  +
    revision: u64,
          50  +
    /// Exact accepting generation, or `None` after withdrawal.
          51  +
    generation: Option<H2GenerationId>,
          52  +
    /// Whether the accepting generation has no prospective or accepted requests.
          53  +
    idle: bool,
          54  +
}
          55  +
          56  +
impl H2AdvertisementSnapshot {
          57  +
    /// Reports one accepting generation.
          58  +
    pub(in crate::client::pool) fn accepting(revision: u64, generation: H2GenerationId) -> Self {
          59  +
        Self {
          60  +
            revision,
          61  +
            generation: Some(generation),
          62  +
            idle: false,
          63  +
        }
          64  +
    }
          65  +
          66  +
    /// Reports one accepting generation with no request work.
          67  +
    pub(in crate::client::pool) fn idle(revision: u64, generation: H2GenerationId) -> Self {
          68  +
        Self {
          69  +
            revision,
          70  +
            generation: Some(generation),
          71  +
            idle: true,
          72  +
        }
          73  +
    }
          74  +
          75  +
    /// Reports that the connection cell has no accepting generation.
          76  +
    pub(in crate::client::pool) fn unavailable(revision: u64) -> Self {
          77  +
        Self {
          78  +
            revision,
          79  +
            generation: None,
          80  +
            idle: false,
          81  +
        }
          82  +
    }
          83  +
}
          84  +
          85  +
/// Admission-owned H2 advertisements and publication-ready groups.
          86  +
///
          87  +
/// At every completed transition:
          88  +
///
          89  +
/// - each connection cell has at most one retained advertisement record;
          90  +
/// - each advertised connection cell is linked once in its eligibility-group order;
          91  +
/// - each idle advertised generation is linked once in the origin reclaim
          92  +
///   order unless it owns the active reclaim reservation;
          93  +
/// - each linked identity names the newest reported generation;
          94  +
/// - `ready_groups` contains exactly the groups with H2-compatible queued
          95  +
///   demand and an advertised peer connection;
          96  +
/// - at most one reclaim reservation crosses to a connection cell; and
          97  +
/// - advertisements, prepared publications, and reclaim reservations own no
          98  +
///   connection capacity or protocol sender.
          99  +
#[derive(Debug, Default)]
         100  +
pub(super) struct H2Publication {
         101  +
    /// Latest report retained for each connection cell.
         102  +
    advertisements: HashMap<PartitionId, AdvertisementRecord>,
         103  +
    /// Advertised connection cells ordered within each reuse group.
         104  +
    group_orders: HashMap<EligibilityGroup, IntrusiveOrder<PartitionId>>,
         105  +
    /// Idle accepting generations ordered across the origin for reclaim.
         106  +
    idle_order: IntrusiveOrder<PartitionId>,
         107  +
    /// Groups that have both compatible demand and a peer advertisement.
         108  +
    ready_groups: BTreeSet<EligibilityGroup>,
         109  +
    /// Group selected by the previous publication turn.
         110  +
    last_ready_group: Option<EligibilityGroup>,
         111  +
    /// Exact idle generation currently crossing to its connection cell.
         112  +
    reclaiming: Option<PreparedH2Reclaim>,
         113  +
}
         114  +
         115  +
/// Admission's latest complete report for one connection cell.
         116  +
#[derive(Debug)]
         117  +
struct AdvertisementRecord {
         118  +
    /// Reuse group in which this connection may be published.
         119  +
    group: EligibilityGroup,
         120  +
    /// Newest connection-cell report retained by admission.
         121  +
    revision: u64,
         122  +
    /// Exact accepting generation named by the report.
         123  +
    generation: Option<H2GenerationId>,
         124  +
    /// Intrusive-order residence for an available generation.
         125  +
    residence: AdvertisementResidence,
         126  +
    /// Whether the accepting generation has no request work.
         127  +
    idle: bool,
         128  +
    /// Links in the origin-wide idle-generation order.
         129  +
    idle_links: Option<IntrusiveLinks<PartitionId>>,
         130  +
}
         131  +
         132  +
/// Whether one connection cell occupies its advertisement order.
         133  +
#[derive(Debug, Default)]
         134  +
enum AdvertisementResidence {
         135  +
    /// This connection cell has no generation available to peers.
         136  +
    #[default]
         137  +
    Unavailable,
         138  +
    /// The cell is linked in its eligibility-group advertisement order.
         139  +
    Advertised {
         140  +
        /// Intrusive links owned by this residence.
         141  +
        links: IntrusiveLinks<PartitionId>,
         142  +
    },
         143  +
}
         144  +
         145  +
impl AdvertisementResidence {
         146  +
    fn links(&self) -> Option<&IntrusiveLinks<PartitionId>> {
         147  +
        match self {
         148  +
            Self::Unavailable => None,
         149  +
            Self::Advertised { links } => Some(links),
         150  +
        }
         151  +
    }
         152  +
         153  +
    fn links_mut(&mut self) -> &mut IntrusiveLinks<PartitionId> {
         154  +
        match self {
         155  +
            Self::Unavailable => panic!("unavailable H2 connection had advertisement links"),
         156  +
            Self::Advertised { links } => links,
         157  +
        }
         158  +
    }
         159  +
}
         160  +
         161  +
/// Identity selected before connection and requesting cell validation.
         162  +
#[derive(Clone, Debug)]
         163  +
pub(super) struct PreparedH2Publication {
         164  +
    /// Admission fence identity.
         165  +
    pub(super) delivery: DeliveryId,
         166  +
    /// Cell whose demand is fenced.
         167  +
    pub(super) requesting_partition: PartitionId,
         168  +
    /// Exact demand generation selected for publication.
         169  +
    pub(super) demand: DemandId,
         170  +
    /// Cell that owns the advertised connection.
         171  +
    pub(super) connection_partition: PartitionId,
         172  +
    /// Exact accepting generation selected from the advertisement.
         173  +
    pub(super) generation: H2GenerationId,
         174  +
    /// Reuse group shared by demand and advertisement.
         175  +
    pub(super) group: EligibilityGroup,
         176  +
}
         177  +
         178  +
/// Exact idle generation selected to release bounded origin capacity.
         179  +
#[derive(Clone, Debug, Eq, PartialEq)]
         180  +
pub(super) struct PreparedH2Reclaim {
         181  +
    /// Cell whose H1-required demand caused reclaim.
         182  +
    pub(super) requesting_partition: PartitionId,
         183  +
    /// Exact demand generation that caused reclaim.
         184  +
    pub(super) demand: DemandId,
         185  +
    /// Cell that owns the idle H2 generation.
         186  +
    pub(super) connection_partition: PartitionId,
         187  +
    /// Exact accepting generation observed idle by admission.
         188  +
    pub(super) generation: H2GenerationId,
         189  +
    /// Reuse group retained for advertisement repair.
         190  +
    pub(super) group: EligibilityGroup,
         191  +
}
         192  +
         193  +
/// Candidate selected from stored demand and advertisement heads.
         194  +
struct PublicationCandidate {
         195  +
    /// Requesting cell selected from the group demand head.
         196  +
    requesting_partition: PartitionId,
         197  +
    /// Demand generation at that head.
         198  +
    demand: DemandId,
         199  +
    /// Peer connection cell selected from the advertisement head.
         200  +
    connection_partition: PartitionId,
         201  +
    /// Advertised generation in that connection cell.
         202  +
    generation: H2GenerationId,
         203  +
    /// Eligibility group whose turn was selected.
         204  +
    group: EligibilityGroup,
         205  +
}
         206  +
         207  +
impl H2Publication {
         208  +
    /// Returns whether some eligibility group may start a publication turn.
         209  +
    pub(super) fn has_ready_group(&self) -> bool {
         210  +
        !self.ready_groups.is_empty()
         211  +
    }
         212  +
         213  +
    /// Replaces one connection cell's advertisement and repairs its index.
         214  +
    pub(super) fn update(
         215  +
        &mut self,
         216  +
        connection_partition: PartitionId,
         217  +
        group: EligibilityGroup,
         218  +
        snapshot: H2AdvertisementSnapshot,
         219  +
        demand: &DemandSchedule,
         220  +
    ) {
         221  +
        if self
         222  +
            .advertisements
         223  +
            .get(&connection_partition)
         224  +
            .is_some_and(|record| record.revision >= snapshot.revision)
         225  +
        {
         226  +
            return;
         227  +
        }
         228  +
         229  +
        let old_group = self
         230  +
            .advertisements
         231  +
            .get(&connection_partition)
         232  +
            .map(|record| record.group.clone());
         233  +
        self.unlink(&connection_partition);
         234  +
        let record = self
         235  +
            .advertisements
         236  +
            .entry(connection_partition)
         237  +
            .or_insert_with(|| AdvertisementRecord {
         238  +
                group: group.clone(),
         239  +
                revision: snapshot.revision,
         240  +
                generation: snapshot.generation,
         241  +
                residence: AdvertisementResidence::Unavailable,
         242  +
                idle: snapshot.idle,
         243  +
                idle_links: None,
         244  +
            });
         245  +
        record.group = group.clone();
         246  +
        record.revision = snapshot.revision;
         247  +
        record.generation = snapshot.generation;
         248  +
        record.idle = snapshot.idle;
         249  +
        if record.generation.is_some() {
         250  +
            self.enqueue(connection_partition);
         251  +
        }
         252  +
        self.enqueue_idle_if_available(connection_partition);
         253  +
         254  +
        if let Some(old_group) = old_group {
         255  +
            self.reconcile_group(&old_group, demand);
         256  +
        }
         257  +
        self.reconcile_group(&group, demand);
         258  +
        self.assert_consistent(demand);
         259  +
    }
         260  +
         261  +
    /// Removes one stale generation observed during connection-cell validation.
         262  +
    pub(super) fn remove_if_exact(
         263  +
        &mut self,
         264  +
        connection_partition: &PartitionId,
         265  +
        generation: H2GenerationId,
         266  +
        demand: &DemandSchedule,
         267  +
    ) {
         268  +
        let Some(record) = self.advertisements.get(connection_partition) else {
         269  +
            return;
         270  +
        };
         271  +
        if record.generation != Some(generation) {
         272  +
            return;
         273  +
        }
         274  +
        let group = record.group.clone();
         275  +
        self.unlink(connection_partition);
         276  +
        let record = self
         277  +
            .advertisements
         278  +
            .get_mut(connection_partition)
         279  +
            .expect("validated H2 advertisement disappeared");
         280  +
        record.generation = None;
         281  +
        record.idle = false;
         282  +
        self.reconcile_group(&group, demand);
         283  +
        self.assert_consistent(demand);
         284  +
    }
         285  +
         286  +
    /// Recomputes whether one group can start a publication.
         287  +
    pub(super) fn reconcile_group(&mut self, group: &EligibilityGroup, demand: &DemandSchedule) {
         288  +
        let ready = demand
         289  +
            .queued_group_head(group)
         290  +
            .and_then(|queued| {
         291  +
                if !queued.requirement.accepts_h2() {
         292  +
                    return None;
         293  +
                }
         294  +
                self.first_peer(group, &queued.requesting_partition)
         295  +
                    .map(|_| queued)
         296  +
            })
         297  +
            .is_some();
         298  +
        if ready {
         299  +
            self.ready_groups.insert(group.clone());
         300  +
        } else {
         301  +
            self.ready_groups.remove(group);
         302  +
        }
         303  +
    }
         304  +
         305  +
    /// Selects one group head and one peer advertisement without scanning cells.
         306  +
    fn candidate(&mut self, demand: &DemandSchedule) -> Option<PublicationCandidate> {
         307  +
        let group = self
         308  +
            .last_ready_group
         309  +
            .as_ref()
         310  +
            .and_then(|last| self.ready_groups.range((Excluded(last), Unbounded)).next())
         311  +
            .or_else(|| self.ready_groups.first())?
         312  +
            .clone();
         313  +
        let Some(queued) = demand.queued_group_head(&group) else {
         314  +
            self.ready_groups.remove(&group);
         315  +
            return None;
         316  +
        };
         317  +
        if !queued.requirement.accepts_h2() {
         318  +
            self.ready_groups.remove(&group);
         319  +
            return None;
         320  +
        }
         321  +
        let Some(connection_partition) = self.first_peer(&group, &queued.requesting_partition)
         322  +
        else {
         323  +
            self.ready_groups.remove(&group);
         324  +
            return None;
         325  +
        };
         326  +
        let record = self
         327  +
            .advertisements
         328  +
            .get(&connection_partition)
         329  +
            .expect("selected H2 advertisement disappeared");
         330  +
        let generation = record
         331  +
            .generation
         332  +
            .expect("selected H2 advertisement had no generation");
         333  +
        Some(PublicationCandidate {
         334  +
            requesting_partition: queued.requesting_partition,
         335  +
            demand: queued.demand,
         336  +
            connection_partition,
         337  +
            generation,
         338  +
            group,
         339  +
        })
         340  +
    }
         341  +
         342  +
    /// Reserves one group demand and creates its unlocked publication identity.
         343  +
    pub(super) fn prepare(
         344  +
        &mut self,
         345  +
        demand: &mut DemandSchedule,
         346  +
        delivery: DeliveryId,
         347  +
    ) -> Option<PreparedH2Publication> {
         348  +
        let candidate = self.candidate(demand)?;
         349  +
        demand.reserve_group_head(
         350  +
            &candidate.group,
         351  +
            &candidate.requesting_partition,
         352  +
            candidate.demand,
         353  +
            delivery,
         354  +
        )?;
         355  +
        self.last_ready_group = Some(candidate.group.clone());
         356  +
        self.reconcile_group(&candidate.group, demand);
         357  +
        self.assert_consistent(demand);
         358  +
        Some(PreparedH2Publication {
         359  +
            delivery,
         360  +
            requesting_partition: candidate.requesting_partition,
         361  +
            demand: candidate.demand,
         362  +
            connection_partition: candidate.connection_partition,
         363  +
            generation: candidate.generation,
         364  +
            group: candidate.group,
         365  +
        })
         366  +
    }
         367  +
         368  +
    /// Reserves the oldest idle H2 generation for H1-required origin demand.
         369  +
    pub(super) fn prepare_reclaim(&mut self, demand: &DemandSchedule) -> Option<PreparedH2Reclaim> {
         370  +
        if self.reclaiming.is_some() {
         371  +
            return None;
         372  +
        }
         373  +
        let queued = demand.queued_head()?;
         374  +
        if queued.requirement.accepts_h2() {
         375  +
            return None;
         376  +
        }
         377  +
        let connection_partition = self.idle_order.head()?;
         378  +
        let record = self
         379  +
            .advertisements
         380  +
            .get(&connection_partition)
         381  +
            .expect("idle HTTP/2 advertisement disappeared");
         382  +
        let prepared = PreparedH2Reclaim {
         383  +
            requesting_partition: queued.requesting_partition,
         384  +
            demand: queued.demand,
         385  +
            connection_partition,
         386  +
            generation: record
         387  +
                .generation
         388  +
                .expect("idle HTTP/2 advertisement had no generation"),
         389  +
            group: record.group.clone(),
         390  +
        };
         391  +
        self.reclaiming = Some(prepared.clone());
         392  +
        self.unlink_idle(&connection_partition);
         393  +
        self.assert_consistent(demand);
         394  +
        Some(prepared)
         395  +
    }
         396  +
         397  +
    /// Completes one reclaim crossing and repairs the exact advertisement.
         398  +
    pub(super) fn finish_reclaim(
         399  +
        &mut self,
         400  +
        prepared: &PreparedH2Reclaim,
         401  +
        snapshot: Option<H2AdvertisementSnapshot>,
         402  +
        demand: &DemandSchedule,
         403  +
    ) {
         404  +
        assert_eq!(
         405  +
            self.reclaiming.as_ref(),
         406  +
            Some(prepared),
         407  +
            "HTTP/2 reclaim completion did not match its reservation"
         408  +
        );
         409  +
        self.reclaiming = None;
         410  +
        match snapshot {
         411  +
            Some(snapshot) => {
         412  +
                self.update(
         413  +
                    prepared.connection_partition,
         414  +
                    prepared.group.clone(),
         415  +
                    snapshot,
         416  +
                    demand,
         417  +
                );
         418  +
            }
         419  +
            None => {
         420  +
                self.unlink(&prepared.connection_partition);
         421  +
                self.advertisements.remove(&prepared.connection_partition);
         422  +
                self.reconcile_group(&prepared.group, demand);
         423  +
            }
         424  +
        }
         425  +
        self.enqueue_idle_if_available(prepared.connection_partition);
         426  +
        self.assert_consistent(demand);
         427  +
    }
         428  +
         429  +
    /// Appends one accepting connection cell to its group order.
         430  +
    fn enqueue(&mut self, connection_partition: PartitionId) {
         431  +
        let group = self
         432  +
            .advertisements
         433  +
            .get(&connection_partition)
         434  +
            .expect("enqueued H2 connection disappeared")
         435  +
            .group
         436  +
            .clone();
         437  +
        let links = self
         438  +
            .group_orders
         439  +
            .entry(group)
         440  +
            .or_default()
         441  +
            .push_back(connection_partition);
         442  +
        if let Some(previous) = links.previous {
         443  +
            self.advertisements
         444  +
                .get_mut(&previous)
         445  +
                .expect("previous H2 advertisement disappeared")
         446  +
                .residence
         447  +
                .links_mut()
         448  +
                .next = Some(connection_partition);
         449  +
        }
         450  +
        self.advertisements
         451  +
            .get_mut(&connection_partition)
         452  +
            .expect("enqueued H2 connection disappeared")
         453  +
            .residence = AdvertisementResidence::Advertised { links };
         454  +
    }
         455  +
         456  +
    /// Appends one unreserved idle generation to origin reclaim order.
         457  +
    fn enqueue_idle_if_available(&mut self, connection_partition: PartitionId) {
         458  +
        let reserved = self
         459  +
            .reclaiming
         460  +
            .as_ref()
         461  +
            .is_some_and(|reclaim| reclaim.connection_partition == connection_partition);
         462  +
        let Some(record) = self.advertisements.get(&connection_partition) else {
         463  +
            return;
         464  +
        };
         465  +
        if record.generation.is_none() || !record.idle || reserved || record.idle_links.is_some() {
         466  +
            return;
         467  +
        }
         468  +
        let links = self.idle_order.push_back(connection_partition);
         469  +
        if let Some(previous) = links.previous {
         470  +
            self.advertisements
         471  +
                .get_mut(&previous)
         472  +
                .expect("previous idle HTTP/2 advertisement disappeared")
         473  +
                .idle_links
         474  +
                .as_mut()
         475  +
                .expect("previous idle HTTP/2 advertisement lost its links")
         476  +
                .next = Some(connection_partition);
         477  +
        }
         478  +
        self.advertisements
         479  +
            .get_mut(&connection_partition)
         480  +
            .expect("enqueued idle HTTP/2 advertisement disappeared")
         481  +
            .idle_links = Some(links);
         482  +
    }
         483  +
         484  +
    /// Removes one generation from origin reclaim order.
         485  +
    fn unlink_idle(&mut self, connection_partition: &PartitionId) {
         486  +
        let Some(record) = self.advertisements.get_mut(connection_partition) else {
         487  +
            return;
         488  +
        };
         489  +
        let Some(links) = record.idle_links.take() else {
         490  +
            return;
         491  +
        };
         492  +
        if let Some(previous) = links.previous {
         493  +
            self.advertisements
         494  +
                .get_mut(&previous)
         495  +
                .expect("previous idle HTTP/2 advertisement disappeared")
         496  +
                .idle_links
         497  +
                .as_mut()
         498  +
                .expect("previous idle HTTP/2 advertisement lost its links")
         499  +
                .next = links.next;
         500  +
        }
         501  +
        if let Some(next) = links.next {
         502  +
            self.advertisements
         503  +
                .get_mut(&next)
         504  +
                .expect("next idle HTTP/2 advertisement disappeared")
         505  +
                .idle_links
         506  +
                .as_mut()
         507  +
                .expect("next idle HTTP/2 advertisement lost its links")
         508  +
                .previous = links.previous;
         509  +
        }
         510  +
        self.idle_order.remove(*connection_partition, links);
         511  +
    }
         512  +
         513  +
    /// Removes one connection cell and repairs its group order.
         514  +
    fn unlink(&mut self, connection_partition: &PartitionId) {
         515  +
        self.unlink_idle(connection_partition);
         516  +
        let Some(record) = self.advertisements.get_mut(connection_partition) else {
         517  +
            return;
         518  +
        };
         519  +
        let residence = std::mem::take(&mut record.residence);
         520  +
        let AdvertisementResidence::Advertised { links } = residence else {
         521  +
            return;
         522  +
        };
         523  +
        let group = record.group.clone();
         524  +
        if let Some(previous) = links.previous {
         525  +
            self.advertisements
         526  +
                .get_mut(&previous)
         527  +
                .expect("previous H2 advertisement disappeared")
         528  +
                .residence
         529  +
                .links_mut()
         530  +
                .next = links.next;
         531  +
        }
         532  +
        if let Some(next) = links.next {
         533  +
            self.advertisements
         534  +
                .get_mut(&next)
         535  +
                .expect("next H2 advertisement disappeared")
         536  +
                .residence
         537  +
                .links_mut()
         538  +
                .previous = links.previous;
         539  +
        }
         540  +
        let order = self
         541  +
            .group_orders
         542  +
            .get_mut(&group)
         543  +
            .expect("advertised H2 connection lost its group order");
         544  +
        order.remove(*connection_partition, links);
         545  +
        if order.len() == 0 {
         546  +
            self.group_orders.remove(&group);
         547  +
        }
         548  +
    }
         549  +
         550  +
    /// Returns the group head, or its successor when the requesting cell owns it.
         551  +
    fn first_peer(
         552  +
        &self,
         553  +
        group: &EligibilityGroup,
         554  +
        requesting_partition: &PartitionId,
         555  +
    ) -> Option<PartitionId> {
         556  +
        let head = self.group_orders.get(group)?.head()?;
         557  +
        if head != *requesting_partition {
         558  +
            return Some(head);
         559  +
        }
         560  +
        self.advertisements
         561  +
            .get(&head)
         562  +
            .expect("H2 advertisement head disappeared")
         563  +
            .residence
         564  +
            .links()
         565  +
            .and_then(|links| links.next)
         566  +
    }
         567  +
         568  +
    fn assert_consistent(&self, demand: &DemandSchedule) {
         569  +
        #[cfg(not(any(debug_assertions, test)))]
         570  +
        let _ = demand;
         571  +
         572  +
        #[cfg(any(debug_assertions, test))]
         573  +
        {
         574  +
            if std::thread::panicking() {
         575  +
                return;
         576  +
            }
         577  +
            for (partition, record) in &self.advertisements {
         578  +
                assert_eq!(
         579  +
                    record.generation.is_some(),
         580  +
                    record.residence.links().is_some(),
         581  +
                    "H2 advertisement residence did not match connection availability"
         582  +
                );
         583  +
                if record.residence.links().is_some() {
         584  +
                    assert!(
         585  +
                        self.group_orders.contains_key(&record.group),
         586  +
                        "H2 advertisement lost its group order"
         587  +
                    );
         588  +
                }
         589  +
                let reserved = self
         590  +
                    .reclaiming
         591  +
                    .as_ref()
         592  +
                    .is_some_and(|reclaim| reclaim.connection_partition == *partition);
         593  +
                assert_eq!(
         594  +
                    record.generation.is_some() && record.idle && !reserved,
         595  +
                    record.idle_links.is_some(),
         596  +
                    "H2 reclaim residence did not match idle availability"
         597  +
                );
         598  +
                debug_assert_ne!(
         599  +
                    record.idle_links.as_ref().and_then(|links| links.next),
         600  +
                    Some(*partition)
         601  +
                );
         602  +
                debug_assert_ne!(
         603  +
                    record.residence.links().and_then(|links| links.next),
         604  +
                    Some(*partition)
         605  +
                );
         606  +
            }
         607  +
         608  +
            for (group, order) in &self.group_orders {
         609  +
                let expected = self
         610  +
                    .advertisements
         611  +
                    .values()
         612  +
                    .filter(|record| record.group == *group && record.residence.links().is_some())
         613  +
                    .count();
         614  +
                order.assert_consistent(
         615  +
                    expected,
         616  +
                    self.advertisements.len(),
         617  +
                    "HTTP/2 advertisement order",
         618  +
                    |partition| {
         619  +
                        *self
         620  +
                            .advertisements
         621  +
                            .get(&partition)
         622  +
                            .expect("ordered H2 advertisement disappeared")
         623  +
                            .residence
         624  +
                            .links()
         625  +
                            .expect("ordered H2 advertisement lost its links")
         626  +
                    },
         627  +
                );
         628  +
            }
         629  +
         630  +
            let expected_idle = self
         631  +
                .advertisements
         632  +
                .values()
         633  +
                .filter(|record| record.idle_links.is_some())
         634  +
                .count();
         635  +
            self.idle_order.assert_consistent(
         636  +
                expected_idle,
         637  +
                self.advertisements.len(),
         638  +
                "idle HTTP/2 reclaim order",
         639  +
                |partition| {
         640  +
                    self.advertisements
         641  +
                        .get(&partition)
         642  +
                        .expect("ordered idle HTTP/2 advertisement disappeared")
         643  +
                        .idle_links
         644  +
                        .expect("ordered idle HTTP/2 advertisement lost its links")
         645  +
                },
         646  +
            );
         647  +
         648  +
            let expected_ready = self
         649  +
                .group_orders
         650  +
                .keys()
         651  +
                .filter(|group| {
         652  +
                    demand.queued_group_head(group).is_some_and(|queued| {
         653  +
                        queued.requirement.accepts_h2()
         654  +
                            && self
         655  +
                                .first_peer(group, &queued.requesting_partition)
         656  +
                                .is_some()
         657  +
                    })
         658  +
                })
         659  +
                .cloned()
         660  +
                .collect::<BTreeSet<_>>();
         661  +
            assert_eq!(
         662  +
                expected_ready, self.ready_groups,
         663  +
                "HTTP/2 publication-ready groups did not match demand and advertisements"
         664  +
            );
         665  +
        }
         666  +
    }
         667  +
}
         668  +
         669  +
/// Identity-only publication crossing with a terminal admission fallback.
         670  +
pub(in crate::client::pool) struct H2PublicationGuard {
         671  +
    /// Admission owner of the fenced demand and advertisement.
         672  +
    origin: Arc<OriginAdmission>,
         673  +
    /// Identities revalidated across the unlocked cell transitions.
         674  +
    prepared: PreparedH2Publication,
         675  +
    /// Terminal acknowledgement submitted if the crossing unwinds.
         676  +
    on_drop: Option<DeliveryAckResult>,
         677  +
}
         678  +
         679  +
impl H2PublicationGuard {
         680  +
    pub(super) fn new(origin: Arc<OriginAdmission>, prepared: PreparedH2Publication) -> Self {
         681  +
        Self {
         682  +
            origin,
         683  +
            prepared,
         684  +
            on_drop: Some(DeliveryAckResult::RetrySameResidence),
         685  +
        }
         686  +
    }
         687  +
         688  +
    /// Crosses connection and requesting cells, then acknowledges the fence.
         689  +
    ///
         690  +
    /// A missing connection cell or stale generation withdraws that exact
         691  +
    /// advertisement and retries the same demand residence. A missing
         692  +
    /// requesting cell retires the demand. Requesting-cell rejection retries
         693  +
    /// only when the original demand and route installation are still useful.
         694  +
    pub(super) fn publish_once(self) -> Option<AdmissionAction> {
         695  +
        let generation = self.prepared.generation;
         696  +
        let Some(connection_cell) = self.origin.cell(&self.prepared.connection_partition) else {
         697  +
            return self.finish(DeliveryAckResult::RetrySameResidence, Some(generation));
         698  +
        };
         699  +
        if !OriginCell::h2_generation_is_accepting(&connection_cell, self.prepared.generation) {
         700  +
            return self.finish(DeliveryAckResult::RetrySameResidence, Some(generation));
         701  +
        }
         702  +
         703  +
        let Some(requesting_cell) = self.origin.cell(&self.prepared.requesting_partition) else {
         704  +
            return self.finish(DeliveryAckResult::Rejected { successor: None }, None);
         705  +
        };
         706  +
        let route = H2Route::new(&connection_cell, self.prepared.generation);
         707  +
        if !OriginCell::install_h2_route(
         708  +
            &requesting_cell,
         709  +
            route,
         710  +
            &self.prepared.group,
         711  +
            self.prepared.demand,
         712  +
        ) {
         713  +
            return self.finish(DeliveryAckResult::RetrySameResidence, None);
         714  +
        }
         715  +
         716  +
        let next = self.finish(DeliveryAckResult::Accepted { successor: None }, None);
         717  +
        OriginCell::service_h2_waiters(&requesting_cell);
         718  +
        OriginCell::service_peer_h2_waiters(&requesting_cell);
         719  +
        next
         720  +
    }
         721  +
         722  +
    fn finish(
         723  +
        mut self,
         724  +
        result: DeliveryAckResult,
         725  +
        stale_generation: Option<H2GenerationId>,
         726  +
    ) -> Option<AdmissionAction> {
         727  +
        let outcome = match &result {
         728  +
            DeliveryAckResult::Accepted { .. } => "accepted",
         729  +
            DeliveryAckResult::RetrySameResidence => "retry",
         730  +
            DeliveryAckResult::Rejected { .. } => "rejected",
         731  +
        };
         732  +
        // The unlocked cell crossing is complete. Admission owns the fence
         733  +
        // again, so unwinding must not replay a failed acknowledgement.
         734  +
        self.on_drop = None;
         735  +
        let next = OriginAdmission::finish_h2_publication(
         736  +
            &self.origin,
         737  +
            &self.prepared,
         738  +
            stale_generation,
         739  +
            result,
         740  +
        );
         741  +
        self.trace(outcome);
         742  +
        next
         743  +
    }
         744  +
         745  +
    fn trace(&self, outcome: &str) {
         746  +
        tracing::trace!(
         747  +
            request_partition = ?self.prepared.requesting_partition,
         748  +
            connection_partition = ?self.prepared.connection_partition,
         749  +
            origin_scheme = %self.origin.origin().scheme(),
         750  +
            origin_host = self.origin.origin().host(),
         751  +
            origin_port = ?self.origin.origin().port(),
         752  +
            h2_generation = ?self.prepared.generation,
         753  +
            demand = ?self.prepared.demand,
         754  +
            outcome,
         755  +
            "HTTP/2 peer publication completed"
         756  +
        );
         757  +
    }
         758  +
}
         759  +
         760  +
impl Drop for H2PublicationGuard {
         761  +
    fn drop(&mut self) {
         762  +
        let Some(result) = self.on_drop.take() else {
         763  +
            return;
         764  +
        };
         765  +
        let next =
         766  +
            OriginAdmission::finish_h2_publication(&self.origin, &self.prepared, None, result);
         767  +
        self.trace("guard_drop");
         768  +
        OriginAdmission::drive(next);
         769  +
    }
         770  +
}
         771  +
         772  +
/// Exact idle-generation crossing with an admission repair fallback.
         773  +
pub(in crate::client::pool) struct H2ReclaimAction {
         774  +
    /// Admission authority that owns the reclaim reservation.
         775  +
    origin: Arc<OriginAdmission>,
         776  +
    /// Prepared reclaim still owned by this fallback.
         777  +
    prepared: Option<PreparedH2Reclaim>,
         778  +
}
         779  +
         780  +
impl H2ReclaimAction {
         781  +
    /// Creates one unlocked exact-generation reclaim crossing.
         782  +
    pub(super) fn new(origin: Arc<OriginAdmission>, prepared: PreparedH2Reclaim) -> Self {
         783  +
        Self {
         784  +
            origin,
         785  +
            prepared: Some(prepared),
         786  +
        }
         787  +
    }
         788  +
         789  +
    /// Attempts idle reclaim and returns the next bounded-origin action.
         790  +
    pub(super) fn drive_once(mut self) -> Option<AdmissionAction> {
         791  +
        let prepared = self
         792  +
            .prepared
         793  +
            .as_ref()
         794  +
            .expect("HTTP/2 reclaim action consumed more than once")
         795  +
            .clone();
         796  +
        let (snapshot, reclaimed) = match self.origin.cell(&prepared.connection_partition) {
         797  +
            Some(cell) => {
         798  +
                let (snapshot, connection_id) =
         799  +
                    OriginCell::reclaim_idle_h2(&cell, prepared.generation);
         800  +
                (Some(snapshot), connection_id)
         801  +
            }
         802  +
            None => (None, None),
         803  +
        };
         804  +
        self.prepared = None;
         805  +
        let next = OriginAdmission::finish_h2_reclaim(&self.origin, &prepared, snapshot);
         806  +
        tracing::trace!(
         807  +
            connection_id = ?reclaimed,
         808  +
            request_partition = ?prepared.requesting_partition,
         809  +
            connection_partition = ?prepared.connection_partition,
         810  +
            origin_scheme = %self.origin.origin().scheme(),
         811  +
            origin_host = self.origin.origin().host(),
         812  +
            origin_port = ?self.origin.origin().port(),
         813  +
            h2_generation = ?prepared.generation,
         814  +
            demand = ?prepared.demand,
         815  +
            outcome = if reclaimed.is_some() { "reclaimed" } else { "rejected" },
         816  +
            "HTTP/2 idle reclaim completed"
         817  +
        );
         818  +
        next
         819  +
    }
         820  +
}
         821  +
         822  +
impl Drop for H2ReclaimAction {
         823  +
    fn drop(&mut self) {
         824  +
        let Some(prepared) = self.prepared.take() else {
         825  +
            return;
         826  +
        };
         827  +
        let snapshot = self
         828  +
            .origin
         829  +
            .cell(&prepared.connection_partition)
         830  +
            .map(|cell| cell.report_h2_advertisement());
         831  +
        let next = OriginAdmission::finish_h2_reclaim(&self.origin, &prepared, snapshot);
         832  +
        tracing::trace!(
         833  +
            request_partition = ?prepared.requesting_partition,
         834  +
            connection_partition = ?prepared.connection_partition,
         835  +
            origin_scheme = %self.origin.origin().scheme(),
         836  +
            origin_host = self.origin.origin().host(),
         837  +
            origin_port = ?self.origin.origin().port(),
         838  +
            h2_generation = ?prepared.generation,
         839  +
            demand = ?prepared.demand,
         840  +
            outcome = "guard_drop",
         841  +
            "HTTP/2 idle reclaim completed"
         842  +
        );
         843  +
        OriginAdmission::drive(next);
         844  +
    }
         845  +
}
         846  +
#[cfg(all(test, not(smithy_http_client_loom)))]
         847  +
mod tests {
         848  +
    use super::*;
         849  +
    use crate::client::pool::admission::{DemandSnapshot, ProtocolRequirement, SnapshotVersion};
         850  +
         851  +
    fn partition(index: usize) -> PartitionId {
         852  +
        PartitionId::from_index(index)
         853  +
    }
         854  +
         855  +
    fn generation(value: u64) -> H2GenerationId {
         856  +
        H2GenerationId::for_test(value)
         857  +
    }
         858  +
         859  +
    fn demand(id: u64, group: EligibilityGroup) -> DemandSnapshot {
         860  +
        DemandSnapshot::active(
         861  +
            DemandId::from_u64(id),
         862  +
            SnapshotVersion::INITIAL,
         863  +
            ProtocolRequirement::H2Required,
         864  +
            group,
         865  +
        )
         866  +
    }
         867  +
         868  +
    fn h1_demand(id: u64, group: EligibilityGroup) -> DemandSnapshot {
         869  +
        DemandSnapshot::active(
         870  +
            DemandId::from_u64(id),
         871  +
            SnapshotVersion::INITIAL,
         872  +
            ProtocolRequirement::H1Required,
         873  +
            group,
         874  +
        )
         875  +
    }
         876  +
         877  +
    #[test]
         878  +
    fn exact_generation_replacement_rejects_stale_removal() {
         879  +
        let group = EligibilityGroup::Pool;
         880  +
        let connection_partition = partition(1);
         881  +
        let requesting_partition = partition(2);
         882  +
        let first = generation(10);
         883  +
        let second = generation(11);
         884  +
        let mut schedule = DemandSchedule::default();
         885  +
        schedule.publish(requesting_partition, demand(1, group.clone()));
         886  +
        let mut publication = H2Publication::default();
         887  +
        publication.update(
         888  +
            connection_partition,
         889  +
            group.clone(),
         890  +
            H2AdvertisementSnapshot::accepting(1, first),
         891  +
            &schedule,
         892  +
        );
         893  +
        publication.update(
         894  +
            connection_partition,
         895  +
            group.clone(),
         896  +
            H2AdvertisementSnapshot::accepting(2, second),
         897  +
            &schedule,
         898  +
        );
         899  +
         900  +
        publication.remove_if_exact(&connection_partition, first, &schedule);
         901  +
        let prepared = publication
         902  +
            .prepare(&mut schedule, DeliveryId(1))
         903  +
            .expect("newer advertisement should remain publishable");
         904  +
        assert_eq!(second, prepared.generation);
         905  +
        assert_eq!(connection_partition, prepared.connection_partition);
         906  +
        assert_eq!(requesting_partition, prepared.requesting_partition);
         907  +
    }
         908  +
         909  +
    #[test]
         910  +
    fn current_generation_removal_repairs_the_connection_index() {
         911  +
        let group = EligibilityGroup::Pool;
         912  +
        let connection_partition = partition(1);
         913  +
        let requesting_partition = partition(2);
         914  +
        let current = generation(10);
         915  +
        let mut schedule = DemandSchedule::default();
         916  +
        schedule.publish(requesting_partition, demand(1, group.clone()));
         917  +
        let mut publication = H2Publication::default();
         918  +
        publication.update(
         919  +
            connection_partition,
         920  +
            group,
         921  +
            H2AdvertisementSnapshot::accepting(1, current),
         922  +
            &schedule,
         923  +
        );
         924  +
         925  +
        publication.remove_if_exact(&connection_partition, current, &schedule);
         926  +
         927  +
        assert!(!publication.has_ready_group());
         928  +
        assert!(publication.group_orders.is_empty());
         929  +
        assert!(matches!(
         930  +
            publication.advertisements[&connection_partition].residence,
         931  +
            AdvertisementResidence::Unavailable
         932  +
        ));
         933  +
    }
         934  +
         935  +
    #[test]
         936  +
    fn publication_skips_the_requesting_cell_and_selects_a_peer_in_its_group() {
         937  +
        let pool = EligibilityGroup::Pool;
         938  +
        let isolated = EligibilityGroup::Partition(partition(3));
         939  +
        let requesting_partition = partition(1);
         940  +
        let peer = partition(2);
         941  +
        let other_group = partition(3);
         942  +
        let mut schedule = DemandSchedule::default();
         943  +
        schedule.publish(requesting_partition, demand(1, pool.clone()));
         944  +
        schedule.publish(other_group, demand(2, isolated.clone()));
         945  +
        let mut publication = H2Publication::default();
         946  +
        publication.update(
         947  +
            requesting_partition,
         948  +
            pool.clone(),
         949  +
            H2AdvertisementSnapshot::accepting(1, generation(1)),
         950  +
            &schedule,
         951  +
        );
         952  +
        publication.update(
         953  +
            peer,
         954  +
            pool,
         955  +
            H2AdvertisementSnapshot::accepting(1, generation(2)),
         956  +
            &schedule,
         957  +
        );
         958  +
        publication.update(
         959  +
            other_group,
         960  +
            isolated,
         961  +
            H2AdvertisementSnapshot::accepting(1, generation(3)),
         962  +
            &schedule,
         963  +
        );
         964  +
         965  +
        let prepared = publication
         966  +
            .prepare(&mut schedule, DeliveryId(1))
         967  +
            .expect("pool demand should find its peer connection");
         968  +
        assert_eq!(requesting_partition, prepared.requesting_partition);
         969  +
        assert_eq!(peer, prepared.connection_partition);
         970  +
        assert_eq!(generation(2), prepared.generation);
         971  +
    }
         972  +
         973  +
    #[test]
         974  +
    fn older_advertisement_cannot_replace_the_current_generation() {
         975  +
        let group = EligibilityGroup::Pool;
         976  +
        let connection_partition = partition(1);
         977  +
        let requesting_partition = partition(2);
         978  +
        let current = generation(2);
         979  +
        let mut schedule = DemandSchedule::default();
         980  +
        schedule.publish(requesting_partition, demand(1, group.clone()));
         981  +
        let mut publication = H2Publication::default();
         982  +
        publication.update(
         983  +
            connection_partition,
         984  +
            group.clone(),
         985  +
            H2AdvertisementSnapshot::accepting(2, current),
         986  +
            &schedule,
         987  +
        );
         988  +
        publication.update(
         989  +
            connection_partition,
         990  +
            group,
         991  +
            H2AdvertisementSnapshot::accepting(1, generation(1)),
         992  +
            &schedule,
         993  +
        );
         994  +
         995  +
        let prepared = publication
         996  +
            .prepare(&mut schedule, DeliveryId(1))
         997  +
            .expect("current advertisement should remain publishable");
         998  +
        assert_eq!(current, prepared.generation);
         999  +
    }
        1000  +
        1001  +
    #[test]
        1002  +
    fn h1_required_demand_is_not_publication_ready() {
        1003  +
        let group = EligibilityGroup::Pool;
        1004  +
        let requesting_partition = partition(1);
        1005  +
        let connection_partition = partition(2);
        1006  +
        let mut schedule = DemandSchedule::default();
        1007  +
        schedule.publish(
        1008  +
            requesting_partition,
        1009  +
            DemandSnapshot::active(
        1010  +
                DemandId::from_u64(1),
        1011  +
                SnapshotVersion::INITIAL,
        1012  +
                ProtocolRequirement::H1Required,
        1013  +
                group.clone(),
        1014  +
            ),
        1015  +
        );
        1016  +
        let mut publication = H2Publication::default();
        1017  +
        publication.update(
        1018  +
            connection_partition,
        1019  +
            group,
        1020  +
            H2AdvertisementSnapshot::accepting(1, generation(1)),
        1021  +
            &schedule,
        1022  +
        );
        1023  +
        1024  +
        assert!(!publication.has_ready_group());
        1025  +
        assert!(publication.prepare(&mut schedule, DeliveryId(1)).is_none());
        1026  +
    }
        1027  +
        1028  +
    #[test]
        1029  +
    fn publication_turns_rotate_across_ready_groups() {
        1030  +
        let first_group = EligibilityGroup::Partition(partition(10));
        1031  +
        let second_group = EligibilityGroup::Partition(partition(20));
        1032  +
        let first_request = partition(1);
        1033  +
        let first_connection = partition(2);
        1034  +
        let second_request = partition(3);
        1035  +
        let second_connection = partition(4);
        1036  +
        let mut schedule = DemandSchedule::default();
        1037  +
        schedule.publish(first_request, demand(1, first_group.clone()));
        1038  +
        schedule.publish(second_request, demand(2, second_group.clone()));
        1039  +
        let mut publication = H2Publication::default();
        1040  +
        publication.update(
        1041  +
            first_connection,
        1042  +
            first_group.clone(),
        1043  +
            H2AdvertisementSnapshot::accepting(1, generation(1)),
        1044  +
            &schedule,
        1045  +
        );
        1046  +
        publication.update(
        1047  +
            second_connection,
        1048  +
            second_group.clone(),
        1049  +
            H2AdvertisementSnapshot::accepting(1, generation(2)),
        1050  +
            &schedule,
        1051  +
        );
        1052  +
        1053  +
        let first = publication
        1054  +
            .prepare(&mut schedule, DeliveryId(1))
        1055  +
            .expect("first ready group was not selected");
        1056  +
        schedule.finish_delivery(
        1057  +
            first.delivery,
        1058  +
            &first.requesting_partition,
        1059  +
            DeliveryAckResult::RetrySameResidence,
        1060  +
        );
        1061  +
        publication.reconcile_group(&first.group, &schedule);
        1062  +
        1063  +
        let second = publication
        1064  +
            .prepare(&mut schedule, DeliveryId(2))
        1065  +
            .expect("second ready group was not selected");
        1066  +
        assert_ne!(first.group, second.group);
        1067  +
    }
        1068  +
        1069  +
    #[test]
        1070  +
    #[should_panic(
        1071  +
        expected = "HTTP/2 publication-ready groups did not match demand and advertisements"
        1072  +
    )]
        1073  +
    fn consistency_check_rejects_a_missing_ready_group() {
        1074  +
        let group = EligibilityGroup::Pool;
        1075  +
        let connection_partition = partition(1);
        1076  +
        let requesting_partition = partition(2);
        1077  +
        let mut schedule = DemandSchedule::default();
        1078  +
        schedule.publish(requesting_partition, demand(1, group.clone()));
        1079  +
        let mut publication = H2Publication::default();
        1080  +
        publication.update(
        1081  +
            connection_partition,
        1082  +
            group,
        1083  +
            H2AdvertisementSnapshot::accepting(1, generation(1)),
        1084  +
            &schedule,
        1085  +
        );
        1086  +
        1087  +
        publication.ready_groups.clear();
        1088  +
        1089  +
        publication.assert_consistent(&schedule);
        1090  +
    }
        1091  +
        1092  +
    #[test]
        1093  +
    fn only_idle_h2_is_selected_for_h1_required_reclaim() {
        1094  +
        let group = EligibilityGroup::Pool;
        1095  +
        let requesting_partition = partition(1);
        1096  +
        let connection_partition = partition(2);
        1097  +
        let current = generation(10);
        1098  +
        let mut schedule = DemandSchedule::default();
        1099  +
        schedule.publish(requesting_partition, h1_demand(1, group.clone()));
        1100  +
        let mut publication = H2Publication::default();
        1101  +
        publication.update(
        1102  +
            connection_partition,
        1103  +
            group.clone(),
        1104  +
            H2AdvertisementSnapshot::accepting(1, current),
        1105  +
            &schedule,
        1106  +
        );
        1107  +
        1108  +
        assert!(
        1109  +
            publication.prepare_reclaim(&schedule).is_none(),
        1110  +
            "busy HTTP/2 generation entered reclaim order"
        1111  +
        );
        1112  +
        1113  +
        publication.update(
        1114  +
            connection_partition,
        1115  +
            group,
        1116  +
            H2AdvertisementSnapshot::idle(2, current),
        1117  +
            &schedule,
        1118  +
        );
        1119  +
        let prepared = publication
        1120  +
            .prepare_reclaim(&schedule)
        1121  +
            .expect("idle HTTP/2 generation was not selected");
        1122  +
        assert_eq!(requesting_partition, prepared.requesting_partition);
        1123  +
        assert_eq!(connection_partition, prepared.connection_partition);
        1124  +
        assert_eq!(current, prepared.generation);
        1125  +
        assert!(publication.idle_order.head().is_none());
        1126  +
        1127  +
        publication.finish_reclaim(
        1128  +
            &prepared,
        1129  +
            Some(H2AdvertisementSnapshot::idle(2, current)),
        1130  +
            &schedule,
        1131  +
        );
        1132  +
        assert_eq!(Some(connection_partition), publication.idle_order.head());
        1133  +
    }
        1134  +
        1135  +
    #[test]
        1136  +
    fn reclaim_completion_cannot_restore_a_replaced_generation() {
        1137  +
        let group = EligibilityGroup::Pool;
        1138  +
        let requesting_partition = partition(1);
        1139  +
        let connection_partition = partition(2);
        1140  +
        let old = generation(10);
        1141  +
        let replacement = generation(11);
        1142  +
        let mut schedule = DemandSchedule::default();
        1143  +
        schedule.publish(requesting_partition, h1_demand(1, group.clone()));
        1144  +
        let mut publication = H2Publication::default();
        1145  +
        publication.update(
        1146  +
            connection_partition,
        1147  +
            group.clone(),
        1148  +
            H2AdvertisementSnapshot::idle(1, old),
        1149  +
            &schedule,
        1150  +
        );
        1151  +
        let prepared = publication
        1152  +
            .prepare_reclaim(&schedule)
        1153  +
            .expect("idle HTTP/2 generation was not selected");
        1154  +
        1155  +
        publication.update(
        1156  +
            connection_partition,
        1157  +
            group,
        1158  +
            H2AdvertisementSnapshot::idle(2, replacement),
        1159  +
            &schedule,
        1160  +
        );
        1161  +
        publication.finish_reclaim(
        1162  +
            &prepared,
        1163  +
            Some(H2AdvertisementSnapshot::idle(1, old)),
        1164  +
            &schedule,
        1165  +
        );
        1166  +
        1167  +
        let next = publication
        1168  +
            .prepare_reclaim(&schedule)
        1169  +
            .expect("replacement generation was not restored to reclaim order");
        1170  +
        assert_eq!(replacement, next.generation);
        1171  +
    }
        1172  +
}