Skip to content

[#730] Fix import/export context leak on failed initializeRemote validation#731

Merged
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:fix/issue-730-iectx-leak
Jul 11, 2026
Merged

[#730] Fix import/export context leak on failed initializeRemote validation#731
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:fix/issue-730-iectx-leak

Conversation

@vharseko

Copy link
Copy Markdown
Member

Fixes #730.

Problem

InitOnLineTest failed on CI with a 3-test cascade (ubuntu-latest/11 leg of
PR #714's run, unrelated to it), all in per-test cleanup:

afterTest:1312 ReplicationDomain: Import/Export is not expected to be running
               expected [false] but found [true]

The trigger was a benign topology race inside initializeTargetExportMultiSS:
the InitializeTarget task started before DS(1) had learned about the just
connected DS(2), and failed with "Cannot start total update … the remote
directory server DS(2) is unknown"
. What was not benign is what it left
behind.

Root cause 1 (product): import/export context leak

ReplicationDomain.initializeRemote() acquired the import/export context
before validating the request, and both validation errors
(ERR_FULL_UPDATE_NO_REMOTES, ERR_FULL_UPDATE_MISSING_REMOTE) escaped
without releasing it. The only release site was not exception-safe and even
carried an upstream FIXME:

releaseIEContext(); // FIXME should not this be in a finally?

Once leaked, ieRunning() stays true forever and acquireIEContext()'s
compareAndSet can never succeed again: the domain rejects every
subsequent total update as ERR_SIMULTANEOUS_IMPORT_EXPORT_REJECTED until
the server is restarted.
In production a single dsreplication initialize
pointed at a not-yet-known server id permanently poisons the domain.

Fix: validate the request before acquiring the context, and release the
context in a finally around the extracted export body (resolving the
FIXME). The export body itself is unchanged — moved to a private overload so
the existing logic keeps its shape.

Root cause 2 (test): afterTest asserted before cleaning up

InitOnLineTest.afterTest() asserted ieRunning() before any cleanup;
on failure it leaked the domain config, both brokers and all three
replication servers into the following tests — turning one flake into the
observed 3-test cascade. The assertion now runs after the cleanup, and the
tests that schedule InitializeTarget tasks first wait for the target
replica to appear in getReplicaInfos(), closing the trigger race as well.

Tests

  • InitOnLineTest.initializeTargetUnknownRemote (new) — schedules an
    InitializeTarget task for a server id unknown to the domain, waits for
    STOPPED_BY_ERROR, and asserts the import/export context was released.
    Verified failing without the product fix with exactly the CI signature
    (expected [false] but found [true], deterministic), green with it.
  • Full InitOnLineTest class: 10/10, 0 failures locally (reruns
    disabled).

…nitializeRemote validation

ReplicationDomain.initializeRemote() acquired the import/export context
before validating the request: when validation failed ("no remotes" for an
initialize-all, or the target replica unknown to the domain - e.g. an
InitializeTarget task racing topology propagation), the DirectoryException
escaped without releasing the context. ieRunning() then stayed true forever
and the domain rejected every subsequent total update as a simultaneous
import/export until restart. The release site even carried an upstream
"FIXME should not this be in a finally?".

Validate the request before acquiring the context, and release the context
in a finally around the extracted export body, resolving the FIXME.

Also fix InitOnLineTest, where this leak cascaded over the whole class on CI
(3 of the last tests failing in afterTest on the ubuntu-latest/11 leg of PR
714's run): afterTest asserted ieRunning() before any cleanup, leaking the
domain config, brokers and three replication servers into the following
tests. Assert after the cleanup instead, wait for the target replica to
appear in the topology before scheduling InitializeTarget tasks, and add
initializeTargetUnknownRemote which reproduces the leak deterministically -
verified failing without the product fix and green with it (full class
10/10).
@vharseko vharseko added bug java Pull requests that update java code replication tests Test suites: fixing, enabling, un-disabling labels Jul 10, 2026
@vharseko vharseko requested a review from maximthomas July 10, 2026 20:10
…dentityPlatform#731 CI)

addTask() waits for the added task to reach RUNNING, tolerating a task fast
enough to have already COMPLETED_SUCCESSFULLY - but not one fast enough to
have already STOPPED_BY_ERROR. initializeTargetUnknownRemote's task fails
its validation within milliseconds (by design), so on a fast CI runner the
RUNNING phase may never be observed: the ubuntu-latest/11 leg failed with
"Expected task state:RUNNING ... found [STOPPED_BY_ERROR]".

Accept both terminal states in the RUNNING wait; callers interested in the
final state already wait for it explicitly afterwards (the only direct
RUNNING waiter outside addTask does exactly that).
@vharseko

Copy link
Copy Markdown
Member Author

@maximthomas please review

@maximthomas

Copy link
Copy Markdown
Contributor

LGTM

@vharseko vharseko merged commit 06e91a1 into OpenIdentityPlatform:master Jul 11, 2026
17 checks passed
@vharseko vharseko deleted the fix/issue-730-iectx-leak branch July 11, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Pull requests that update java code replication tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

initializeRemote() leaks import/export context on failed validation: domain permanently rejects total updates (InitOnLineTest CI cascade)

2 participants