From 47769981c6fb596f892f0b2ab64c24c8e22b38e9 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 23 Sep 2026 07:38:47 +0000
Subject: [PATCH] [#915] Bound the wait of a write transaction for a row lock another session holds (#1010)
---
opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/CachedConnection.java | 30 +++++++++++++++++++++++++++++-
1 files changed, 29 insertions(+), 1 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/CachedConnection.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/CachedConnection.java
index 516960e..d629d0f 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/CachedConnection.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/CachedConnection.java
@@ -1325,6 +1325,14 @@
private volatile boolean poolable;
/**
+ * The session value the row lock bound of this backend displaces on this connection, read once and
+ * kept for its life: see {@link #rowLockBoundDisplaces()}. Volatile for the reason
+ * {@link #poolable} is - it is written by one borrower and read by the next, which is another
+ * thread, and the value is a plain field of an object the pool hands between them.
+ */
+ private volatile Long rowLockBoundDisplaces;
+
+ /**
* When this connection last answered the database, as a {@link System#nanoTime()} reading:
* established - the login and the two round trips that set it up have just answered - or
* validated. It is never stamped on the way back into the pool, although that is where a
@@ -1369,7 +1377,7 @@
/**
* Keeps this connection out of the pool: it serves the borrower holding it and is closed rather
* than pooled when that borrow ends. For a borrower that left something of its own on the session
- * and could not take it off again - {@code JDBCStorage.restoreDdlLockBound()} is the one that
+ * and could not take it off again - {@code JDBCStorage.restoreLockBound()} is the one that
* does (#885) - where the blast radius is then this one connection instead of every borrow it
* would have served after this one.
*/
@@ -1377,6 +1385,26 @@
poolable = false;
}
+ /**
+ * What this session carried before the row lock bound of {@code JDBCStorage.write()} first
+ * displaced it, or null while that has not been read yet (#915).
+ * <p>
+ * Remembered for the life of the connection because that bound is on the hot path - one write of
+ * the server, one arming - while the readback it saves is a round trip. Only this backend writes
+ * that setting on a connection of this pool, every write puts the value back before the
+ * connection is released, and a connection whose restore failed is kept out of the pool by
+ * {@link #keepOutOfThePool} rather than handed on: so a value remembered here cannot outlive the
+ * session that answered it. Not reset on borrow for the same reason - it describes the session,
+ * which outlives every borrow of it.
+ */
+ Long rowLockBoundDisplaces() {
+ return rowLockBoundDisplaces;
+ }
+
+ void rowLockBoundDisplaces(Long previous) {
+ rowLockBoundDisplaces = previous;
+ }
+
/** Gives back the right to hold this connection, once and only if it was taken. */
void releasePermit() {
if (metered && permitReleased.compareAndSet(false, true)) {
--
Gitblit v1.10.0