Hardswap Invariants in tests + Quota fixes

This commit is contained in:
Alexey
2026-09-20 00:28:52 +03:00
parent 89dacbd17e
commit d706b3f3ba
66 changed files with 1821 additions and 505 deletions
+8
View File
@@ -20,6 +20,13 @@ impl WebSession {
completion: StreamCompletion,
retain_reservation_on_reject: bool,
) -> bool {
if self.close_if_cancelled() {
completion
.retain_rejected
.store(retain_reservation_on_reject, Ordering::Release);
drop(completion);
return false;
}
let stream = completion.stream;
let peer_port = completion.peer_port;
let Some(manager) = self.manager.upgrade() else {
@@ -60,6 +67,7 @@ impl WebSession {
);
let logical_stream = WebLogicalStream::new(Arc::clone(&session), stream);
tokio::select! {
biased;
_ = cancel.cancelled() => {}
_ = run_stream(
Arc::clone(&session),
+37 -12
View File
@@ -26,9 +26,14 @@ impl WebSession {
if !self.carrier().is_multiplexed() {
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let (epoch, healthy) = {
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if let Some(unacked) = &state.unacked {
@@ -92,6 +97,11 @@ impl WebSession {
notified.as_mut().enable();
{
let mut state = self.state.lock();
if self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if state.down_epoch != epoch {
return Ok(PollResult {
body: Bytes::new(),
@@ -129,18 +139,33 @@ impl WebSession {
notified.await;
}
};
match tokio::time::timeout(deadline, poll).await {
Ok(result) => result,
Err(_) => {
let mut state = self.state.lock();
if state.down_epoch == epoch {
state.activity.touch_progress(Instant::now());
tokio::select! {
biased;
_ = self.cancel.cancelled() => {
self.close_if_cancelled();
Err(ManagerError::Closed)
}
result = tokio::time::timeout(deadline, poll) => match result {
Ok(result) => result,
Err(_) => {
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.state.lock();
if self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if state.down_epoch == epoch {
state.activity.touch_progress(Instant::now());
}
Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: false,
})
}
Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: false,
})
}
}
}
+7
View File
@@ -22,6 +22,9 @@ impl WebSession {
if self.carrier() != WebCarrier::HttpsLanes || lane_id > frame::MAX_STREAM_ID {
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let frames = match frame::parse_all(body, &self.limits) {
Ok(frames) => frames,
Err(_) => {
@@ -175,6 +178,10 @@ impl WebSession {
if matches!(result, Err(ManagerError::Backpressure)) {
return result;
}
if matches!(result, Err(ManagerError::Closed)) && self.close_if_cancelled() {
drop(opened);
return result;
}
if result.is_err() {
self.close(SessionCloseReason::Protocol);
drop(opened);
+79 -41
View File
@@ -42,9 +42,14 @@ impl WebSession {
if !self.carrier().uses_lanes() || lane_id > frame::MAX_STREAM_ID {
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let lane_ready = if let Some(expected_instance) = expected_instance {
let state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
state
@@ -63,7 +68,9 @@ impl WebSession {
}
let (instance, epoch, notify, healthy) = {
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
let (acknowledged, replay) = {
@@ -175,7 +182,9 @@ impl WebSession {
notified.as_mut().enable();
{
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
let carrier_health_eligible = lane_id != 0
@@ -245,55 +254,72 @@ impl WebSession {
notified.await;
}
};
match tokio::time::timeout(deadline, poll).await {
Ok(result) => result,
Err(_) => {
let mut state = self.state.lock();
if state.closed {
return Err(ManagerError::Closed);
}
if !state.carrier_lanes.contains_key(&lane_id) {
return Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: true,
});
}
if lane_id != 0
&& !state.streams.contains_key(&lane_id)
&& state.closed_streams.contains(&lane_id)
{
return Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: true,
});
}
if let Some(lane) = state.carrier_lanes.get(&lane_id) {
if lane.instance != instance {
tokio::select! {
biased;
_ = self.cancel.cancelled() => {
self.close_if_cancelled();
Err(ManagerError::Closed)
}
result = tokio::time::timeout(deadline, poll) => match result {
Ok(result) => result,
Err(_) => {
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.state.lock();
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if !state.carrier_lanes.contains_key(&lane_id) {
return Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: true,
});
}
if lane.down_epoch == epoch {
state.activity.touch_progress(Instant::now());
if lane_id != 0
&& !state.streams.contains_key(&lane_id)
&& state.closed_streams.contains(&lane_id)
{
return Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: true,
});
}
if let Some(lane) = state.carrier_lanes.get(&lane_id) {
if lane.instance != instance {
return Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: true,
});
}
if lane.down_epoch == epoch {
state.activity.touch_progress(Instant::now());
}
}
Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: false,
})
}
Ok(PollResult {
body: Bytes::new(),
next_cursor: cursor,
lane_closed: false,
})
}
}
}
async fn wait_for_lane_open(&self, lane_id: u32, cursor: u64) -> Result<bool, ManagerError> {
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let wait = {
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if state.carrier_lanes.contains_key(&lane_id) {
@@ -334,7 +360,9 @@ impl WebSession {
notified.as_mut().enable();
{
let state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
if state.carrier_lanes.contains_key(&lane_id)
@@ -346,14 +374,24 @@ impl WebSession {
}
notified.await;
}
})
.await;
});
let opened = tokio::select! {
biased;
_ = self.cancel.cancelled() => {
drop(wait);
self.close_if_cancelled();
return Err(ManagerError::Closed);
}
opened = opened => opened,
};
drop(wait);
match opened {
Ok(result) => result,
Err(_) => {
let state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
drop(state);
self.close_if_cancelled();
Err(ManagerError::Closed)
} else {
Ok(state.carrier_lanes.contains_key(&lane_id)
+11 -13
View File
@@ -121,6 +121,15 @@ impl CarrierSupersedeCompletion<'_> {
}
impl WebSession {
/// Closes a bearer as soon as its process-owned user registration is revoked.
pub(crate) fn close_if_cancelled(&self) -> bool {
if !self.cancel.is_cancelled() {
return false;
}
self.close(SessionCloseReason::UserDisabled);
true
}
/// Closes carrier state while relay tasks retain their admission until exit.
pub(crate) fn close(&self, reason: SessionCloseReason) -> SessionCloseOutcome {
let mut state = self.state.lock();
@@ -221,19 +230,8 @@ impl WebSession {
/// Atomically closes a session only when reconnect grace is still due.
pub(crate) fn close_if_due(&self, now: Instant) -> bool {
if self.cancel.is_cancelled() {
let released = {
let mut state = self.state.lock();
if state.closed || state.close_requested.is_some() {
None
} else {
Some(self.release_on_close_locked(&mut state, SessionCloseReason::UserDisabled))
}
};
if let Some(released) = released {
self.finish_close(released);
return true;
}
if self.close_if_cancelled() {
return true;
}
let healthy = {
let mut state = self.state.lock();
+3
View File
@@ -34,6 +34,9 @@ impl WebSession {
&self,
state: &SessionState,
) -> Result<(), crate::web::manager::ManagerError> {
if self.cancel.is_cancelled() {
return Err(crate::web::manager::ManagerError::Closed);
}
if state.negotiation_phase == SessionNegotiationPhase::Uncommitted
&& self
.carrier_deadline_at
+7
View File
@@ -69,6 +69,9 @@ impl WebSession {
if !self.carrier().is_multiplexed() {
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
if self
.up_active
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
@@ -157,6 +160,10 @@ impl WebSession {
if matches!(result, Err(ManagerError::Backpressure)) {
return result;
}
if matches!(result, Err(ManagerError::Closed)) && self.close_if_cancelled() {
drop(opened);
return result;
}
if result.is_err() {
self.close(SessionCloseReason::Protocol);
drop(opened);
+24 -3
View File
@@ -39,8 +39,12 @@ pub(crate) struct WebSocketProbeReservation {
impl WebSocketProbeReservation {
/// Binds the admitted process connection to the future commit acknowledgement.
pub(crate) fn bind(&mut self, owner: u64) -> Result<(), ManagerError> {
if self.session.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.session.state.lock();
if state.closed
|| self.session.cancel.is_cancelled()
|| !state.websocket_probe_claimed
|| state.websocket_commit_ack_owner.is_some()
{
@@ -85,8 +89,12 @@ impl WebSocketLaneReservation {
if self.phase != WebSocketLaneReservationPhase::Reserved {
return Err(ManagerError::Concurrent);
}
if self.session.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.session.state.lock();
if state.closed
|| self.session.cancel.is_cancelled()
|| state
.carrier_lanes
.get(&self.claim.lane.lane_id)
@@ -113,8 +121,12 @@ impl WebSocketLaneReservation {
{
return Err(ManagerError::Protocol);
}
if self.session.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.session.state.lock();
if state
if self.session.cancel.is_cancelled()
|| state
.carrier_lanes
.get(&self.claim.lane.lane_id)
.is_none_or(|lane| lane.instance != self.claim.lane.instance)
@@ -174,8 +186,11 @@ impl WebSession {
self: &Arc<Self>,
acknowledge_commit: bool,
) -> Result<Option<WebSocketProbeReservation>, ManagerError> {
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
return Err(ManagerError::Closed);
}
self.ensure_carrier_active_locked(&state)?;
@@ -216,8 +231,11 @@ impl WebSession {
{
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let mut state = self.state.lock();
if state.closed {
if state.closed || self.cancel.is_cancelled() {
return Err(ManagerError::Closed);
}
if state.active_peer_ports.len() >= self.profile.max_streams_per_session
@@ -306,6 +324,9 @@ impl WebSession {
{
return Err(ManagerError::Protocol);
}
if self.close_if_cancelled() {
return Err(ManagerError::Closed);
}
let lane_id = reservation.lane_id();
let frames = frame::parse_all(body, &self.limits).map_err(|_| ManagerError::Protocol)?;
if frames