From 655132fb059cfa70fb338d54f1bb0fb48198fb0f Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 02 Sep 2026 06:44:10 +0000
Subject: [PATCH] [#873] Give each backend its own compressed schema trees (#881)
---
opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java | 63 +++++++++++++++++++++++++++++++
1 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java
index 9e784d3..d73da22 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/cassandra/TestCase.java
@@ -42,6 +42,7 @@
import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
import java.net.InetSocketAddress;
import java.util.NoSuchElementException;
@@ -134,6 +135,68 @@
});
}
+ /** A storage under a backend id of its own, so that its table is one no other test has created. */
+ private CASStorage openStorage(String backendId, AccessMode accessMode) throws Exception {
+ final CASBackendCfg backendCfg = mockCfg(CASBackendCfg.class);
+ when(backendCfg.getBackendId()).thenReturn(backendId);
+ when(backendCfg.getDBDirectory()).thenReturn("CASTestCase");
+ final CASStorage storage = new CASStorage(backendCfg, null);
+ storage.open(accessMode);
+ return storage;
+ }
+
+ /**
+ * The compressed schema asks whether a tree is there before anything has been written (#873), so
+ * treeExists() has to answer for a table that openTree() has not created yet. It may only answer
+ * where nothing can follow from the answer: a read has nothing to lose, but a writeable
+ * transaction has just created the table through openTree(), so a query rejected there is a
+ * fault. Reporting a populated tree absent would have the compressed schema allocate its tokens
+ * from zero again and overwrite the definitions its entries were encoded with.
+ */
+ @Test
+ public void testTreeExistsAnswersForAMissingTable() throws Exception {
+ final CASStorage storage = openStorage("CASTreeExists", AccessMode.READ_WRITE);
+ final TreeName tree = new TreeName("testTreeExists", "tree");
+ try {
+ // the keyspace is there - open(READ_WRITE) creates it - but the table is not
+ assertFalse(storage.read(new ReadOperation<Boolean>() {
+ @Override
+ public Boolean run(ReadableTransaction txn) throws Exception {
+ return txn.treeExists(tree);
+ }
+ }), "a read of a table that was never created finds no tree");
+
+ try {
+ storage.write(new WriteOperation() {
+ @Override
+ public void run(WriteableTransaction txn) throws Exception {
+ txn.treeExists(tree);
+ }
+ });
+ fail("a writeable transaction must not report a rejected query as an absent tree");
+ } catch (InvalidQueryException expected) {
+ // the table of a writeable open is created by openTree(), so its absence is a fault
+ }
+
+ // Every tree of this backend is a partition of the one table, so it has no existence of
+ // its own: it is there once it holds a record, and not before.
+ storage.write(new WriteOperation() {
+ @Override
+ public void run(WriteableTransaction txn) throws Exception {
+ txn.openTree(tree, true);
+ assertFalse(txn.treeExists(tree), "an empty partition holds no record");
+ txn.put(tree, key(0), value(0));
+ assertTrue(txn.treeExists(tree));
+ txn.deleteTree(tree);
+ assertFalse(txn.treeExists(tree));
+ }
+ });
+ } finally {
+ dropTree(storage, tree);
+ storage.close();
+ }
+ }
+
private static void dropTree(CASStorage storage, final TreeName tree) {
try {
storage.write(new WriteOperation() {
--
Gitblit v1.10.0