@@ -8,6 +8,7 @@ use diesel::IntoSql;
88
99use diesel_async:: pooled_connection:: { PoolError as DieselPoolError , PoolableConnection } ;
1010use diesel_async:: { AsyncConnection , RunQueryDsl } ;
11+ use graph:: env:: ENV_VARS ;
1112use graph:: prelude:: error;
1213use graph:: prelude:: Counter ;
1314use graph:: prelude:: Gauge ;
@@ -237,6 +238,37 @@ pub(crate) fn spawn_size_stat_collector(
237238 } ) ;
238239}
239240
241+ /// Reap connections that are too old (older than 30 minutes) or if there
242+ /// are more than `connection_min_idle` connections in the pool that have
243+ /// been idle for longer than `idle_timeout`
244+ pub ( crate ) fn spawn_connection_reaper ( pool : AsyncPool , idle_timeout : Duration ) {
245+ const MAX_LIFETIME : Duration = Duration :: from_secs ( 30 * 60 ) ;
246+ let Some ( min_idle) = ENV_VARS . store . connection_min_idle else {
247+ // If this is None, we will never reap anything
248+ return ;
249+ } ;
250+ // What happens here isn't exactly what we would like to have: we would
251+ // like to have at any point `min_idle` unused connections in the pool,
252+ // but there is no way to achieve that with deadpool. Instead, we try to
253+ // keep `min_idle` connections around if they exist
254+ tokio:: task:: spawn ( async move {
255+ loop {
256+ let mut idle_count = 0 ;
257+ pool. retain ( |_, metrics| {
258+ if metrics. age ( ) > MAX_LIFETIME {
259+ return false ;
260+ }
261+ if metrics. last_used ( ) > idle_timeout {
262+ idle_count += 1 ;
263+ return idle_count <= min_idle;
264+ }
265+ true
266+ } ) ;
267+ tokio:: time:: sleep ( Duration :: from_secs ( 30 ) ) . await ;
268+ }
269+ } ) ;
270+ }
271+
240272pub ( crate ) struct WaitMeter {
241273 wait_gauge : Gauge ,
242274 pub ( crate ) wait_stats : PoolWaitStats ,
0 commit comments