fix(rustproxy-http): Evict stale HTTP/2 pooled senders and retry bodyless requests with fresh backend connections to avoid 502s
This commit is contained in:
@@ -18,6 +18,9 @@ const MAX_IDLE_PER_KEY: usize = 16;
|
||||
const IDLE_TIMEOUT: Duration = Duration::from_secs(90);
|
||||
/// Background eviction interval.
|
||||
const EVICTION_INTERVAL: Duration = Duration::from_secs(30);
|
||||
/// Maximum age for pooled HTTP/2 connections before proactive eviction.
|
||||
/// Prevents staleness from backends that close idle connections (e.g. nginx GOAWAY).
|
||||
const MAX_H2_AGE: Duration = Duration::from_secs(120);
|
||||
|
||||
/// Identifies a unique backend endpoint.
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||
@@ -37,7 +40,6 @@ struct IdleH1 {
|
||||
/// A pooled HTTP/2 sender (multiplexed, Clone-able).
|
||||
struct PooledH2 {
|
||||
sender: http2::SendRequest<BoxBody<Bytes, hyper::Error>>,
|
||||
#[allow(dead_code)] // Reserved for future age-based eviction
|
||||
created_at: Instant,
|
||||
}
|
||||
|
||||
@@ -116,8 +118,8 @@ impl ConnectionPool {
|
||||
let entry = self.h2_pool.get(key)?;
|
||||
let pooled = entry.value();
|
||||
|
||||
// Check if the h2 connection is still alive
|
||||
if pooled.sender.is_closed() {
|
||||
// Check if the h2 connection is still alive and not too old
|
||||
if pooled.sender.is_closed() || pooled.created_at.elapsed() >= MAX_H2_AGE {
|
||||
drop(entry);
|
||||
self.h2_pool.remove(key);
|
||||
return None;
|
||||
@@ -130,6 +132,12 @@ impl ConnectionPool {
|
||||
None
|
||||
}
|
||||
|
||||
/// Remove a dead HTTP/2 sender from the pool.
|
||||
/// Called when `send_request` fails to prevent subsequent requests from reusing the stale sender.
|
||||
pub fn remove_h2(&self, key: &PoolKey) {
|
||||
self.h2_pool.remove(key);
|
||||
}
|
||||
|
||||
/// Register an HTTP/2 sender in the pool. Since h2 is multiplexed,
|
||||
/// only one sender per key is stored (it's Clone-able).
|
||||
pub fn register_h2(&self, key: PoolKey, sender: http2::SendRequest<BoxBody<Bytes, hyper::Error>>) {
|
||||
@@ -165,10 +173,10 @@ impl ConnectionPool {
|
||||
h1_pool.remove(&key);
|
||||
}
|
||||
|
||||
// Evict dead H2 connections
|
||||
// Evict dead or aged-out H2 connections
|
||||
let mut dead_h2 = Vec::new();
|
||||
for entry in h2_pool.iter() {
|
||||
if entry.value().sender.is_closed() {
|
||||
if entry.value().sender.is_closed() || entry.value().created_at.elapsed() >= MAX_H2_AGE {
|
||||
dead_h2.push(entry.key().clone());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user