Problem
During the OpenSearch migration, index bootstrap is not idempotent against an orphaned cluster index — an index that physically exists in the cluster but is missing from the dotCMS index store.
This happens when a previous startup created a physical index but never committed its store pointer (e.g. the OS VersionedIndices row was left uncommitted after a partial/interrupted init, or the OS live create / pointOS failed mid-bootstrap).
Observed symptoms (Phase 1)
-
On restart, the catchup path re-derives the same logical name and the create fails:
org.opensearch.client.opensearch._types.OpenSearchException: Request failed:
[resource_already_exists_exception] index [cluster_XXXX.working_YYYYYYYY.os/...] already exists
at com.dotcms.content.index.opensearch.OSIndexAPIImpl.createIndex(OSIndexAPIImpl.java)
at ...ContentletIndexAPIImpl.bootstrapAndPointOS(...)
at ...ContentletIndexAPIImpl.initOSCatchup(...)
at ...ContentletIndexAPIImpl.checkAndInitializeIndex(...)
The exception bubbles to checkAndInitializeIndex() and is swallowed, leaving the instance half-initialised.
-
Because init aborted, the read provider then queries an index that was never finished, surfacing as:
Elasticsearch SEARCH error ... search_phase_execution_exception, reason=all shards failed
Root cause
ContentletIndexAPIImpl.createContentIndex(name, shards, tag) (used only by the ES/OS bootstrap paths) always issues a create. The indexReadyOS()/indexReadyES() checks key off the store (VersionedIndices / indicies), not the physical cluster state, so when the two diverge the bootstrap retries a create that can never succeed.
Fix
Make createContentIndex(name, shards, tag) idempotent: if the physical index already exists, reuse it, re-assert the custom mapping (putMapping is additive), and let the caller's point() re-register it in the store instead of issuing a failing create.
Follow-ups (out of scope of the fix PR)
Problem
During the OpenSearch migration, index bootstrap is not idempotent against an orphaned cluster index — an index that physically exists in the cluster but is missing from the dotCMS index store.
This happens when a previous startup created a physical index but never committed its store pointer (e.g. the OS
VersionedIndicesrow was left uncommitted after a partial/interrupted init, or the OSlivecreate /pointOSfailed mid-bootstrap).Observed symptoms (Phase 1)
On restart, the catchup path re-derives the same logical name and the create fails:
The exception bubbles to
checkAndInitializeIndex()and is swallowed, leaving the instance half-initialised.Because init aborted, the read provider then queries an index that was never finished, surfacing as:
Root cause
ContentletIndexAPIImpl.createContentIndex(name, shards, tag)(used only by the ES/OS bootstrap paths) always issues a create. TheindexReadyOS()/indexReadyES()checks key off the store (VersionedIndices/indicies), not the physical cluster state, so when the two diverge the bootstrap retries a create that can never succeed.Fix
Make
createContentIndex(name, shards, tag)idempotent: if the physical index already exists, reuse it, re-assert the custom mapping (putMappingis additive), and let the caller'spoint()re-register it in the store instead of issuing a failing create.Follow-ups (out of scope of the fix PR)
checkAndInitializeIndex()swallows bootstrap failures and leaves the instance running half-initialised — should fail loudly instead.ContentFactoryIndexOperationsESlogs only the wrapper message onsearch_phase_execution_exception, hiding the per-shardShardSearchFailureroot cause — should surface it.