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/1 reuse for bounded origins.
|
7 - | //!
|
8 - | //! [`H1Reuse`] tracks the latest availability reported by each cell and pairs
|
9 - | //! the oldest origin-wide demand with a different cell's connection. Eligible
|
10 - | //! demand borrows the connection's request sender. Ineligible demand reclaims
|
11 - | //! the connection's capacity instead.
|
12 - | //!
|
13 - | //! Admission owns the operation record. The connection cell retains a
|
14 - | //! local `H1ReuseReservation` serialized with sender return. Values crossing
|
15 - | //! between those lock domains own their fallback: an uninstalled operation is
|
16 - | //! cancelled, and an uncommitted candidate returns its sender before demand
|
17 - | //! becomes schedulable again.
|
18 - | //!
|
19 - | //! Admission advances each operation through these phases:
|
20 - | //!
|
21 - | //! ```text
|
22 - | //! Installing -- reservation installed ----------------------> Installed
|
23 - | //! Installing -- sender extracted ---------------------------> Resolving
|
24 - | //! Installing(cancelled) -- reservation installed -----------> Cancelling
|
25 - | //! Installing(cancelled) -- sender extracted ----------------> Resolving
|
26 - | //! Installing -- reservation rejected or cell expired -------> removed
|
27 - | //! Installed -- sender return intercepted -------------------> Resolving
|
28 - | //! Installed -- demand cancelled ----------------------------> Cancelling
|
29 - | //! Installed -- terminal cell outcome ------------------------> removed
|
30 - | //! Cancelling -- sender return intercepted ------------------> Resolving
|
31 - | //! Cancelling -- reservation cleared -------------------------> removed
|
32 - | //! Resolving -- repeated candidate handoff ------------------> Resolving
|
33 - | //! Resolving -- borrow or reclaim completes ------------------> removed
|
34 - | //! ```
|
35 - | //!
|
36 - | //! A sender extracted during installation takes the `Resolving` path even if
|
37 - | //! cancellation raced with the install acknowledgement. The extracted sender
|
38 - | //! must complete its terminal transition before cancellation can finish.
|
39 - |
|
40 - | use super::{
|
41 - | AdmissionAction, DeliveryAckResult, DeliveryGuard, DeliveryId, DemandId, DemandSchedule,
|
42 - | IntrusiveLinks, IntrusiveOrder, OriginAdmission,
|
43 - | };
|
44 - | use crate::client::pool::cell::h1::{H1Selection, ProvisionalH1};
|
45 - | use crate::client::pool::cell::OriginCell;
|
46 - | use crate::client::pool::partition::{EligibilityGroup, PartitionId};
|
47 - | use crate::sync::Arc;
|
48 - | use aws_smithy_runtime_api::client::connection::ConnectionId;
|
49 - | use std::collections::{HashMap, VecDeque};
|
50 - | use std::fmt;
|
51 - |
|
52 - | /// Identity of one cross-cell HTTP/1 reuse operation.
|
53 - | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
54 - | pub(in crate::client::pool) struct ReuseId(u64);
|
55 - |
|
56 - | impl ReuseId {
|
57 - | /// Creates a deterministic identity for focused transition tests.
|
58 - | #[cfg(test)]
|
59 - | pub(in crate::client::pool) const fn for_test(value: u64) -> Self {
|
60 - | Self(value)
|
61 - | }
|
62 - | }
|
63 - |
|
64 - | /// How an HTTP/1 reuse operation satisfies waiting demand.
|
65 - | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
66 - | pub(in crate::client::pool) enum ReuseMode {
|
67 - | /// Move the selected sender to the requesting cell for dispatch.
|
68 - | Borrow,
|
69 - | /// Close the selected connection and deliver its capacity instead.
|
70 - | Reclaim,
|
71 - | }
|
72 - |
|
73 - | /// Origin-locked HTTP/1 availability order and active reuse operations.
|
74 - | #[derive(Debug, Default)]
|
75 - | pub(super) struct H1Reuse {
|
76 - | /// Latest availability for every cell that has advertised an HTTP/1 record.
|
77 - | connection_partitions: HashMap<PartitionId, H1AvailabilityRecord>,
|
78 - | /// FIFO view across every cell with a connection available for reclaim.
|
79 - | origin_order: IntrusiveOrder<PartitionId>,
|
80 - | /// FIFO views of connections eligible for borrowing.
|
81 - | group_orders: HashMap<EligibilityGroup, IntrusiveOrder<PartitionId>>,
|
82 - | /// Nonterminal operations indexed by their never-reused identity.
|
83 - | operations: HashMap<ReuseId, ReuseOperation>,
|
84 - | /// Requesting cells that already have a reuse operation in progress.
|
85 - | requesting_partitions: HashMap<PartitionId, ReuseId>,
|
86 - | /// Installed operations whose requesting cell became stale.
|
87 - | cancellations: VecDeque<ReuseId>,
|
88 - | /// Next reuse operation identity.
|
89 - | next_reuse: u64,
|
90 - | }
|
91 - |
|
92 - | /// Complete HTTP/1 availability reported by a connection cell.
|
93 - | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
94 - | pub(in crate::client::pool) struct H1Availability {
|
95 - | /// Whether at least one reusable or active HTTP/1 record remains.
|
96 - | pub(in crate::client::pool) advertised: bool,
|
97 - | /// Whether local H1 work temporarily excludes peer operations.
|
98 - | pub(in crate::client::pool) blocked: bool,
|
99 - | }
|
100 - |
|
101 - | /// Versioned HTTP/1 availability crossing from a cell to admission.
|
102 - | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
103 - | pub(in crate::client::pool) struct H1AvailabilitySnapshot {
|
104 - | /// Monotonic revision assigned under the cell lock.
|
105 - | revision: u64,
|
106 - | /// Complete availability at this revision.
|
107 - | availability: H1Availability,
|
108 - | }
|
109 - |
|
110 - | impl H1AvailabilitySnapshot {
|
111 - | /// Creates one complete connection cell report.
|
112 - | pub(in crate::client::pool) fn new(revision: u64, availability: H1Availability) -> Self {
|
113 - | Self {
|
114 - | revision,
|
115 - | availability,
|
116 - | }
|
117 - | }
|
118 - | }
|
119 - |
|
120 - | /// Terminal HTTP/1 availability observed at a connection cell.
|
121 - | pub(in crate::client::pool) enum H1AvailabilityOutcome {
|
122 - | /// The connection cell remains live and reports its complete availability.
|
123 - | Reported {
|
124 - | /// Cell described by `snapshot`.
|
125 - | connection_partition: PartitionId,
|
126 - | /// Complete availability after the terminal transition.
|
127 - | snapshot: H1AvailabilitySnapshot,
|
128 - | },
|
129 - | /// The connection cell disappeared before the crossing completed.
|
130 - | Expired {
|
131 - | /// Cell no longer retained by the partition registry.
|
132 - | connection_partition: PartitionId,
|
133 - | },
|
134 - | }
|
135 - |
|
136 - | impl H1AvailabilityOutcome {
|
137 - | /// Wraps a complete report with the connection cell identity it describes.
|
138 - | pub(in crate::client::pool) fn reported(
|
139 - | connection_partition: PartitionId,
|
140 - | snapshot: H1AvailabilitySnapshot,
|
141 - | ) -> Self {
|
142 - | Self::Reported {
|
143 - | connection_partition,
|
144 - | snapshot,
|
145 - | }
|
146 - | }
|
147 - |
|
148 - | /// Records that a connection cell no longer exists.
|
149 - | pub(in crate::client::pool) fn expired(connection_partition: PartitionId) -> Self {
|
150 - | Self::Expired {
|
151 - | connection_partition,
|
152 - | }
|
153 - | }
|
154 - |
|
155 - | /// Returns the connection cell whose admission view must change.
|
156 - | fn connection_partition(&self) -> &PartitionId {
|
157 - | match self {
|
158 - | Self::Reported {
|
159 - | connection_partition,
|
160 - | ..
|
161 - | }
|
162 - | | Self::Expired {
|
163 - | connection_partition,
|
164 - | } => connection_partition,
|
165 - | }
|
166 - | }
|
167 - | }
|
168 - |
|
169 - | /// Result of installing a reuse reservation at the connection cell.
|
170 - | pub(in crate::client::pool) enum ReuseInstallResult {
|
171 - | /// A future reusable return will satisfy the reuse operation.
|
172 - | Installed,
|
173 - | /// An idle sender was extracted immediately.
|
174 - | Candidate(ReuseCandidate),
|
175 - | /// The connection cell could not retain the reuse operation.
|
176 - | Rejected(H1AvailabilitySnapshot),
|
177 - | }
|
178 - |
|
179 - | /// Admission's current view of one HTTP/1 connection cell.
|
180 - | #[derive(Debug)]
|
181 - | struct H1AvailabilityRecord {
|
182 - | /// Reuse group whose peers may borrow this connection cell's sender.
|
183 - | group: EligibilityGroup,
|
184 - | /// Newest connection cell report accepted by admission.
|
185 - | revision: u64,
|
186 - | /// Whether the cell has an H1 record that can return or be reclaimed.
|
187 - | advertised: bool,
|
188 - | /// Reuse operation currently reserving this cell's connection.
|
189 - | reuse_id: Option<ReuseId>,
|
190 - | /// Whether connection cell-local work temporarily excludes peers.
|
191 - | blocked: bool,
|
192 - | /// Linked scheduling residence while this connection cell is selectable.
|
193 - | residence: H1AvailabilityResidence,
|
194 - | }
|
195 - |
|
196 - | impl H1AvailabilityRecord {
|
197 - | /// Returns whether this connection cell must occupy both scheduling views.
|
198 - | fn is_schedulable(&self) -> bool {
|
199 - | self.advertised && !self.blocked && self.reuse_id.is_none()
|
200 - | }
|
201 - | }
|
202 - |
|
203 - | /// Whether a connection cell is linked in both scheduling views.
|
204 - | #[derive(Debug, Default)]
|
205 - | enum H1AvailabilityResidence {
|
206 - | /// The connection cell is absent from connection cell-selection order.
|
207 - | #[default]
|
208 - | Unavailable,
|
209 - | /// The connection cell is linked once in origin and group order.
|
210 - | Available {
|
211 - | /// Links in origin-wide reclaim order.
|
212 - | origin: IntrusiveLinks<PartitionId>,
|
213 - | /// Links in eligibility-group borrow order.
|
214 - | group: IntrusiveLinks<PartitionId>,
|
215 - | },
|
216 - | }
|
217 - |
|
218 - | impl H1AvailabilityResidence {
|
219 - | /// Returns whether the connection cell is linked in both views.
|
220 - | fn is_available(&self) -> bool {
|
221 - | matches!(self, Self::Available { .. })
|
222 - | }
|
223 - |
|
224 - | /// Returns this residence's links for one scheduling view.
|
225 - | fn links(&self, view: AvailabilityOrderView) -> Option<&IntrusiveLinks<PartitionId>> {
|
226 - | match (self, view) {
|
227 - | (Self::Available { origin, .. }, AvailabilityOrderView::Reclaim) => Some(origin),
|
228 - | (Self::Available { group, .. }, AvailabilityOrderView::Borrow) => Some(group),
|
229 - | (Self::Unavailable, _) => None,
|
230 - | }
|
231 - | }
|
232 - |
|
233 - | /// Returns mutable links while repairing one scheduling view.
|
234 - | fn links_mut(&mut self, view: AvailabilityOrderView) -> &mut IntrusiveLinks<PartitionId> {
|
235 - | match (self, view) {
|
236 - | (Self::Available { origin, .. }, AvailabilityOrderView::Reclaim) => origin,
|
237 - | (Self::Available { group, .. }, AvailabilityOrderView::Borrow) => group,
|
238 - | (Self::Unavailable, _) => {
|
239 - | panic!("unavailable connection cell has no order links")
|
240 - | }
|
241 - | }
|
242 - | }
|
243 - | }
|
244 - |
|
245 - | /// Selects one of the two intrusive connection cell-order link sets.
|
246 - | #[derive(Clone, Copy)]
|
247 - | enum AvailabilityOrderView {
|
248 - | /// Origin-wide connection-reclaim order.
|
249 - | Reclaim,
|
250 - | /// Eligibility-group connection-borrow order.
|
251 - | Borrow,
|
252 - | }
|
253 - |
|
254 - | /// Admission-owned state for one nonterminal reuse operation.
|
255 - | #[derive(Clone, Debug)]
|
256 - | struct ReuseOperation {
|
257 - | /// Cell whose connection is reserved by this operation.
|
258 - | connection_partition: PartitionId,
|
259 - | /// Cell whose demand caused this operation.
|
260 - | requesting_partition: PartitionId,
|
261 - | /// Exact requesting cell demand generation fenced by the reuse operation.
|
262 - | demand: DemandId,
|
263 - | /// Whether resolution borrows a sender or reclaims its capacity.
|
264 - | mode: ReuseMode,
|
265 - | /// Admission-side progress of the reuse operation.
|
266 - | phase: ReusePhase,
|
267 - | /// Whether requesting cell demand became stale before connection-cell
|
268 - | /// resolution.
|
269 - | cancelled: bool,
|
270 - | }
|
271 - |
|
272 - | /// Origin-side progress of one reuse operation.
|
273 - | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
274 - | enum ReusePhase {
|
275 - | /// Admission selected a connection, but the cell has not reserved it.
|
276 - | Installing,
|
277 - | /// The cell will intercept the connection's next reusable return.
|
278 - | Installed,
|
279 - | /// A provisional sender is resolving outside admission.
|
280 - | Resolving,
|
281 - | /// Request cancellation must clear the installed reservation.
|
282 - | Cancelling,
|
283 - | }
|
284 - |
|
285 - | /// Work required to install a reuse reservation outside the admission lock.
|
286 - | #[derive(Debug)]
|
287 - | pub(in crate::client::pool) struct PreparedReuseInstall {
|
288 - | /// Operation being installed.
|
289 - | pub(in crate::client::pool) id: ReuseId,
|
290 - | /// Cell whose connection was selected while admission was locked.
|
291 - | pub(in crate::client::pool) connection_partition: PartitionId,
|
292 - | }
|
293 - |
|
294 - | /// Work required to cancel a reservation outside the admission lock.
|
295 - | pub(super) struct PreparedReuseCancellation {
|
296 - | /// Operation whose reservation must be cleared.
|
297 - | id: ReuseId,
|
298 - | /// Cell that owns the reservation.
|
299 - | connection_partition: PartitionId,
|
300 - | }
|
301 - |
|
302 - | impl H1Reuse {
|
303 - | /// Publishes the connection cell's complete current availability.
|
304 - | pub(super) fn update_availability(
|
305 - | &mut self,
|
306 - | connection_partition: PartitionId,
|
307 - | group: EligibilityGroup,
|
308 - | snapshot: H1AvailabilitySnapshot,
|
309 - | ) {
|
310 - | if self
|
311 - | .connection_partitions
|
312 - | .get(&connection_partition)
|
313 - | .is_some_and(|record| record.revision >= snapshot.revision)
|
314 - | {
|
315 - | return;
|
316 - | }
|
317 - | self.unlink_cell(&connection_partition);
|
318 - | let record = self
|
319 - | .connection_partitions
|
320 - | .entry(connection_partition)
|
321 - | .or_insert_with(|| H1AvailabilityRecord {
|
322 - | group: group.clone(),
|
323 - | revision: snapshot.revision,
|
324 - | advertised: snapshot.availability.advertised,
|
325 - | reuse_id: None,
|
326 - | blocked: snapshot.availability.blocked,
|
327 - | residence: H1AvailabilityResidence::Unavailable,
|
328 - | });
|
329 - | record.group = group;
|
330 - | record.revision = snapshot.revision;
|
331 - | record.advertised = snapshot.availability.advertised;
|
332 - | record.blocked = snapshot.availability.blocked;
|
333 - | self.enqueue_cell_if_available(&connection_partition);
|
334 - | self.assert_consistent();
|
335 - | }
|
336 - |
|
337 - | /// Marks a requesting cell reuse operation stale after demand publication or delivery.
|
338 - | pub(super) fn reconcile_requesting_cell(
|
339 - | &mut self,
|
340 - | requesting_partition: &PartitionId,
|
341 - | schedule: &DemandSchedule,
|
342 - | ) {
|
343 - | let Some(reuse_id) = self
|
344 - | .requesting_partitions
|
345 - | .get(requesting_partition)
|
346 - | .copied()
|
347 - | else {
|
348 - | return;
|
349 - | };
|
350 - | let Some(record) = self.operations.get_mut(&reuse_id) else {
|
351 - | self.requesting_partitions.remove(requesting_partition);
|
352 - | self.assert_consistent();
|
353 - | return;
|
354 - | };
|
355 - | if schedule.is_current_queued(&record.requesting_partition, record.demand) {
|
356 - | return;
|
357 - | }
|
358 - | record.cancelled = true;
|
359 - | if record.phase == ReusePhase::Installed {
|
360 - | record.phase = ReusePhase::Cancelling;
|
361 - | self.cancellations.push_back(reuse_id);
|
362 - | }
|
363 - | self.assert_consistent();
|
364 - | }
|
365 - |
|
366 - | /// Selects another cell's connection for the oldest origin-wide demand.
|
367 - | ///
|
368 - | /// An eligible cell lends its sender. Otherwise, a connection is reclaimed
|
369 - | /// so its capacity can satisfy the same demand. HTTP/1-compatible demand
|
370 - | /// does not select its own cell because local senders are handled under the
|
371 - | /// cell lock. HTTP/2-only demand may reclaim same-cell idle HTTP/1 capacity.
|
372 - | pub(super) fn prepare_reuse(
|
373 - | &mut self,
|
374 - | schedule: &DemandSchedule,
|
375 - | ) -> Option<PreparedReuseInstall> {
|
376 - | let requesting_partition = schedule.queued_head()?;
|
377 - | if self
|
378 - | .requesting_partitions
|
379 - | .contains_key(&requesting_partition.requesting_partition)
|
380 - | {
|
381 - | return None;
|
382 - | }
|
383 - |
|
384 - | let (connection_partition, mode) = if requesting_partition.requirement.accepts_h1() {
|
385 - | match self.take_group_peer(
|
386 - | &requesting_partition.eligibility_group,
|
387 - | &requesting_partition.requesting_partition,
|
388 - | ) {
|
389 - | Some(connection_partition) => (connection_partition, ReuseMode::Borrow),
|
390 - | None => (
|
391 - | self.take_origin_peer(&requesting_partition.requesting_partition)?,
|
392 - | ReuseMode::Reclaim,
|
393 - | ),
|
394 - | }
|
395 - | } else {
|
396 - | (self.take_origin_head()?, ReuseMode::Reclaim)
|
397 - | };
|
398 - |
|
399 - | let id = self.take_reuse_id();
|
400 - | let availability_record = self
|
401 - | .connection_partitions
|
402 - | .get_mut(&connection_partition)
|
403 - | .expect("selected HTTP/1 connection cell disappeared");
|
404 - | debug_assert!(availability_record.reuse_id.is_none());
|
405 - | availability_record.reuse_id = Some(id);
|
406 - | self.requesting_partitions
|
407 - | .insert(requesting_partition.requesting_partition, id);
|
408 - | self.operations.insert(
|
409 - | id,
|
410 - | ReuseOperation {
|
411 - | connection_partition,
|
412 - | requesting_partition: requesting_partition.requesting_partition,
|
413 - | demand: requesting_partition.demand,
|
414 - | mode,
|
415 - | phase: ReusePhase::Installing,
|
416 - | cancelled: false,
|
417 - | },
|
418 - | );
|
419 - | self.assert_consistent();
|
420 - | Some(PreparedReuseInstall {
|
421 - | id,
|
422 - | connection_partition,
|
423 - | })
|
424 - | }
|
425 - |
|
426 - | /// Extracts cancellation work, discarding entries for completed operations.
|
427 - | pub(super) fn prepare_cancellation(&mut self) -> Option<PreparedReuseCancellation> {
|
428 - | while let Some(id) = self.cancellations.pop_front() {
|
429 - | let Some(record) = self.operations.get(&id) else {
|
430 - | continue;
|
431 - | };
|
432 - | if record.phase != ReusePhase::Cancelling {
|
433 - | continue;
|
434 - | }
|
435 - | return Some(PreparedReuseCancellation {
|
436 - | id,
|
437 - | connection_partition: record.connection_partition,
|
438 - | });
|
439 - | }
|
440 - | None
|
441 - | }
|
442 - |
|
443 - | /// Applies an install acknowledgement and returns the retained reuse operation.
|
444 - | fn finish_install(&mut self, id: ReuseId, resolved: bool) -> Option<ReuseOperation> {
|
445 - | let record = self.operations.get_mut(&id)?;
|
446 - | if record.phase != ReusePhase::Installing {
|
447 - | return None;
|
448 - | }
|
449 - | record.phase = if resolved {
|
450 - | ReusePhase::Resolving
|
451 - | } else if record.cancelled {
|
452 - | ReusePhase::Cancelling
|
453 - | } else {
|
454 - | ReusePhase::Installed
|
455 - | };
|
456 - | let record = record.clone();
|
457 - | self.assert_consistent();
|
458 - | Some(record)
|
459 - | }
|
460 - |
|
461 - | /// Moves an installed reuse operation to provisional-candidate resolution.
|
462 - | fn begin_resolution(&mut self, id: ReuseId) -> Option<ReuseOperation> {
|
463 - | let record = self.operations.get_mut(&id)?;
|
464 - | if !matches!(
|
465 - | record.phase,
|
466 - | ReusePhase::Installed | ReusePhase::Resolving | ReusePhase::Cancelling
|
467 - | ) {
|
468 - | return None;
|
469 - | }
|
470 - | record.phase = ReusePhase::Resolving;
|
471 - | let record = record.clone();
|
472 - | self.assert_consistent();
|
473 - | Some(record)
|
474 - | }
|
475 - |
|
476 - | /// Removes one reuse operation and applies the connection cell's terminal report.
|
477 - | ///
|
478 - | /// The outcome names its connection cell explicitly so an expired cell cannot be
|
479 - | /// mistaken for an unchanged connection cell and a duplicate terminal report still
|
480 - | /// refreshes admission's connection cell view.
|
481 - | fn finish_reuse(
|
482 - | &mut self,
|
483 - | id: ReuseId,
|
484 - | outcome: H1AvailabilityOutcome,
|
485 - | ) -> Option<ReuseOperation> {
|
486 - | let outcome_cell = *outcome.connection_partition();
|
487 - | self.apply_h1_availability_outcome(outcome);
|
488 - |
|
489 - | let record = self.operations.remove(&id);
|
490 - | if let Some(record) = record.as_ref() {
|
491 - | debug_assert_eq!(record.connection_partition, outcome_cell);
|
492 - | if self.requesting_partitions.get(&record.requesting_partition) == Some(&id) {
|
493 - | self.requesting_partitions
|
494 - | .remove(&record.requesting_partition);
|
495 - | }
|
496 - | if let Some(connection_partition) = self
|
497 - | .connection_partitions
|
498 - | .get_mut(&record.connection_partition)
|
499 - | {
|
500 - | if connection_partition.reuse_id == Some(id) {
|
501 - | connection_partition.reuse_id = None;
|
502 - | }
|
503 - | }
|
504 - | }
|
505 - |
|
506 - | self.enqueue_cell_if_available(&outcome_cell);
|
507 - | self.assert_consistent();
|
508 - | record
|
509 - | }
|
510 - |
|
511 - | /// Applies a report without assuming its reuse operation record still exists.
|
512 - | fn apply_h1_availability_outcome(&mut self, outcome: H1AvailabilityOutcome) {
|
513 - | match outcome {
|
514 - | H1AvailabilityOutcome::Reported {
|
515 - | connection_partition,
|
516 - | snapshot,
|
517 - | } => {
|
518 - | if self
|
519 - | .connection_partitions
|
520 - | .get(&connection_partition)
|
521 - | .is_some_and(|record| record.revision >= snapshot.revision)
|
522 - | {
|
523 - | return;
|
524 - | }
|
525 - | self.unlink_cell(&connection_partition);
|
526 - | if let Some(record) = self.connection_partitions.get_mut(&connection_partition) {
|
527 - | record.revision = snapshot.revision;
|
528 - | record.advertised = snapshot.availability.advertised;
|
529 - | record.blocked = snapshot.availability.blocked;
|
530 - | }
|
531 - | }
|
532 - | H1AvailabilityOutcome::Expired {
|
533 - | connection_partition,
|
534 - | } => {
|
535 - | self.unlink_cell(&connection_partition);
|
536 - | self.connection_partitions.remove(&connection_partition);
|
537 - | }
|
538 - | }
|
539 - | }
|
540 - |
|
541 - | /// Removes and returns the first eligible peer in one reuse group.
|
542 - | fn take_group_peer(
|
543 - | &mut self,
|
544 - | group: &EligibilityGroup,
|
545 - | requesting_partition: &PartitionId,
|
546 - | ) -> Option<PartitionId> {
|
547 - | let connection_partition = {
|
548 - | let order = self.group_orders.get(group)?;
|
549 - | self.first_peer(order, requesting_partition, AvailabilityOrderView::Borrow)?
|
550 - | };
|
551 - | self.unlink_cell(&connection_partition);
|
552 - | Some(connection_partition)
|
553 - | }
|
554 - |
|
555 - | /// Removes and returns the first origin-wide peer.
|
556 - | fn take_origin_peer(&mut self, requesting_partition: &PartitionId) -> Option<PartitionId> {
|
557 - | let connection_partition = self.first_peer(
|
558 - | &self.origin_order,
|
559 - | requesting_partition,
|
560 - | AvailabilityOrderView::Reclaim,
|
561 - | )?;
|
562 - | self.unlink_cell(&connection_partition);
|
563 - | Some(connection_partition)
|
564 - | }
|
565 - |
|
566 - | /// Removes and returns the oldest origin-wide connection, including local.
|
567 - | fn take_origin_head(&mut self) -> Option<PartitionId> {
|
568 - | let connection_partition = self.origin_order.head()?;
|
569 - | self.unlink_cell(&connection_partition);
|
570 - | Some(connection_partition)
|
571 - | }
|
572 - |
|
573 - | /// Returns the head, or its successor when the head is the requesting cell itself.
|
574 - | fn first_peer(
|
575 - | &self,
|
576 - | order: &IntrusiveOrder<PartitionId>,
|
577 - | requesting_partition: &PartitionId,
|
578 - | view: AvailabilityOrderView,
|
579 - | ) -> Option<PartitionId> {
|
580 - | let head = order.head()?;
|
581 - | if head != *requesting_partition {
|
582 - | return Some(head);
|
583 - | }
|
584 - | let record = self
|
585 - | .connection_partitions
|
586 - | .get(&head)
|
587 - | .expect("connection cell order head disappeared");
|
588 - | record.residence.links(view).and_then(|links| links.next)
|
589 - | }
|
590 - |
|
591 - | /// Appends an available, unreserved cell to both scheduling views.
|
592 - | fn enqueue_cell_if_available(&mut self, connection_partition: &PartitionId) {
|
593 - | let Some(record) = self.connection_partitions.get(connection_partition) else {
|
594 - | return;
|
595 - | };
|
596 - | if !record.is_schedulable() || record.residence.is_available() {
|
597 - | return;
|
598 - | }
|
599 - | let group = record.group.clone();
|
600 - | let origin_links = self.origin_order.push_back(*connection_partition);
|
601 - | let group_links = self
|
602 - | .group_orders
|
603 - | .entry(group.clone())
|
604 - | .or_default()
|
605 - | .push_back(*connection_partition);
|
606 - |
|
607 - | if let Some(previous) = origin_links.previous {
|
608 - | self.connection_partitions
|
609 - | .get_mut(&previous)
|
610 - | .expect("origin connection cell order tail disappeared")
|
611 - | .residence
|
612 - | .links_mut(AvailabilityOrderView::Reclaim)
|
613 - | .next = Some(*connection_partition);
|
614 - | }
|
615 - | if let Some(previous) = group_links.previous {
|
616 - | self.connection_partitions
|
617 - | .get_mut(&previous)
|
618 - | .expect("group connection cell order tail disappeared")
|
619 - | .residence
|
620 - | .links_mut(AvailabilityOrderView::Borrow)
|
621 - | .next = Some(*connection_partition);
|
622 - | }
|
623 - |
|
624 - | self.connection_partitions
|
625 - | .get_mut(connection_partition)
|
626 - | .expect("enqueued connection cell disappeared")
|
627 - | .residence = H1AvailabilityResidence::Available {
|
628 - | origin: origin_links,
|
629 - | group: group_links,
|
630 - | };
|
631 - | }
|
632 - |
|
633 - | /// Unlinks a connection cell eagerly from both scheduling views.
|
634 - | fn unlink_cell(&mut self, connection_partition: &PartitionId) {
|
635 - | let Some(record) = self.connection_partitions.get_mut(connection_partition) else {
|
636 - | return;
|
637 - | };
|
638 - | let residence = std::mem::take(&mut record.residence);
|
639 - | let H1AvailabilityResidence::Available { origin, group } = residence else {
|
640 - | return;
|
641 - | };
|
642 - | let group_key = record.group.clone();
|
643 - |
|
644 - | Self::repair_links(
|
645 - | &mut self.connection_partitions,
|
646 - | connection_partition,
|
647 - | &origin,
|
648 - | AvailabilityOrderView::Reclaim,
|
649 - | );
|
650 - | self.origin_order.remove(*connection_partition, origin);
|
651 - |
|
652 - | Self::repair_links(
|
653 - | &mut self.connection_partitions,
|
654 - | connection_partition,
|
655 - | &group,
|
656 - | AvailabilityOrderView::Borrow,
|
657 - | );
|
658 - | self.group_orders
|
659 - | .get_mut(&group_key)
|
660 - | .expect("available connection cell lost its group order")
|
661 - | .remove(*connection_partition, group);
|
662 - | }
|
663 - |
|
664 - | /// Repairs neighboring links after one connection cell leaves an order.
|
665 - | fn repair_links(
|
666 - | connection_partitions: &mut HashMap<PartitionId, H1AvailabilityRecord>,
|
667 - | connection_partition: &PartitionId,
|
668 - | links: &IntrusiveLinks<PartitionId>,
|
669 - | view: AvailabilityOrderView,
|
670 - | ) {
|
671 - | if let Some(previous) = links.previous {
|
672 - | connection_partitions
|
673 - | .get_mut(&previous)
|
674 - | .expect("previous connection cell disappeared")
|
675 - | .residence
|
676 - | .links_mut(view)
|
677 - | .next = links.next;
|
678 - | }
|
679 - | if let Some(next) = links.next {
|
680 - | connection_partitions
|
681 - | .get_mut(&next)
|
682 - | .expect("next connection cell disappeared")
|
683 - | .residence
|
684 - | .links_mut(view)
|
685 - | .previous = links.previous;
|
686 - | }
|
687 - | debug_assert_ne!(links.previous.as_ref(), Some(connection_partition));
|
688 - | debug_assert_ne!(links.next.as_ref(), Some(connection_partition));
|
689 - | }
|
690 - |
|
691 - | /// Allocates a reuse operation identity.
|
692 - | fn take_reuse_id(&mut self) -> ReuseId {
|
693 - | let value = self.next_reuse;
|
694 - | self.next_reuse = value
|
695 - | .checked_add(1)
|
696 - | .expect("HTTP/1 reuse operation identity exhausted");
|
697 - | ReuseId(value)
|
698 - | }
|
699 - |
|
700 - | /// Checks reuse-operation indexes and both availability orders after every mutation.
|
701 - | fn assert_consistent(&self) {
|
702 - | #[cfg(debug_assertions)]
|
703 - | {
|
704 - | if std::thread::panicking() {
|
705 - | return;
|
706 - | }
|
707 - | self.assert_consistent_debug();
|
708 - | }
|
709 - | }
|
710 - |
|
711 - | #[cfg(debug_assertions)]
|
712 - | fn assert_consistent_debug(&self) {
|
713 - | for (id, operation) in &self.operations {
|
714 - | assert_eq!(
|
715 - | self.requesting_partitions
|
716 - | .get(&operation.requesting_partition),
|
717 - | Some(id),
|
718 - | "reuse operation's requesting-cell index did not name the operation"
|
719 - | );
|
720 - | assert_eq!(
|
721 - | self.connection_partitions
|
722 - | .get(&operation.connection_partition)
|
723 - | .and_then(|connection_partition| connection_partition.reuse_id),
|
724 - | Some(*id),
|
725 - | "reuse operation's connection-cell index did not name the operation"
|
726 - | );
|
727 - | }
|
728 - | for (requesting_partition, id) in &self.requesting_partitions {
|
729 - | assert_eq!(
|
730 - | self.operations
|
731 - | .get(id)
|
732 - | .map(|operation| &operation.requesting_partition),
|
733 - | Some(requesting_partition),
|
734 - | "requesting cell index named a missing reuse operation"
|
735 - | );
|
736 - | }
|
737 - | for (connection_partition, record) in &self.connection_partitions {
|
738 - | if let Some(id) = record.reuse_id {
|
739 - | assert_eq!(
|
740 - | self.operations
|
741 - | .get(&id)
|
742 - | .map(|operation| &operation.connection_partition),
|
743 - | Some(connection_partition),
|
744 - | "connection cell index named a missing reuse operation"
|
745 - | );
|
746 - | }
|
747 - | assert_eq!(
|
748 - | record.residence.is_available(),
|
749 - | record.is_schedulable(),
|
750 - | "connection cell scheduling residence did not match availability"
|
751 - | );
|
752 - | }
|
753 - | self.assert_order(&self.origin_order, None, AvailabilityOrderView::Reclaim);
|
754 - | for (group, order) in &self.group_orders {
|
755 - | self.assert_order(order, Some(group), AvailabilityOrderView::Borrow);
|
756 - | }
|
757 - | }
|
758 - |
|
759 - | #[cfg(debug_assertions)]
|
760 - | fn assert_order(
|
761 - | &self,
|
762 - | order: &IntrusiveOrder<PartitionId>,
|
763 - | expected_group: Option<&EligibilityGroup>,
|
764 - | view: AvailabilityOrderView,
|
765 - | ) {
|
766 - | let expected = self
|
767 - | .connection_partitions
|
768 - | .values()
|
769 - | .filter(|record| {
|
770 - | record.residence.is_available()
|
771 - | && expected_group.is_none_or(|group| &record.group == group)
|
772 - | })
|
773 - | .count();
|
774 - | order.assert_consistent(
|
775 - | expected,
|
776 - | self.connection_partitions.len(),
|
777 - | "HTTP/1 connection cell order",
|
778 - | |connection_partition| {
|
779 - | let record = self
|
780 - | .connection_partitions
|
781 - | .get(&connection_partition)
|
782 - | .expect("ordered HTTP/1 connection cell disappeared");
|
783 - | if let Some(group) = expected_group {
|
784 - | assert_eq!(
|
785 - | &record.group, group,
|
786 - | "connection cell appeared in the wrong eligibility order"
|
787 - | );
|
788 - | }
|
789 - | *record
|
790 - | .residence
|
791 - | .links(view)
|
792 - | .expect("ordered connection cell lost its links")
|
793 - | },
|
794 - | );
|
795 - | }
|
796 - | }
|
797 - |
|
798 - | /// One unlocked HTTP/1 coordination step.
|
799 - | pub(in crate::client::pool) enum H1ReuseAction {
|
800 - | /// Install a prepared reservation in its connection cell.
|
801 - | Install(ReuseInstallAction),
|
802 - | /// Cancel an installed reservation.
|
803 - | Cancel(ReuseCancelAction),
|
804 - | /// Close a selected connection and release its capacity.
|
805 - | Reclaim(ReclaimAction),
|
806 - | /// Complete the connection cell after a sender transfer.
|
807 - | CompleteConnectionCell(ConnectionCellCompletion),
|
808 - | }
|
809 - |
|
810 - | impl H1ReuseAction {
|
811 - | /// Creates an unlocked connection cell installation crossing.
|
812 - | pub(super) fn install(origin: Arc<OriginAdmission>, prepared: PreparedReuseInstall) -> Self {
|
813 - | Self::Install(ReuseInstallAction {
|
814 - | origin,
|
815 - | install: Some(prepared),
|
816 - | })
|
817 - | }
|
818 - |
|
819 - | /// Creates an unlocked connection cell-cancellation crossing.
|
820 - | pub(super) fn cancel(
|
821 - | origin: Arc<OriginAdmission>,
|
822 - | cancellation: PreparedReuseCancellation,
|
823 - | ) -> Self {
|
824 - | Self::Cancel(ReuseCancelAction {
|
825 - | origin,
|
826 - | cancellation: Some(cancellation),
|
827 - | })
|
828 - | }
|
829 - |
|
830 - | /// Executes one crossing and returns the next admission action.
|
831 - | pub(super) fn drive_once(self) -> Option<AdmissionAction> {
|
832 - | match self {
|
833 - | Self::Install(action) => action.drive_once(),
|
834 - | Self::Cancel(action) => action.drive_once(),
|
835 - | Self::Reclaim(action) => action.drive_once(),
|
836 - | Self::CompleteConnectionCell(action) => action.drive_once(),
|
837 - | }
|
838 - | }
|
839 - | }
|
840 - |
|
841 - | /// Reservation installation crossing from admission to a connection cell.
|
842 - | pub(in crate::client::pool) struct ReuseInstallAction {
|
843 - | /// Admission authority that prepared and owns the reuse operation record.
|
844 - | origin: Arc<OriginAdmission>,
|
845 - | /// Prepared reservation still owned by this cancellation fallback.
|
846 - | install: Option<PreparedReuseInstall>,
|
847 - | }
|
848 - |
|
849 - | impl ReuseInstallAction {
|
850 - | /// Installs the prepared reservation without holding the admission lock.
|
851 - | fn drive_once(mut self) -> Option<AdmissionAction> {
|
852 - | let prepared = self
|
853 - | .install
|
854 - | .take()
|
855 - | .expect("HTTP/1 reuse operation install consumed more than once");
|
856 - | let Some(connection_partition) = self.origin.cell(&prepared.connection_partition) else {
|
857 - | return OriginAdmission::finish_h1_reuse(
|
858 - | &self.origin,
|
859 - | prepared.id,
|
860 - | H1AvailabilityOutcome::expired(prepared.connection_partition),
|
861 - | );
|
862 - | };
|
863 - | OriginCell::install_h1_reuse(&connection_partition, self.origin.clone(), prepared)
|
864 - | }
|
865 - | }
|
866 - |
|
867 - | impl Drop for ReuseInstallAction {
|
868 - | fn drop(&mut self) {
|
869 - | if let Some(prepared) = self.install.take() {
|
870 - | let outcome = h1_availability_outcome(
|
871 - | &self.origin,
|
872 - | &prepared.connection_partition,
|
873 - | |connection_partition| connection_partition.cancel_h1_reuse(prepared.id),
|
874 - | );
|
875 - | let next = OriginAdmission::finish_h1_reuse(&self.origin, prepared.id, outcome);
|
876 - | OriginAdmission::drive(next);
|
877 - | }
|
878 - | }
|
879 - | }
|
880 - |
|
881 - | /// Installed reservation cancellation crossing to its connection cell.
|
882 - | pub(in crate::client::pool) struct ReuseCancelAction {
|
883 - | /// Admission authority that owns the reuse operation record.
|
884 - | origin: Arc<OriginAdmission>,
|
885 - | /// Cancellation work still owned by this guard.
|
886 - | cancellation: Option<PreparedReuseCancellation>,
|
887 - | }
|
888 - |
|
889 - | impl ReuseCancelAction {
|
890 - | /// Clears the cell reservation and completes the admission operation.
|
891 - | fn drive_once(mut self) -> Option<AdmissionAction> {
|
892 - | let cancellation = self
|
893 - | .cancellation
|
894 - | .take()
|
895 - | .expect("HTTP/1 reuse operation cancellation consumed more than once");
|
896 - | let outcome = h1_availability_outcome(
|
897 - | &self.origin,
|
898 - | &cancellation.connection_partition,
|
899 - | |connection_partition| connection_partition.cancel_h1_reuse(cancellation.id),
|
900 - | );
|
901 - | OriginAdmission::finish_h1_reuse(&self.origin, cancellation.id, outcome)
|
902 - | }
|
903 - | }
|
904 - |
|
905 - | impl Drop for ReuseCancelAction {
|
906 - | fn drop(&mut self) {
|
907 - | if let Some(cancellation) = self.cancellation.take() {
|
908 - | let outcome = h1_availability_outcome(
|
909 - | &self.origin,
|
910 - | &cancellation.connection_partition,
|
911 - | |connection_partition| connection_partition.cancel_h1_reuse(cancellation.id),
|
912 - | );
|
913 - | let next = OriginAdmission::finish_h1_reuse(&self.origin, cancellation.id, outcome);
|
914 - | OriginAdmission::drive(next);
|
915 - | }
|
916 - | }
|
917 - | }
|
918 - |
|
919 - | /// Provisional sender owned by one resolving reuse operation.
|
920 - | pub(in crate::client::pool) struct ReuseCandidate {
|
921 - | /// Admission authority that owns the operation record.
|
922 - | origin: Arc<OriginAdmission>,
|
923 - | /// Operation that selected this sender.
|
924 - | reuse_id: ReuseId,
|
925 - | /// Cell that must revalidate or receive the sender back.
|
926 - | connection_partition: PartitionId,
|
927 - | /// Sender fallback while connection cell resolution is incomplete.
|
928 - | provisional: Option<ProvisionalH1>,
|
929 - | }
|
930 - |
|
931 - | impl ReuseCandidate {
|
932 - | /// Takes provisional sender ownership for one resolving connection cell reuse operation.
|
933 - | pub(in crate::client::pool) fn new(
|
934 - | origin: Arc<OriginAdmission>,
|
935 - | reuse_id: ReuseId,
|
936 - | connection_partition: PartitionId,
|
937 - | provisional: ProvisionalH1,
|
938 - | ) -> Self {
|
939 - | Self {
|
940 - | origin,
|
941 - | reuse_id,
|
942 - | connection_partition,
|
943 - | provisional: Some(provisional),
|
944 - | }
|
945 - | }
|
946 - |
|
947 - | /// Returns the connection selected by this reuse operation.
|
948 - | fn connection_id(&self) -> ConnectionId {
|
949 - | self.provisional
|
950 - | .as_ref()
|
951 - | .expect("HTTP/1 reuse operation candidate consumed more than once")
|
952 - | .connection_id()
|
953 - | }
|
954 - |
|
955 - | /// Revalidates the cell reservation and turns the sender into a selection.
|
956 - | ///
|
957 - | /// Failure returns this guard intact so dropping it restores the sender to
|
958 - | /// its owning cell and completes the reuse operation exactly once.
|
959 - | pub(in crate::client::pool) fn commit(mut self) -> Result<H1Selection, Self> {
|
960 - | let Some(connection_partition) = self.origin.cell(&self.connection_partition) else {
|
961 - | return Err(self);
|
962 - | };
|
963 - | let provisional = self
|
964 - | .provisional
|
965 - | .take()
|
966 - | .expect("HTTP/1 reuse operation candidate consumed more than once");
|
967 - | match OriginCell::commit_h1_reuse(&connection_partition, self.reuse_id, provisional) {
|
968 - | Ok(selection) => Ok(selection),
|
969 - | Err(provisional) => {
|
970 - | self.provisional = Some(provisional);
|
971 - | Err(self)
|
972 - | }
|
973 - | }
|
974 - | }
|
975 - |
|
976 - | /// Attempts reclaim and reports its terminal cell outcome and close result.
|
977 - | fn reclaim(mut self) -> (H1AvailabilityOutcome, bool) {
|
978 - | let Some(connection_partition) = self.origin.cell(&self.connection_partition) else {
|
979 - | drop(self.provisional.take());
|
980 - | return (
|
981 - | H1AvailabilityOutcome::expired(self.connection_partition),
|
982 - | false,
|
983 - | );
|
984 - | };
|
985 - | let provisional = self
|
986 - | .provisional
|
987 - | .take()
|
988 - | .expect("HTTP/1 reuse operation candidate consumed more than once");
|
989 - | let (availability, reclaimed) =
|
990 - | match OriginCell::reclaim_h1_reuse(&connection_partition, self.reuse_id, provisional) {
|
991 - | Ok(result) => result,
|
992 - | Err(provisional) => (
|
993 - | OriginCell::reject_h1_reuse_candidate(
|
994 - | &connection_partition,
|
995 - | self.reuse_id,
|
996 - | provisional,
|
997 - | ),
|
998 - | false,
|
999 - | ),
|
1000 - | };
|
1001 - | (
|
1002 - | H1AvailabilityOutcome::reported(self.connection_partition, availability),
|
1003 - | reclaimed,
|
1004 - | )
|
1005 - | }
|
1006 - | }
|
1007 - |
|
1008 - | impl fmt::Debug for ReuseCandidate {
|
1009 - | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
1010 - | f.debug_struct("ReuseCandidate")
|
1011 - | .field("reuse_id", &self.reuse_id)
|
1012 - | .field("connection_partition", &self.connection_partition)
|
1013 - | .field("provisional", &self.provisional)
|
1014 - | .finish_non_exhaustive()
|
1015 - | }
|
1016 - | }
|
1017 - |
|
1018 - | impl Drop for ReuseCandidate {
|
1019 - | fn drop(&mut self) {
|
1020 - | let Some(provisional) = self.provisional.take() else {
|
1021 - | return;
|
1022 - | };
|
1023 - | let outcome = match self.origin.cell(&self.connection_partition) {
|
1024 - | Some(connection_partition) => H1AvailabilityOutcome::reported(
|
1025 - | self.connection_partition,
|
1026 - | OriginCell::reject_h1_reuse_candidate(
|
1027 - | &connection_partition,
|
1028 - | self.reuse_id,
|
1029 - | provisional,
|
1030 - | ),
|
1031 - | ),
|
1032 - | None => {
|
1033 - | drop(provisional);
|
1034 - | H1AvailabilityOutcome::expired(self.connection_partition)
|
1035 - | }
|
1036 - | };
|
1037 - | let next = OriginAdmission::finish_h1_reuse(&self.origin, self.reuse_id, outcome);
|
1038 - | OriginAdmission::drive(next);
|
1039 - | }
|
1040 - | }
|
1041 - |
|
1042 - | /// Reclaim decision carrying its selected provisional sender.
|
1043 - | pub(in crate::client::pool) struct ReclaimAction {
|
1044 - | /// Admission authority that selected reclaim.
|
1045 - | origin: Arc<OriginAdmission>,
|
1046 - | /// Cell whose demand receives capacity released by reclaim.
|
1047 - | requesting_partition: PartitionId,
|
1048 - | /// Reuse operation completed by the reclaim attempt.
|
1049 - | reuse_id: ReuseId,
|
1050 - | /// Candidate whose `Drop` is the fallback before execution.
|
1051 - | candidate: Option<ReuseCandidate>,
|
1052 - | }
|
1053 - |
|
1054 - | impl ReclaimAction {
|
1055 - | /// Attempts logical close outside admission and reports the connection cell result.
|
1056 - | fn drive_once(mut self) -> Option<AdmissionAction> {
|
1057 - | let candidate = self
|
1058 - | .candidate
|
1059 - | .take()
|
1060 - | .expect("HTTP/1 reclaim action consumed more than once");
|
1061 - | let connection_id = candidate.connection_id();
|
1062 - | let connection_partition = candidate.connection_partition;
|
1063 - | let (outcome, reclaimed) = candidate.reclaim();
|
1064 - | if reclaimed {
|
1065 - | tracing::trace!(
|
1066 - | connection_id = %connection_id,
|
1067 - | request_partition = ?self.requesting_partition,
|
1068 - | connection_partition = ?connection_partition,
|
1069 - | origin_scheme = %self.origin.origin().scheme(),
|
1070 - | origin_host = self.origin.origin().host(),
|
1071 - | origin_port = ?self.origin.origin().port(),
|
1072 - | "HTTP/1 connection reclaimed for peer demand"
|
1073 - | );
|
1074 - | }
|
1075 - | OriginAdmission::finish_h1_reuse(&self.origin, self.reuse_id, outcome)
|
1076 - | }
|
1077 - | }
|
1078 - |
|
1079 - | /// Connection-owning-cell completion after an irreversible sender transfer.
|
1080 - | pub(in crate::client::pool) struct ConnectionCellCompletion {
|
1081 - | /// Admission authority that owns the remaining operation record.
|
1082 - | origin: Arc<OriginAdmission>,
|
1083 - | /// Operation completed at the connection cell.
|
1084 - | reuse_id: ReuseId,
|
1085 - | /// Cell whose local reservation must be released.
|
1086 - | connection_partition: PartitionId,
|
1087 - | /// Whether the requesting cell accepted and owns the sender.
|
1088 - | transferred: bool,
|
1089 - | /// Whether `Drop` still owns cell completion.
|
1090 - | active: bool,
|
1091 - | }
|
1092 - |
|
1093 - | impl ConnectionCellCompletion {
|
1094 - | /// Completes the connection cell outside admission.
|
1095 - | fn drive_once(mut self) -> Option<AdmissionAction> {
|
1096 - | let outcome = h1_availability_outcome(
|
1097 - | &self.origin,
|
1098 - | &self.connection_partition,
|
1099 - | |connection_partition| {
|
1100 - | connection_partition.complete_h1_reuse(self.reuse_id, self.transferred)
|
1101 - | },
|
1102 - | );
|
1103 - | let action = OriginAdmission::finish_h1_reuse(&self.origin, self.reuse_id, outcome);
|
1104 - | self.active = false;
|
1105 - | action
|
1106 - | }
|
1107 - | }
|
1108 - |
|
1109 - | impl Drop for ConnectionCellCompletion {
|
1110 - | fn drop(&mut self) {
|
1111 - | if !self.active {
|
1112 - | return;
|
1113 - | }
|
1114 - | let outcome = h1_availability_outcome(
|
1115 - | &self.origin,
|
1116 - | &self.connection_partition,
|
1117 - | |connection_partition| {
|
1118 - | connection_partition.complete_h1_reuse(self.reuse_id, self.transferred)
|
1119 - | },
|
1120 - | );
|
1121 - | let next = OriginAdmission::finish_h1_reuse(&self.origin, self.reuse_id, outcome);
|
1122 - | OriginAdmission::drive(next);
|
1123 - | }
|
1124 - | }
|
1125 - |
|
1126 - | /// Produces a complete connection cell outcome without overloading absence as no-op.
|
1127 - | fn h1_availability_outcome(
|
1128 - | origin: &OriginAdmission,
|
1129 - | connection_partition: &PartitionId,
|
1130 - | report: impl FnOnce(&Arc<OriginCell>) -> H1AvailabilitySnapshot,
|
1131 - | ) -> H1AvailabilityOutcome {
|
1132 - | match origin.cell(connection_partition) {
|
1133 - | Some(cell) => H1AvailabilityOutcome::reported(*connection_partition, report(&cell)),
|
1134 - | None => H1AvailabilityOutcome::expired(*connection_partition),
|
1135 - | }
|
1136 - | }
|
1137 - |
|
1138 - | impl OriginAdmission {
|
1139 - | /// Publishes a connection cell availability change and drives bounded progress.
|
1140 - | pub(in crate::client::pool) fn update_h1_availability(
|
1141 - | origin: &Arc<Self>,
|
1142 - | connection_partition: PartitionId,
|
1143 - | group: EligibilityGroup,
|
1144 - | snapshot: H1AvailabilitySnapshot,
|
1145 - | ) {
|
1146 - | let action = {
|
1147 - | let mut state = origin.state.lock();
|
1148 - | state
|
1149 - | .h1
|
1150 - | .update_availability(connection_partition, group, snapshot);
|
1151 - | Self::prepare_action(origin, &mut state)
|
1152 - | };
|
1153 - | Self::drive(action);
|
1154 - | }
|
1155 - |
|
1156 - | /// Completes a reuse operation whose returning sender was no longer reusable.
|
1157 - | pub(in crate::client::pool) fn reject_returned_h1_reuse(
|
1158 - | origin: &Arc<Self>,
|
1159 - | id: ReuseId,
|
1160 - | connection_partition: PartitionId,
|
1161 - | snapshot: H1AvailabilitySnapshot,
|
1162 - | ) {
|
1163 - | let action = Self::finish_h1_reuse(
|
1164 - | origin,
|
1165 - | id,
|
1166 - | H1AvailabilityOutcome::reported(connection_partition, snapshot),
|
1167 - | );
|
1168 - | Self::drive(action);
|
1169 - | }
|
1170 - |
|
1171 - | /// Applies a connection cell reuse operation installation result.
|
1172 - | pub(in crate::client::pool) fn finish_h1_reuse_install(
|
1173 - | origin: &Arc<Self>,
|
1174 - | id: ReuseId,
|
1175 - | connection_partition: PartitionId,
|
1176 - | result: ReuseInstallResult,
|
1177 - | ) -> Option<AdmissionAction> {
|
1178 - | match result {
|
1179 - | ReuseInstallResult::Rejected(availability) => Self::finish_h1_reuse(
|
1180 - | origin,
|
1181 - | id,
|
1182 - | H1AvailabilityOutcome::reported(connection_partition, availability),
|
1183 - | ),
|
1184 - | ReuseInstallResult::Installed => {
|
1185 - | let mut state = origin.state.lock();
|
1186 - | let record = state.h1.finish_install(id, false);
|
1187 - | if record.as_ref().is_some_and(|record| record.cancelled) {
|
1188 - | state.h1.cancellations.push_back(id);
|
1189 - | }
|
1190 - | Self::prepare_action(origin, &mut state)
|
1191 - | }
|
1192 - | ReuseInstallResult::Candidate(candidate) => {
|
1193 - | {
|
1194 - | let mut state = origin.state.lock();
|
1195 - | let record = state.h1.finish_install(id, true);
|
1196 - | if record.is_none() {
|
1197 - | drop(state);
|
1198 - | drop(candidate);
|
1199 - | return None;
|
1200 - | }
|
1201 - | }
|
1202 - | Self::resolve_h1_reuse(origin, id, candidate)
|
1203 - | }
|
1204 - | }
|
1205 - | }
|
1206 - |
|
1207 - | /// Resolves a provisional sender through borrow or reclaim policy.
|
1208 - | pub(in crate::client::pool) fn resolve_h1_reuse(
|
1209 - | origin: &Arc<Self>,
|
1210 - | id: ReuseId,
|
1211 - | candidate: ReuseCandidate,
|
1212 - | ) -> Option<AdmissionAction> {
|
1213 - | let action = {
|
1214 - | let mut state = origin.state.lock();
|
1215 - | let Some(record) = state.h1.begin_resolution(id) else {
|
1216 - | drop(state);
|
1217 - | drop(candidate);
|
1218 - | return None;
|
1219 - | };
|
1220 - | if record.cancelled
|
1221 - | || !state
|
1222 - | .demand_schedule
|
1223 - | .is_current_queued(&record.requesting_partition, record.demand)
|
1224 - | {
|
1225 - | drop(state);
|
1226 - | drop(candidate);
|
1227 - | return None;
|
1228 - | }
|
1229 - |
|
1230 - | match record.mode {
|
1231 - | ReuseMode::Borrow => {
|
1232 - | let delivery = state.take_delivery_id();
|
1233 - | let old_group = state
|
1234 - | .demand_schedule
|
1235 - | .group_for(&record.requesting_partition);
|
1236 - | let Some(scheduled) = state.demand_schedule.reserve_reuse_demand(
|
1237 - | &record.requesting_partition,
|
1238 - | record.demand,
|
1239 - | delivery,
|
1240 - | ) else {
|
1241 - | drop(state);
|
1242 - | drop(candidate);
|
1243 - | return None;
|
1244 - | };
|
1245 - | state.reconcile_demand_indexes(&scheduled.requesting_partition, old_group);
|
1246 - | Some(AdmissionAction::Delivery(DeliveryGuard::borrowed_h1(
|
1247 - | origin.clone(),
|
1248 - | delivery,
|
1249 - | scheduled.requesting_partition,
|
1250 - | scheduled.demand,
|
1251 - | id,
|
1252 - | record.connection_partition,
|
1253 - | candidate,
|
1254 - | )))
|
1255 - | }
|
1256 - | ReuseMode::Reclaim => {
|
1257 - | Some(AdmissionAction::H1(H1ReuseAction::Reclaim(ReclaimAction {
|
1258 - | origin: origin.clone(),
|
1259 - | requesting_partition: record.requesting_partition,
|
1260 - | reuse_id: id,
|
1261 - | candidate: Some(candidate),
|
1262 - | })))
|
1263 - | }
|
1264 - | }
|
1265 - | };
|
1266 - | action
|
1267 - | }
|
1268 - |
|
1269 - | /// Applies a terminal connection cell outcome and schedules the next admission action.
|
1270 - | fn finish_h1_reuse(
|
1271 - | origin: &Arc<Self>,
|
1272 - | id: ReuseId,
|
1273 - | outcome: H1AvailabilityOutcome,
|
1274 - | ) -> Option<AdmissionAction> {
|
1275 - | let mut state = origin.state.lock();
|
1276 - | state.h1.finish_reuse(id, outcome);
|
1277 - | Self::prepare_action(origin, &mut state)
|
1278 - | }
|
1279 - |
|
1280 - | /// Closes a borrow delivery fence and schedules connection cell completion.
|
1281 - | pub(super) fn finish_borrow_delivery(
|
1282 - | origin: &Arc<Self>,
|
1283 - | reuse_id: ReuseId,
|
1284 - | delivery: DeliveryId,
|
1285 - | requesting_partition: &PartitionId,
|
1286 - | result: DeliveryAckResult,
|
1287 - | transferred_connection_cell: Option<PartitionId>,
|
1288 - | rejected_outcome: Option<H1AvailabilityOutcome>,
|
1289 - | ) -> Option<AdmissionAction> {
|
1290 - | let mut state = origin.state.lock();
|
1291 - | state.finish_delivery(delivery, requesting_partition, result);
|
1292 - | if let Some(connection_partition) = transferred_connection_cell {
|
1293 - | return Some(AdmissionAction::H1(H1ReuseAction::CompleteConnectionCell(
|
1294 - | ConnectionCellCompletion {
|
1295 - | origin: origin.clone(),
|
1296 - | reuse_id,
|
1297 - | connection_partition,
|
1298 - | transferred: true,
|
1299 - | active: true,
|
1300 - | },
|
1301 - | )));
|
1302 - | }
|
1303 - | state.h1.finish_reuse(
|
1304 - | reuse_id,
|
1305 - | rejected_outcome.expect("rejected borrow had no connection cell outcome"),
|
1306 - | );
|
1307 - | Self::prepare_action(origin, &mut state)
|
1308 - | }
|
1309 - | }
|
1310 - |
|
1311 - | #[cfg(all(test, not(smithy_http_client_loom)))]
|
1312 - | mod tests {
|
1313 - | use super::*;
|
1314 - | use crate::client::pool::admission::{DemandSnapshot, ProtocolRequirement, SnapshotVersion};
|
1315 - | use crate::client::pool::partition::PartitionId;
|
1316 - |
|
1317 - | fn cell(index: usize) -> PartitionId {
|
1318 - | PartitionId::from_index(index)
|
1319 - | }
|
1320 - |
|
1321 - | fn schedule(requesting_partition: PartitionId, group: EligibilityGroup) -> DemandSchedule {
|
1322 - | let mut schedule = DemandSchedule::default();
|
1323 - | schedule.publish(
|
1324 - | requesting_partition,
|
1325 - | DemandSnapshot::active(
|
1326 - | DemandId::from_u64(1),
|
1327 - | SnapshotVersion::INITIAL,
|
1328 - | ProtocolRequirement::H1Compatible,
|
1329 - | group,
|
1330 - | ),
|
1331 - | );
|
1332 - | schedule
|
1333 - | }
|
1334 - |
|
1335 - | fn availability_snapshot(
|
1336 - | revision: u64,
|
1337 - | advertised: bool,
|
1338 - | blocked: bool,
|
1339 - | ) -> H1AvailabilitySnapshot {
|
1340 - | H1AvailabilitySnapshot::new(
|
1341 - | revision,
|
1342 - | H1Availability {
|
1343 - | advertised,
|
1344 - | blocked,
|
1345 - | },
|
1346 - | )
|
1347 - | }
|
1348 - |
|
1349 - | #[test]
|
1350 - | fn availability_publication_keeps_one_bounded_residence() {
|
1351 - | let connection_partition = cell(1);
|
1352 - | let group = EligibilityGroup::Pool;
|
1353 - | let mut coordination = H1Reuse::default();
|
1354 - |
|
1355 - | for revision in 1..=500 {
|
1356 - | coordination.update_availability(
|
1357 - | connection_partition,
|
1358 - | group.clone(),
|
1359 - | availability_snapshot(revision, true, false),
|
1360 - | );
|
1361 - | }
|
1362 - |
|
1363 - | assert_eq!(1, coordination.connection_partitions.len());
|
1364 - | assert_eq!(1, coordination.origin_order.len());
|
1365 - | assert_eq!(
|
1366 - | 1,
|
1367 - | coordination
|
1368 - | .group_orders
|
1369 - | .get(&group)
|
1370 - | .expect("connection cell group was not published")
|
1371 - | .len()
|
1372 - | );
|
1373 - |
|
1374 - | coordination.update_availability(
|
1375 - | connection_partition,
|
1376 - | group.clone(),
|
1377 - | availability_snapshot(501, false, false),
|
1378 - | );
|
1379 - | assert_eq!(0, coordination.origin_order.len());
|
1380 - | assert_eq!(
|
1381 - | 0,
|
1382 - | coordination
|
1383 - | .group_orders
|
1384 - | .get(&group)
|
1385 - | .expect("connection cell group disappeared")
|
1386 - | .len()
|
1387 - | );
|
1388 - | }
|
1389 - |
|
1390 - | #[test]
|
1391 - | fn h2_required_demand_reclaims_local_h1_capacity() {
|
1392 - | let requesting_partition = cell(1);
|
1393 - | let group = EligibilityGroup::Pool;
|
1394 - | let mut schedule = DemandSchedule::default();
|
1395 - | schedule.publish(
|
1396 - | requesting_partition,
|
1397 - | DemandSnapshot::active(
|
1398 - | DemandId::from_u64(1),
|
1399 - | SnapshotVersion::INITIAL,
|
1400 - | ProtocolRequirement::H2Required,
|
1401 - | group.clone(),
|
1402 - | ),
|
1403 - | );
|
1404 - | let mut coordination = H1Reuse::default();
|
1405 - | coordination.update_availability(
|
1406 - | requesting_partition,
|
1407 - | group,
|
1408 - | availability_snapshot(1, true, false),
|
1409 - | );
|
1410 - |
|
1411 - | let prepared = coordination
|
1412 - | .prepare_reuse(&schedule)
|
1413 - | .expect("local HTTP/1 capacity was not selected for reclaim");
|
1414 - |
|
1415 - | assert_eq!(requesting_partition, prepared.connection_partition);
|
1416 - | assert_eq!(
|
1417 - | ReuseMode::Reclaim,
|
1418 - | coordination.operations[&prepared.id].mode
|
1419 - | );
|
1420 - | }
|
1421 - |
|
1422 - | #[test]
|
1423 - | fn reuse_selection_skips_the_requesting_cell() {
|
1424 - | let requesting_partition = cell(1);
|
1425 - | let peer = cell(2);
|
1426 - | let group = EligibilityGroup::Pool;
|
1427 - | let mut coordination = H1Reuse::default();
|
1428 - | coordination.update_availability(
|
1429 - | requesting_partition,
|
1430 - | group.clone(),
|
1431 - | availability_snapshot(1, true, false),
|
1432 - | );
|
1433 - | coordination.update_availability(
|
1434 - | peer,
|
1435 - | group.clone(),
|
1436 - | availability_snapshot(1, true, false),
|
1437 - | );
|
1438 - |
|
1439 - | let reuse_id = coordination
|
1440 - | .prepare_reuse(&schedule(requesting_partition, group))
|
1441 - | .expect("peer connection cell was not selected");
|
1442 - | assert_eq!(peer, reuse_id.connection_partition);
|
1443 - | assert_ne!(requesting_partition, reuse_id.connection_partition);
|
1444 - | }
|
1445 - |
|
1446 - | #[test]
|
1447 - | fn expired_connection_cell_terminates_its_reuse_without_republication() {
|
1448 - | let connection_partition = cell(1);
|
1449 - | let requesting_partition = cell(2);
|
1450 - | let group = EligibilityGroup::Pool;
|
1451 - | let schedule = schedule(requesting_partition, group.clone());
|
1452 - | let mut coordination = H1Reuse::default();
|
1453 - | coordination.update_availability(
|
1454 - | connection_partition,
|
1455 - | group,
|
1456 - | availability_snapshot(1, true, false),
|
1457 - | );
|
1458 - | let reuse_id = coordination
|
1459 - | .prepare_reuse(&schedule)
|
1460 - | .expect("connection cell did not produce a reuse operation");
|
1461 - |
|
1462 - | coordination.finish_reuse(
|
1463 - | reuse_id.id,
|
1464 - | H1AvailabilityOutcome::expired(connection_partition),
|
1465 - | );
|
1466 - |
|
1467 - | assert!(!coordination
|
1468 - | .connection_partitions
|
1469 - | .contains_key(&connection_partition));
|
1470 - | assert!(coordination.prepare_reuse(&schedule).is_none());
|
1471 - | assert!(coordination.operations.is_empty());
|
1472 - | }
|
1473 - |
|
1474 - | #[test]
|
1475 - | fn stale_terminal_report_cannot_hide_newer_availability() {
|
1476 - | let connection_partition = cell(1);
|
1477 - | let requesting_partition = cell(2);
|
1478 - | let group = EligibilityGroup::Pool;
|
1479 - | let schedule = schedule(requesting_partition, group.clone());
|
1480 - | let mut coordination = H1Reuse::default();
|
1481 - | coordination.update_availability(
|
1482 - | connection_partition,
|
1483 - | group,
|
1484 - | availability_snapshot(1, true, false),
|
1485 - | );
|
1486 - | let reuse_id = coordination
|
1487 - | .prepare_reuse(&schedule)
|
1488 - | .expect("connection cell did not produce a reuse operation");
|
1489 - |
|
1490 - | coordination.finish_reuse(
|
1491 - | reuse_id.id,
|
1492 - | H1AvailabilityOutcome::reported(
|
1493 - | connection_partition,
|
1494 - | availability_snapshot(3, true, false),
|
1495 - | ),
|
1496 - | );
|
1497 - | coordination.finish_reuse(
|
1498 - | reuse_id.id,
|
1499 - | H1AvailabilityOutcome::reported(
|
1500 - | connection_partition,
|
1501 - | availability_snapshot(2, false, false),
|
1502 - | ),
|
1503 - | );
|
1504 - |
|
1505 - | let connection_partition = coordination
|
1506 - | .connection_partitions
|
1507 - | .get(&connection_partition)
|
1508 - | .expect("stale report removed the connection cell");
|
1509 - | assert!(connection_partition.advertised);
|
1510 - | assert!(connection_partition.residence.is_available());
|
1511 - | assert_eq!(3, connection_partition.revision);
|
1512 - | }
|
1513 - |
|
1514 - | #[test]
|
1515 - | fn duplicate_terminal_report_still_refreshes_availability() {
|
1516 - | let connection_partition = cell(1);
|
1517 - | let requesting_partition = cell(2);
|
1518 - | let group = EligibilityGroup::Pool;
|
1519 - | let schedule = schedule(requesting_partition, group.clone());
|
1520 - | let mut coordination = H1Reuse::default();
|
1521 - | coordination.update_availability(
|
1522 - | connection_partition,
|
1523 - | group,
|
1524 - | availability_snapshot(1, true, false),
|
1525 - | );
|
1526 - | let reuse_id = coordination
|
1527 - | .prepare_reuse(&schedule)
|
1528 - | .expect("connection cell did not produce a reuse operation");
|
1529 - |
|
1530 - | coordination.finish_reuse(
|
1531 - | reuse_id.id,
|
1532 - | H1AvailabilityOutcome::reported(
|
1533 - | connection_partition,
|
1534 - | availability_snapshot(2, true, false),
|
1535 - | ),
|
1536 - | );
|
1537 - | coordination.finish_reuse(
|
1538 - | reuse_id.id,
|
1539 - | H1AvailabilityOutcome::reported(
|
1540 - | connection_partition,
|
1541 - | availability_snapshot(3, false, false),
|
1542 - | ),
|
1543 - | );
|
1544 - |
|
1545 - | let connection_partition = coordination
|
1546 - | .connection_partitions
|
1547 - | .get(&connection_partition)
|
1548 - | .expect("live availability report removed the connection cell");
|
1549 - | assert!(!connection_partition.advertised);
|
1550 - | assert!(!connection_partition.residence.is_available());
|
1551 - | }
|
1552 - | }
|