Describe the bug
listTrees() of the JDBC backend answers from a lazily populated cache rather than from the database:
// opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java:665
public Set<TreeName> listTrees() {
return tree2table.asMap().keySet();
}
tree2table gains an entry the first time this process names a table for a tree — openTree, read, put, getRecordCount, a cursor. Nothing seeds it: open() only takes a connection and sets the storage status.
removeStorageFiles() is the one caller that runs before the root container is opened, and it drops exactly what that cache names:
// opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendImpl.java:670
if (importConfig.clearBackend())
{
// clear all files before opening the root container
storage.removeStorageFiles();
}
// opendj-server-legacy/src/main/java/org/opends/server/backends/jdbc/JDBCStorage.java:147
final Set<TreeName> trees=listTrees();
if (!trees.isEmpty()) {
... drop table ...
}
In the offline import-ldif tool the backend is configured but never opened — BackendImpl.importLDIF refuses to run when rootContainer != null, so nothing has touched a tree yet — the cache is empty, trees.isEmpty() holds and the drop loop is skipped entirely. Nothing is cleared.
In the online import task the same command does drop the tables: ImportTask disables the backend and then calls importLDIF on the Backend object it already holds, so the JDBCStorage instance is the one that has been serving traffic with a fully populated tree2table — close() does not invalidate it.
JE and PDB enumerate the environment itself (JEStorage.listTrees() -> env.getDatabaseNames()), and the Cassandra backend truncates the single table it names after the backend id, so both honour the contract regardless of process history. JDBC is the only storage whose answer depends on what the process happened to do earlier.
Impact
import-ldif --clearBackend does not clear a JDBC backend when run offline. The trees the import rebuilds are cleared by the importer itself, so the imported entries still come out right and the damage is confined to what the new import never touches:
- the table of a base DN or of an index that is no longer configured keeps its rows for good, where on JE the same command removes the whole backend directory;
- the compressed-schema tables survive an operation documented to clear everything;
- the same command behaves differently offline and online, so the leftovers are hard to attribute to anything.
Expected behavior
removeStorageFiles() clears the backend's tables whether or not this process has opened the backend, and listTrees() answers from the database rather than from a per-process cache.
The naming makes this harder than it looks: a table is opendj_<sha224 of the tree name>, so the catalog cannot be filtered by a per-backend prefix and a hash cannot be turned back into a TreeName. Two ways out:
- keep the mapping in the database — a small
opendj_trees(tree_name, table_name) row written by openTree() and read back by listTrees(), which also gives backendstat a real answer before the root container is open;
- or derive the table name from the backend id and the tree name, so that the backend's tables can be enumerated from the catalog by prefix.
Environment
master (5.2.x), JDBC backend, all four dialects.
Found while reviewing #881 (#873).
Describe the bug
listTrees()of the JDBC backend answers from a lazily populated cache rather than from the database:tree2tablegains an entry the first time this process names a table for a tree —openTree,read,put,getRecordCount, a cursor. Nothing seeds it:open()only takes a connection and sets the storage status.removeStorageFiles()is the one caller that runs before the root container is opened, and it drops exactly what that cache names:In the offline
import-ldiftool the backend is configured but never opened —BackendImpl.importLDIFrefuses to run whenrootContainer != null, so nothing has touched a tree yet — the cache is empty,trees.isEmpty()holds and the drop loop is skipped entirely. Nothing is cleared.In the online import task the same command does drop the tables:
ImportTaskdisables the backend and then callsimportLDIFon theBackendobject it already holds, so theJDBCStorageinstance is the one that has been serving traffic with a fully populatedtree2table—close()does not invalidate it.JE and PDB enumerate the environment itself (
JEStorage.listTrees()->env.getDatabaseNames()), and the Cassandra backend truncates the single table it names after the backend id, so both honour the contract regardless of process history. JDBC is the only storage whose answer depends on what the process happened to do earlier.Impact
import-ldif --clearBackenddoes not clear a JDBC backend when run offline. The trees the import rebuilds are cleared by the importer itself, so the imported entries still come out right and the damage is confined to what the new import never touches:Expected behavior
removeStorageFiles()clears the backend's tables whether or not this process has opened the backend, andlistTrees()answers from the database rather than from a per-process cache.The naming makes this harder than it looks: a table is
opendj_<sha224 of the tree name>, so the catalog cannot be filtered by a per-backend prefix and a hash cannot be turned back into aTreeName. Two ways out:opendj_trees(tree_name, table_name)row written byopenTree()and read back bylistTrees(), which also givesbackendstata real answer before the root container is open;Environment
master (5.2.x), JDBC backend, all four dialects.
Found while reviewing #881 (#873).