Skip to content

Commit 01462e8

Browse files
authored
fix: isolate epoch/extension polling onto dedicated pool (#251)
Add a separate 1-connection poll_pool used exclusively for extension- existence checks and epoch sentinel heartbeats. This prevents activity work (graph loading, status updates) on the management pool from starving the health-check loop and causing spurious runtime shutdowns under load.
1 parent 7ee46cc commit 01462e8

1 file changed

Lines changed: 30 additions & 5 deletions

File tree

src/worker.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ async fn run_duroxide_runtime() {
125125
);
126126

127127
// Management pool: consolidates former polling and activity pools into one.
128-
// Used for extension-existence polling, epoch sentinels, worker-ready writes,
129-
// graph loading, and status updates. Sized by the max_management_connections GUC.
128+
// Used for graph loading and status updates. Sized by the max_management_connections GUC.
130129
// Retry in a loop so the worker survives the target database not yet existing
131130
// (e.g. pg_regress creates `contrib_regression` after PostgreSQL starts).
132131
let mgmt_pool = loop {
@@ -150,13 +149,38 @@ async fn run_duroxide_runtime() {
150149
}
151150
};
152151

152+
// Dedicated polling pool: a separate 1-connection pool used exclusively for
153+
// extension-existence checks and epoch sentinel heartbeats. This isolation
154+
// prevents activity work (graph loading, status updates) from starving the
155+
// health-check loop and causing spurious runtime shutdowns under high load.
156+
let poll_pool = loop {
157+
if is_shutdown_requested() {
158+
log!("pg_durable: shutdown requested before poll pool created, exiting");
159+
return;
160+
}
161+
match sqlx::postgres::PgPoolOptions::new()
162+
.max_connections(1)
163+
.connect(&pg_conn_str)
164+
.await
165+
{
166+
Ok(pool) => break pool,
167+
Err(e) => {
168+
log!(
169+
"pg_durable: failed to create poll pool (will retry in 5s): {}",
170+
e
171+
);
172+
tokio::time::sleep(Duration::from_secs(5)).await;
173+
}
174+
}
175+
};
176+
153177
loop {
154178
if is_shutdown_requested() {
155179
log!("pg_durable: shutdown requested, exiting");
156180
break;
157181
}
158182

159-
if !wait_for_extension_creation(&mgmt_pool, WAIT_FOR_EXTENSION_POLL_INTERVAL).await {
183+
if !wait_for_extension_creation(&poll_pool, WAIT_FOR_EXTENSION_POLL_INTERVAL).await {
160184
break;
161185
}
162186

@@ -191,7 +215,7 @@ async fn run_duroxide_runtime() {
191215

192216
// Write a sentinel so we can detect drop+recreate even if the
193217
// extension is always present in pg_extension between polls.
194-
let epoch_id = match write_epoch_sentinel(&mgmt_pool).await {
218+
let epoch_id = match write_epoch_sentinel(&poll_pool).await {
195219
Ok(id) => {
196220
log!("pg_durable: epoch sentinel written ({})", id);
197221
Some(id)
@@ -203,7 +227,7 @@ async fn run_duroxide_runtime() {
203227
};
204228

205229
run_until_extension_dropped_or_shutdown(
206-
&mgmt_pool,
230+
&poll_pool,
207231
duroxide_runtime,
208232
EXTENSION_DROP_POLL_INTERVAL,
209233
SHUTDOWN_CHECK_INTERVAL,
@@ -213,6 +237,7 @@ async fn run_duroxide_runtime() {
213237
}
214238

215239
mgmt_pool.close().await;
240+
poll_pool.close().await;
216241
}
217242

218243
async fn wait_for_extension_creation(poll_pool: &sqlx::PgPool, poll_interval: Duration) -> bool {

0 commit comments

Comments
 (0)