From 8ad97160e90f47e5fffe0b8a8c65ee421fa0903c Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 22 Sep 2026 09:08:37 +0000
Subject: [PATCH] [#933] Read a catalog table another session created while this one was creating it (#1005)
---
opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java | 35 +++++++++++++++++++++++++++--------
1 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java
index b323570..55cb13c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java
@@ -4724,8 +4724,9 @@
* <p>
* Serialized on the storage, so that two transactions opening trees at the same time cannot both
* find the table absent and both go on to create it. It serializes this storage and nothing
- * else, which is why the create tolerates a table that turned up while it was being made: an
- * offline tool beside a running server is a pair no lock of one process can order. The stamp -
+ * else, which is why the create tolerates a table that turned up while it was being made - an
+ * offline tool beside a running server is a pair no lock of one process can order - and why what
+ * it tolerated is then read like any other catalog that was already there. The stamp -
* the one thing under it that is nobody's dependency - is issued outside it.
* <p>
* Two things are kept out of the lock because they are the slow ones. The flag is read before
@@ -4760,11 +4761,24 @@
if (!lostTheRace) {
if (isExistsTable(catalog)) {
readEnrolledTrees(catalog);
- } else {
- createCatalogTable(catalog);
- // nothing to read from a table that has just been created, and nothing this open
- // enrols may be skipped as already recorded
+ } else if (!createCatalogTable(catalog)) {
+ // the table was there after all: another session created it while this one was
+ // creating it, and a table this session did not create is a catalog with rows in it
+ // like any other - the branch above is what those rows are for. The flag below is
+ // raised over what this storage knows the catalog records, and raised over none of
+ // it, every tree of this open is enrolled again against a catalog already naming
+ // them: one upsert and one commit each, and the same for every later write of this
+ // open that names a tree (#933). It reads on the session the failed create reset,
+ // which is a connection rolled back or, where even that failed, one given up for
+ // the read to establish again: either is a session a select may be asked of. That
+ // re-establish is the one connect of this class made under the lock: it needs a
+ // create cut by a dead catalog connection, a table another session made meanwhile,
+ // and a database refusing logins, and it is bounded by the deadline of a borrow
+ // like the connect made outside it
+ readEnrolledTrees(catalog);
}
+ // and where the create really did create it, there is nothing to read: a table just
+ // made holds no row, and nothing this open enrols may be skipped as already recorded
catalogTableOpened=true;
}
}
@@ -4856,8 +4870,12 @@
* line. What is left is the privilege the account is missing. An engine this backend does not
* know has no number a lock could be told by ({@code isLockTimeout()} answers false on a null
* dialect), and gets the privilege line there as it did.
+ *
+ * @return whether this session created the table. {@code false} says another session created it
+ * while this one was creating it, which is a table full of rows this storage has not
+ * read: what {@link #openCatalog} does with that answer is read them (#933).
*/
- void createCatalogTable(TreeName catalog) {
+ boolean createCatalogTable(TreeName catalog) {
final String tableName=getTableName(catalog);
Dialect dialect=null; // read inside the try, and asked again by the catch, which tells a lock by the engine's own number
try {
@@ -4878,6 +4896,7 @@
catalogCon.commit();
return null;
});
+ return true;
} catch (SQLException | RuntimeException e) {
// the unchecked one as well, for the reason enrolInCatalog() takes it: what the statement
// left behind has to be rolled back whatever class the failure arrived in, this connection
@@ -4901,7 +4920,7 @@
if (alreadyThere) {
logger.debug(LocalizableMessage.raw("jdbc: table %s was created by another session while this one was creating it: %s",
tableName, stackTraceToSingleLineString(e)));
- return;
+ return false; // read by the caller, the rows being another session's and not this one's
}
// A lock another session holds is not a privilege the account lacks, whichever bound ended
// the wait for it: asked of the engine's own number on every road, since gaveUpOnTheLock()
--
Gitblit v1.10.0