Skip to content

[#993] Register an entry container's configuration listeners only once it has opened - #999

Open
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issue-993-entry-container-listeners-on-open
Open

[#993] Register an entry container's configuration listeners only once it has opened#999
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issue-993-entry-container-listeners-on-open

Conversation

@vharseko

@vharseko vharseko commented Sep 9, 2026

Copy link
Copy Markdown
Member

Fixes #993.

The defect

EntryContainer registered itself and its two configuration managers as listeners of the backend
configuration from its constructor, and only close() takes them off again. open() caught
StorageRuntimeException alone, while it is declared to throw ConfigException and really does -
an index type the attribute has no matching rule for, an index protecting both its keys and its
values, and (the most reachable of the three) a VLV filter or sort order which does not parse.
Neither RootContainer.openEntryContainer nor openAndRegisterEntryContainers catches it either,
so the container is registered nowhere and nothing will ever call its close().

Two things in the report needed correcting, and both are in the analysis on the
issue
: the
failure is not a NullPointerException - id2entry is assigned by the first statement of the
try - and it is more than five listeners, since every index the failed open got through
registered one of its own. What it costs is worse than an NPE:
ConfigurationHandler.replaceEntry asks every listener on the backend entry whether a change is
acceptable and a single false rejects the whole modify, so an abandoned container can veto a
change on the live backend; and past that gate the entry is already stored, so a failure from one
listener turns a change the live container applied into ERR_CONFIG_FILE_MODIFY_APPLY_FAILED.

The change

EntryContainer

  • the five registrations move from the constructor to the end of a successful open(). A container
    which did not open is not one a configuration change has anything to be applied to, and nothing
    can reach it in between: open() is called before anything holds it.
  • open() catches every failure rather than the storage ones alone, so a ConfigException also
    goes through close().
  • each index is held in its map before it is opened. An attribute index registers its listener at
    the end of open() and a VLV index from its constructor, so the one being opened was not yet one
    close() could find - the hole the StorageRuntimeException catch already had.

RootContainer

  • a failed open() gives back what it took: the entry containers it registered, its own listener,
    and the storage - the last only when this call is what opened it, since a read only root
    container is opened over the very storage instance the backend holds. Nothing else reclaims any
    of it: newRootContainer throws the instance away and BackendConfigManager releases the shared
    lock without calling closeBackend() for a backend which never opened, so a volume left open
    here is one no later attempt to enable that backend can take.
  • openAndRegisterEntryContainers runs inside the write Storage.write may replay, so it gives up
    what a rolled back attempt registered before opening again. Without it an ordinary write-write
    conflict during startup fails the backend with ERR_ENTRY_CONTAINER_ALREADY_REGISTERED and
    leaves that attempt's containers registered - the same shape BackendImpl.changeBaseDNTrees
    already uses for the containers it opens inside a write.

The base DN path needs nothing of its own: a container whose open() throws now closes itself, so
the container changeBaseDNTrees never gets to put in created is reclaimed too.

Tests

FailedBackendOpenTest, five tests, each watched to fail first:

test what it reported before the change
aBackendWhichFailsToOpenLeavesNothingRegistered the root container, the entry container and both configuration managers left registered
anIndexWhichFailsToOpenLeavesNoListenerBehind /dc=com,dc=b993/vlv.vlv1 left registered
aBackendWhichFailsToOpenGivesBackTheStorageItOpened closes: expected 1, was 0
aRootContainerWhichCouldNotOpenTheStorageDoesNotCloseIt guards the storage a read only container did not open
aReplayedOpenLeavesOneSetOfEntryContainers An entry container named 'dc=com,dc=b993' is alreadly registered

Run green with them: PDBTestCase 35/35, EncryptedPDBTestCase 35/35, ReplayedConfigChangeTest
12/12, PDBStorageTest 10/10, OnDiskMergeImporterTest 29/29, PersistentCompressedSchemaTest,
DN2IDTest, StateTest, ID2EntryTest, ID2ChildrenCountTest, BulkCursorTest,
DefaultIndexTest.

…n listeners only once it has opened

EntryContainer registered itself and its two configuration managers from its
constructor, and only close() takes them off again. open() caught
StorageRuntimeException alone, so a ConfigException - a VLV filter or sort order
which does not parse, an index type the attribute has no matching rule for - left
a container nothing holds a reference to, registered on the configuration of a
backend which did not start.

Register the five at the end of a successful open() instead, and catch every
failure there rather than the storage ones alone. Hold each index in its map
before opening it: an attribute index registers its listener at the end of
open() and a VLV index from its constructor, so the one being opened was not yet
one close() could find - the hole the existing catch already had.

RootContainer gives back what a failed open took: the entry containers it
registered, its own listener and the storage, the last only when this call is
what opened it. openAndRegisterEntryContainers runs inside a write the storage
may replay, so it now gives up what a rolled back attempt registered before
opening again; without it a write-write conflict failed the backend with
ERR_ENTRY_CONTAINER_ALREADY_REGISTERED.

Fixes OpenIdentityPlatform#993
@vharseko
vharseko requested a review from maximthomas September 9, 2026 13:59
@vharseko vharseko added bug tests Test suites: fixing, enabling, un-disabling labels Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A ConfigException while opening an EntryContainer leaves its five configuration listeners registered on a half-open container

1 participant