Skip to content

Commit d4d2109

Browse files
goffrieConvex, Inc.
authored and
Convex, Inc.
committed
Defer locking the lease until end of postgres transaction (#37911)
`SELECT FOR SHARE` is implemented using a blocking lock. However we would like to allow newly loaded databases to steal the lease instead of blocking, so let's try to make the length of the critical section shorter. GitOrigin-RevId: a4ec408c15e7b69e2ecfba5b9496c6bf13f4951e
1 parent e495504 commit d4d2109

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

crates/postgres/src/lib.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ use deadpool_postgres::{
9292
Pool,
9393
};
9494
use futures::{
95-
future::Either,
95+
future::{
96+
self,
97+
Either,
98+
},
9699
pin_mut,
97100
stream::{
98101
self,
@@ -1282,18 +1285,32 @@ impl Lease {
12821285
{
12831286
let mut client = self.pool.get_connection("transact", &self.schema).await?;
12841287
let tx = client.transaction().await?;
1288+
let lease_ts = self.lease_ts;
1289+
1290+
let advisory_lease_check = async {
1291+
let timer = metrics::lease_check_timer();
1292+
let stmt = tx.prepare_cached(ADVISORY_LEASE_CHECK).await?;
1293+
let rows = tx.query(&stmt, &[&lease_ts]).await?;
1294+
if rows.len() != 1 {
1295+
return Err(LeaseLostError {}.into());
1296+
}
1297+
timer.finish();
1298+
Ok(())
1299+
};
1300+
1301+
let ((), result) = future::try_join(advisory_lease_check, f(&tx)).await?;
12851302

1303+
// We don't run SELECT FOR UPDATE until the *end* of the transaction
1304+
// to minimize the time spent holding the row lock, and therefore allow
1305+
// the lease to be stolen as much as possible.
12861306
let timer = metrics::lease_precond_timer();
12871307
let stmt = tx.prepare_cached(LEASE_PRECOND).await?;
1288-
let lease_ts = self.lease_ts;
12891308
let rows = tx.query(&stmt, &[&lease_ts]).await?;
12901309
if rows.len() != 1 {
12911310
return Err(LeaseLostError {}.into());
12921311
}
12931312
timer.finish();
12941313

1295-
let result = f(&tx).await?;
1296-
12971314
let timer = metrics::commit_timer();
12981315
tx.commit().await?;
12991316
timer.finish();
@@ -1700,6 +1717,9 @@ const UNSET_READ_ONLY: &str = "DELETE FROM @db_name.read_only WHERE id = 1";
17001717
// If this query returns a result, the lease is still valid and will remain so
17011718
// until the end of the transaction.
17021719
const LEASE_PRECOND: &str = "SELECT 1 FROM @db_name.leases WHERE id=1 AND ts=$1 FOR SHARE";
1720+
// Checks if we still hold the lease without blocking another instance from
1721+
// stealing it.
1722+
const ADVISORY_LEASE_CHECK: &str = "SELECT 1 FROM @db_name.leases WHERE id=1 AND ts=$1";
17031723

17041724
// Acquire the lease unless acquire by someone with a higher timestamp.
17051725
const LEASE_ACQUIRE: &str = "UPDATE @db_name.leases SET ts=$1 WHERE id=1 AND ts<$1";

crates/postgres/src/metrics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,15 @@ pub fn lease_acquire_timer() -> StatusTimer {
306306
StatusTimer::new(&POSTGRES_LEASE_ACQUIRE_SECONDS)
307307
}
308308

309+
register_convex_histogram!(
310+
POSTGRES_ADVISORY_LEASE_CHECK_SECONDS,
311+
"Time to check lease is still held at the start of a transaction",
312+
&STATUS_LABEL
313+
);
314+
pub fn lease_check_timer() -> StatusTimer {
315+
StatusTimer::new(&POSTGRES_ADVISORY_LEASE_CHECK_SECONDS)
316+
}
317+
309318
register_convex_histogram!(
310319
POSTGRES_LEASE_PRECOND_SECONDS,
311320
"Time to check lease precondition",

0 commit comments

Comments
 (0)