fix(server): keep shutdown responsive during sends
This commit is contained in:
2 files changed
+21
-13
No files matched your search
@@ -202,15 +202,14 @@ Do not rewrite the protocol evidence to match them.
|
|||||||
The only interoperability constraint is negative — expiry must never be
|
The only interoperability constraint is negative — expiry must never be
|
||||||
signalled — and silence satisfies it. A zero idle timeout expires a session at
|
signalled — and silence satisfies it. A zero idle timeout expires a session at
|
||||||
the next evaluation; it is not "never expire".
|
the next evaluation; it is not "never expire".
|
||||||
- **Expiry is packet-driven until the runtime exists.** Logical expiry is
|
- **Logical expiry is request-exact; physical reclamation also runs on the
|
||||||
timestamp-based, but reclamation happens in a global sweep run after
|
runtime's fixed maintenance cadence.** Every authenticated, structurally valid
|
||||||
authentication and before dispatch, so an expired session may stay resident
|
request runs the global sweep before dispatch, so a request at the deadline
|
||||||
until the next authenticated, structurally valid request. Nothing observable
|
finds the session expired and a stale session cannot deny an open. The Tokio
|
||||||
turns on it: the table is already bounded, and the sweep runs before capacity
|
runtime invokes that same core sweep once per second so dead sessions do not
|
||||||
is judged, so a stale session cannot deny an open. Because the sweep is global
|
remain resident when there is no further traffic. This cadence is internal
|
||||||
it also runs for a no-test open, where upstream was observed not to reclaim.
|
housekeeping, not protocol policy or public configuration, and the runtime
|
||||||
Do not add a public timer or tick abstraction for this; the Tokio slice may add
|
must not inspect session internals or duplicate the expiry rule.
|
||||||
scheduled sweeps.
|
|
||||||
- **The maximum-duration deadline is `first served echo + maximum + 2 s`.** The
|
- **The maximum-duration deadline is `first served echo + maximum + 2 s`.** The
|
||||||
origin is measured behavior: neither the open, nor a rejected first echo, nor a
|
origin is measured behavior: neither the open, nor a rejected first echo, nor a
|
||||||
rate-limited one starts it. The two-second grace matches the measured upstream
|
rate-limited one starts it. The two-second grace matches the measured upstream
|
||||||
|
|||||||
@@ -62,9 +62,10 @@ impl Server {
|
|||||||
/// Serves datagrams until `shutdown` completes.
|
/// Serves datagrams until `shutdown` completes.
|
||||||
///
|
///
|
||||||
/// Graceful shutdown sends no session-close packets and leaves no hidden
|
/// Graceful shutdown sends no session-close packets and leaves no hidden
|
||||||
/// receive task behind. Per-packet send failures and short sends drop only
|
/// receive task behind, even while a reply send is waiting for socket
|
||||||
/// that reply; receive failures and internal core failures terminate the
|
/// writability. Per-packet send failures and short sends drop only that
|
||||||
/// loop with an error.
|
/// reply; receive failures and internal core failures terminate the loop
|
||||||
|
/// with an error.
|
||||||
pub async fn run<F>(&mut self, shutdown: F) -> Result<(), ServerRuntimeError>
|
pub async fn run<F>(&mut self, shutdown: F) -> Result<(), ServerRuntimeError>
|
||||||
where
|
where
|
||||||
F: Future<Output = ()>,
|
F: Future<Output = ()>,
|
||||||
@@ -89,7 +90,15 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(reply) = self.core.handle_datagram(peer, &self.recv_buffer[..len])? {
|
if let Some(reply) = self.core.handle_datagram(peer, &self.recv_buffer[..len])? {
|
||||||
let sent = self.socket.send_to(&reply, peer).await;
|
let send = self.socket.send_to(&reply, peer);
|
||||||
|
tokio::pin!(send);
|
||||||
|
let sent = loop {
|
||||||
|
tokio::select! {
|
||||||
|
_ = &mut shutdown => return Ok(()),
|
||||||
|
_ = maintenance.tick() => self.core.maintain(),
|
||||||
|
sent = &mut send => break sent,
|
||||||
|
}
|
||||||
|
};
|
||||||
if !matches!(sent, Ok(len) if len == reply.len()) {
|
if !matches!(sent, Ok(len) if len == reply.len()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user