refactor(client): skip some pool housekeeping on shared h2 handles
This commit is contained in:
@@ -251,7 +251,12 @@ impl Connect for HttpConnector {
|
||||
type Future = HttpConnecting;
|
||||
|
||||
fn connect(&self, dst: Destination) -> Self::Future {
|
||||
trace!("Http::connect({:?})", dst.uri);
|
||||
trace!(
|
||||
"Http::connect; scheme={}, host={}, port={:?}",
|
||||
dst.scheme(),
|
||||
dst.host(),
|
||||
dst.port(),
|
||||
);
|
||||
|
||||
if self.enforce_http {
|
||||
if dst.uri.scheme_part() != Some(&Scheme::HTTP) {
|
||||
|
||||
@@ -255,6 +255,8 @@ where C: Connect + Sync + 'static,
|
||||
}
|
||||
})
|
||||
.and_then(move |mut res| {
|
||||
// If pooled is HTTP/2, we can toss this reference immediately.
|
||||
//
|
||||
// when pooled is dropped, it will try to insert back into the
|
||||
// pool. To delay that, spawn a future that completes once the
|
||||
// sender is ready again.
|
||||
@@ -263,7 +265,7 @@ where C: Connect + Sync + 'static,
|
||||
// for a new request to start.
|
||||
//
|
||||
// It won't be ready if there is a body to stream.
|
||||
if pooled.is_ready() {
|
||||
if ver == Ver::Http2 || pooled.is_ready() {
|
||||
drop(pooled);
|
||||
} else if !res.body().is_empty() {
|
||||
let (delayed_tx, delayed_rx) = oneshot::channel();
|
||||
|
||||
@@ -159,7 +159,7 @@ impl<T: Poolable> Pool<T> {
|
||||
}
|
||||
|
||||
pub(super) fn pooled(&self, mut connecting: Connecting<T>, value: T) -> Pooled<T> {
|
||||
let value = match value.reserve() {
|
||||
let (value, pool_ref) = match value.reserve() {
|
||||
Reservation::Shared(to_insert, to_return) => {
|
||||
debug_assert_eq!(
|
||||
connecting.key.1,
|
||||
@@ -174,24 +174,45 @@ impl<T: Poolable> Pool<T> {
|
||||
// prevent the Drop of Connecting from repeating inner.connected()
|
||||
connecting.pool = Weak::new();
|
||||
|
||||
to_return
|
||||
// Shared reservations don't need a reference to the pool,
|
||||
// since the pool always keeps a copy.
|
||||
(to_return, Weak::new())
|
||||
},
|
||||
Reservation::Unique(value) => {
|
||||
// Unique reservations must take a reference to the pool
|
||||
// since they hope to reinsert once the reservation is
|
||||
// completed
|
||||
(value, Arc::downgrade(&self.inner))
|
||||
},
|
||||
Reservation::Unique(value) => value,
|
||||
};
|
||||
Pooled {
|
||||
is_reused: false,
|
||||
key: connecting.key.clone(),
|
||||
pool: Arc::downgrade(&self.inner),
|
||||
pool: pool_ref,
|
||||
value: Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn reuse(&self, key: &Key, value: T) -> Pooled<T> {
|
||||
debug!("reuse idle connection for {:?}", key);
|
||||
// TODO: unhack this
|
||||
// In Pool::pooled(), which is used for inserting brand new connections,
|
||||
// there's some code that adjusts the pool reference taken depending
|
||||
// on if the Reservation can be shared or is unique. By the time
|
||||
// reuse() is called, the reservation has already been made, and
|
||||
// we just have the final value, without knowledge of if this is
|
||||
// unique or shared. So, the hack is to just assume Ver::Http2 means
|
||||
// shared... :(
|
||||
let pool_ref = if key.1 == Ver::Http2 {
|
||||
Weak::new()
|
||||
} else {
|
||||
Arc::downgrade(&self.inner)
|
||||
};
|
||||
|
||||
Pooled {
|
||||
is_reused: true,
|
||||
key: key.clone(),
|
||||
pool: Arc::downgrade(&self.inner),
|
||||
pool: pool_ref,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
@@ -216,15 +237,18 @@ impl<'a, T: Poolable + 'a> IdlePopper<'a, T> {
|
||||
while let Some(entry) = self.list.pop() {
|
||||
// If the connection has been closed, or is older than our idle
|
||||
// timeout, simply drop it and keep looking...
|
||||
//
|
||||
if entry.value.is_closed() {
|
||||
trace!("removing closed connection for {:?}", self.key);
|
||||
continue;
|
||||
}
|
||||
// TODO: Actually, since the `idle` list is pushed to the end always,
|
||||
// that would imply that if *this* entry is expired, then anything
|
||||
// "earlier" in the list would *have* to be expired also... Right?
|
||||
//
|
||||
// In that case, we could just break out of the loop and drop the
|
||||
// whole list...
|
||||
if entry.value.is_closed() || expiration.expires(entry.idle_at) {
|
||||
trace!("remove unacceptable pooled connection for {:?}", self.key);
|
||||
if expiration.expires(entry.idle_at) {
|
||||
trace!("removing expired connection for {:?}", self.key);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -257,10 +281,10 @@ impl<T: Poolable> PoolInner<T> {
|
||||
return;
|
||||
}
|
||||
if key.1 == Ver::Http2 && self.idle.contains_key(&key) {
|
||||
trace!("Pool::put; existing idle HTTP/2 connection for {:?}", key);
|
||||
trace!("put; existing idle HTTP/2 connection for {:?}", key);
|
||||
return;
|
||||
}
|
||||
trace!("Pool::put {:?}", key);
|
||||
trace!("put; add idle connection for {:?}", key);
|
||||
let mut remove_parked = false;
|
||||
let mut value = Some(value);
|
||||
if let Some(parked) = self.parked.get_mut(&key) {
|
||||
@@ -288,7 +312,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
}
|
||||
}
|
||||
|
||||
trace!("Pool::put removing canceled parked {:?}", key);
|
||||
trace!("put; removing canceled waiter for {:?}", key);
|
||||
}
|
||||
remove_parked = parked.is_empty();
|
||||
}
|
||||
@@ -306,7 +330,7 @@ impl<T: Poolable> PoolInner<T> {
|
||||
idle_at: Instant::now(),
|
||||
});
|
||||
}
|
||||
None => trace!("Pool::put found parked {:?}", key),
|
||||
None => trace!("put; found waiter for {:?}", key),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,23 +369,26 @@ impl<T> PoolInner<T> {
|
||||
}
|
||||
|
||||
impl<T: Poolable> PoolInner<T> {
|
||||
/// This should *only* be called by the IdleInterval.
|
||||
fn clear_expired(&mut self) {
|
||||
let dur = if let Some(dur) = self.timeout {
|
||||
dur
|
||||
} else {
|
||||
return
|
||||
};
|
||||
let dur = self.timeout.expect("interval assumes timeout");
|
||||
|
||||
let now = Instant::now();
|
||||
//self.last_idle_check_at = now;
|
||||
|
||||
self.idle.retain(|_key, values| {
|
||||
|
||||
self.idle.retain(|key, values| {
|
||||
values.retain(|entry| {
|
||||
if entry.value.is_closed() {
|
||||
trace!("idle interval evicting closed for {:?}", key);
|
||||
return false;
|
||||
}
|
||||
now - entry.idle_at < dur
|
||||
if now - entry.idle_at > dur {
|
||||
trace!("idle interval evicting expired for {:?}", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, keep this value...
|
||||
true
|
||||
});
|
||||
|
||||
// returning false evicts this key/val
|
||||
@@ -459,9 +486,11 @@ impl<T: Poolable> Drop for Pooled<T> {
|
||||
if let Ok(mut inner) = inner.lock() {
|
||||
inner.put(self.key.clone(), value);
|
||||
}
|
||||
} else {
|
||||
} else if self.key.1 == Ver::Http1 {
|
||||
trace!("pool dropped, dropping pooled ({:?})", self.key);
|
||||
}
|
||||
// Ver::Http2 is already in the Pool (or dead), so we wouldn't
|
||||
// have an actual reference to the Pool.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user