Skip to content

OpenSearch: index bootstrap not idempotent — orphaned cluster index aborts startup #36237

Description

@fabrizzio-dotCMS

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)

  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.

  2. 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)

  • checkAndInitializeIndex() swallows bootstrap failures and leaves the instance running half-initialised — should fail loudly instead.
  • ContentFactoryIndexOperationsES logs only the wrapper message on search_phase_execution_exception, hiding the per-shard ShardSearchFailure root cause — should surface it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    QA

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions