Bridge Request/Buffers/Recovery

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-09-02 18:49:10 +03:00
parent 01ffca5d34
commit 0d8d331c7c
28 changed files with 645 additions and 76 deletions
+58 -15
View File
@@ -24,7 +24,7 @@ impl WebProcessRuntime {
client_ip: IpAddr,
) -> std::result::Result<BootstrapResult, ManagerError> {
let generation = self.active_generation();
self.issue_bootstrap_inner(&generation, profile, client_ip, None, false)
self.issue_bootstrap_inner(&generation, profile, client_ip, None, false, None)
}
/// Issues one bootstrap against the generation that selected the bridge profile.
@@ -35,7 +35,7 @@ impl WebProcessRuntime {
profile: Arc<WebRuntimeProfile>,
client_ip: IpAddr,
) -> std::result::Result<BootstrapResult, ManagerError> {
self.issue_bootstrap_inner(generation, profile, client_ip, None, false)
self.issue_bootstrap_inner(generation, profile, client_ip, None, false, None)
}
/// Issues one bridge bootstrap with bounded non-secret request metadata.
@@ -46,7 +46,7 @@ impl WebProcessRuntime {
client_ip: IpAddr,
user_agent: Option<&str>,
) -> std::result::Result<BootstrapResult, ManagerError> {
self.issue_bootstrap_inner(generation, profile, client_ip, user_agent, false)
self.issue_bootstrap_inner(generation, profile, client_ip, user_agent, false, None)
}
/// Issues one recovery bootstrap with the same positive-only admission boundary.
@@ -56,8 +56,16 @@ impl WebProcessRuntime {
profile: Arc<WebRuntimeProfile>,
client_ip: IpAddr,
user_agent: Option<&str>,
predecessor_session_id: Option<u64>,
) -> std::result::Result<BootstrapResult, ManagerError> {
self.issue_bootstrap_inner(generation, profile, client_ip, user_agent, true)
self.issue_bootstrap_inner(
generation,
profile,
client_ip,
user_agent,
true,
predecessor_session_id,
)
}
fn issue_bootstrap_inner(
@@ -67,6 +75,7 @@ impl WebProcessRuntime {
client_ip: IpAddr,
user_agent: Option<&str>,
recovery: bool,
predecessor_session_id: Option<u64>,
) -> std::result::Result<BootstrapResult, ManagerError> {
let config = generation.config();
let profile = config
@@ -170,6 +179,7 @@ impl WebProcessRuntime {
session_ip_learning_eligible: false,
used: false,
recovery,
predecessor_session_id,
},
);
*state.bootstraps_per_ip.entry(client_ip).or_insert(0) += 1;
@@ -183,14 +193,32 @@ impl WebProcessRuntime {
self.telemetry
.record_bridge_recovery(WebBridgeRecoveryEvent::BootstrapIssued);
}
self.trace.record_profile_lifecycle(
client_ip,
Some(trace_session_id),
&profile,
crate::web::trace::TraceLifecycleEvent::BridgeIssued,
None,
None,
);
if recovery {
self.trace.record_lifecycle_with_context(
None,
Some(client_ip),
crate::web::trace::TraceIdentity::from_optional_profile(
Some(trace_session_id),
&profile,
),
crate::web::trace::TraceLifecycleEvent::BridgeIssued,
None,
None,
crate::web::trace::TraceLifecycleContext {
peer_gap_ms: None,
predecessor_session_id,
},
);
} else {
self.trace.record_profile_lifecycle(
client_ip,
Some(trace_session_id),
&profile,
crate::web::trace::TraceLifecycleEvent::BridgeIssued,
None,
None,
);
}
Ok(BootstrapResult {
token,
trace_session_id,
@@ -227,13 +255,28 @@ impl WebProcessRuntime {
hash: TokenHash,
host: &str,
) -> std::result::Result<Arc<WebSession>, ManagerError> {
self.state
.lock()
let state = self.state.lock();
if let Some(session) = state
.sessions
.get(&hash)
.cloned()
.filter(|session| session.matches_host(host))
.ok_or(ManagerError::Authentication)
{
return Ok(session);
}
let retired_carrier = state
.closed_tokens
.get(&hash)
.filter(|closed| closed.host == host)
.map(|closed| closed.carrier);
drop(state);
if let Some(carrier) = retired_carrier {
self.telemetry.record_session_observation(
carrier,
crate::web::telemetry::WebSessionLifecycleObservation::RequestAfterClose,
);
}
Err(ManagerError::Authentication)
}
/// Resolves a current bearer only when it belongs to the recovering profile.
-2
View File
@@ -82,9 +82,7 @@ impl WebProcessRuntime {
&mut state,
hash,
profile_host,
session.trace_session_id(),
session.carrier(),
reason,
closed_token_lifetime,
self.limits.max_sessions_global.saturating_mul(16),
);
+14 -2
View File
@@ -339,7 +339,14 @@ impl WebProcessRuntime {
state.sessions.insert(session_hash, Arc::clone(&session));
*state.sessions_per_ip.entry(client_ip).or_insert(0) += 1;
*state.sessions_per_profile.entry(profile_key).or_insert(0) += 1;
let (issuance_ip, candidate_count, user_agent, user_agent_id, recovery) = {
let (
issuance_ip,
candidate_count,
user_agent,
user_agent_id,
recovery,
predecessor_session_id,
) = {
let entry = state
.bootstraps
.get_mut(&bootstrap_hash)
@@ -367,6 +374,7 @@ impl WebProcessRuntime {
entry.user_agent.clone(),
entry.user_agent_id,
entry.recovery,
entry.predecessor_session_id,
)
};
decrement_map(&mut state.bootstraps_per_ip, &issuance_ip);
@@ -422,13 +430,17 @@ impl WebProcessRuntime {
scores,
None,
);
self.trace.record_lifecycle(
self.trace.record_lifecycle_with_context(
None,
Some(client_ip),
identity,
TraceLifecycleEvent::SessionCreated,
None,
None,
crate::web::trace::TraceLifecycleContext {
peer_gap_ms: None,
predecessor_session_id,
},
);
Ok(result)
}
@@ -98,9 +98,7 @@ impl WebProcessRuntime {
&mut state,
old_hash,
&replacement.profile.host,
replacement.old_session.trace_session_id(),
replacement.old_session.carrier(),
crate::web::session::SessionCloseReason::CarrierSuperseded,
Duration::from_secs(replacement.old_session.timeouts().bootstrap_lifetime_secs),
self.limits.max_sessions_global.saturating_mul(16),
);
@@ -126,6 +124,9 @@ impl WebProcessRuntime {
self.telemetry.record_bridge_recovery(
crate::web::telemetry::WebBridgeRecoveryEvent::SessionCreated,
);
self.telemetry.record_bridge_recovery(
crate::web::telemetry::WebBridgeRecoveryEvent::ClosedBeforeCommit,
);
}
self.telemetry.record_session_closed(
replacement.old_session.carrier(),
+2 -8
View File
@@ -88,6 +88,8 @@ pub(super) struct Bootstrap {
pub(super) used: bool,
/// Whether this credential was issued by the post-commit recovery representation.
pub(super) recovery: bool,
/// Previous logical session authenticated by the recovery request, if current.
pub(super) predecessor_session_id: Option<u64>,
}
/// Bounded replay marker for one explicitly or naturally closed session token.
@@ -96,12 +98,8 @@ pub(super) struct ClosedToken {
pub(super) expires_at: Instant,
/// Canonical host that owned the session.
pub(super) host: String,
/// Non-secret logical trace owner retained for exact late-request diagnostics.
pub(super) trace_session_id: u64,
/// Carrier that owned the retired bearer.
pub(super) carrier: WebCarrier,
/// First-writer terminal cause for the retired bearer.
pub(super) reason: crate::web::session::SessionCloseReason,
}
/// Current logical-session owner stored without exposing bearer credentials.
@@ -363,9 +361,7 @@ pub(super) fn remember_closed_token_locked(
state: &mut ManagerState,
hash: TokenHash,
host: &str,
trace_session_id: u64,
carrier: WebCarrier,
reason: crate::web::session::SessionCloseReason,
lifetime: Duration,
capacity: usize,
) {
@@ -374,9 +370,7 @@ pub(super) fn remember_closed_token_locked(
ClosedToken {
expires_at: Instant::now() + lifetime,
host: host.to_string(),
trace_session_id,
carrier,
reason,
},
);
while state.closed_tokens.len() > capacity {