Skip to content

[#861] Fail fast when a total update request gets no answer - #864

Merged
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:issues/861-replication-init-hang
Aug 12, 2026
Merged

[#861] Fail fast when a total update request gets no answer#864
vharseko merged 2 commits into
OpenIdentityPlatform:masterfrom
vharseko:issues/861-replication-init-hang

Conversation

@vharseko

Copy link
Copy Markdown
Member

Refs #861 (root-cause analysis and the fourth-bug addendum are in the issue comments).

dsreplication enable/initialize can hang forever in the window between publishing the InitializeRequestMsg and receiving the InitializeTargetMsg: nothing bounds that wait, and both the request and its error answer can be lost silently. A CI occurrence held a runner for 6 hours (mitigated by #862). Four fixes:

  1. Do not lose the requestReplicationDomain.initializeFromRemote() and its retry path now check the result of broker.publish(...): a request silently dropped by a broker caught between two sessions (connection error, recovery pending after a reconnect) fails the task immediately with ERR_INITIALIZATION_FAILED_NOCONN instead of leaving it waiting forever. The broker's resend-on-reconnect procedure only replays UpdateMsgs, so nothing else would ever deliver that request.
  2. Stalled-request watchdog — new ReplicationDomain.abortStalledInitializeFromRemote(timeoutMs), polled every second by InitializeTask while it waits: if neither InitializeTargetMsg nor ErrorMsg arrives within 2 minutes of the request, the task fails with ERR_NO_REACHABLE_PEER_IN_THE_DOMAIN (which the dsreplication CLI already retries). A late InitializeTargetMsg racing the abort is detected under the context's monitor and ignored by the listener, so the abort cannot race the start of an import.
  3. Same-millisecond ErrorMsg gate — both staleness gates (errorMsg.getCreationTime() > ieCtx.startTime) become >=: with all servers on one host the whole request/rejection round-trip can complete within the millisecond the import/export context was created in, and the strict comparison discarded the legitimate answer as stale — the exact single-host CI topology where the hang was observed.
  4. setExceptionIfNoneSet never set anything — the null check was made on the argument instead of the field (upstream bug present since the 2014 "Recreate OpenDJ 3 development branch" import). Every error funneled through it was silently dropped: an accepted ErrorMsg, a failed LDIF import (partial data, no generation-id recomputation) and mid-import disconnections all ended with the initialize task reporting COMPLETED_SUCCESSFULLY.

New regression tests in ReplicationDomainTest:

  • errorMsgFromSameMillisecondTerminatesPendingInitialize — a crafted ErrorMsg timestamped one millisecond before the context stays ignored, one timestamped in the same millisecond terminates the pending initialization with the right error (covers fixes 3 and 4).
  • stalledInitializeFromRemoteIsAborted — a peer that never answers the request: the watchdog refuses to abort before the delay, aborts after it, fails the task with the peer-unreachable error, releases the import/export context, and a second call is a no-op.

Test results: ReplicationDomainTest 12/12, InitOnLineTest 10/10 (the end-to-end online-initialization suite most sensitive to fix 4).

… no answer

Four fixes for the dsreplication enable/initialize hang and its silent
failure modes:
- initializeFromRemote and its retry now fail the task immediately when
  the broker cannot publish the InitializeRequestMsg instead of silently
  dropping it and waiting forever;
- InitializeTask arms a watchdog that aborts the initialization when
  neither InitializeTargetMsg nor ErrorMsg arrives within 2 minutes;
- the ErrorMsg staleness gates accept answers created in the same
  millisecond as the import/export context (single-host round-trips);
- setExceptionIfNoneSet checked the argument instead of the field for
  null (upstream bug since 2014), so every failed total update reported
  success over partially imported data.
@vharseko
vharseko requested a review from maximthomas August 12, 2026 06:57
@vharseko vharseko added replication bug concurrency Thread-safety / race-condition bugs data-loss Data integrity / loss of entries tests Test suites: fixing, enabling, un-disabling labels Aug 12, 2026
…roughout initialize()

CodeQL flagged four may-be-null dereferences of ieCtx in initialize()
after the stalled-watchdog guard was added. Three were real on the
remotely-initiated import path: when acquireIEContext() rejected a
concurrent import/export, the catch/finally blocks dereferenced a null
ieCtx - or, now that setExceptionIfNoneSet() actually records errors,
poisoned and released the context of the unrelated on-going operation.

Acquire (or validate) the context before entering the import
try/finally so ieCtx is final and provably non-null; a rejected
initialization now answers the exporter with an ErrorMsg and leaves
the on-going operation's context untouched. Also collapse the
duplicated 3A Systems Portions lines in the file header.
@vharseko
vharseko merged commit 641ff2c into OpenIdentityPlatform:master Aug 12, 2026
18 checks passed
@vharseko
vharseko deleted the issues/861-replication-init-hang branch August 12, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug concurrency Thread-safety / race-condition bugs data-loss Data integrity / loss of entries replication tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dsreplication enable hangs initializing registration information when adding a third replica; CI Test replication step has no timeout

2 participants