Skip to content

chore: add node/manager/coordinating count checks to OpenSearch readiness probe - #1959

Merged
edwinjosechittilappilly merged 1 commit into
mainfrom
PR-1913-opensearch-health-checks
Jun 24, 2026
Merged

chore: add node/manager/coordinating count checks to OpenSearch readiness probe#1959
edwinjosechittilappilly merged 1 commit into
mainfrom
PR-1913-opensearch-health-checks

Conversation

@edwinjosechittilappilly

@edwinjosechittilappilly edwinjosechittilappilly commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Cherry-pick of #1913 onto main.

The OpenSearch readiness probe (wait_for_opensearch) only checked cluster health status (green/yellow) but not whether the expected number of nodes had actually joined the cluster. On multi-node deployments this caused the startup bootstrap to proceed before the full cluster was formed, leading to flaky behaviour under load.

Changes:

  • src/utils/opensearch_utils.py — after a green/yellow health status, gate on data-node count, cluster-manager count, and coordinating-node count matching configurable thresholds; fall through to retry/backoff if any are short
  • src/config/settings.py — new env vars: OPENSEARCH_NODE_COUNT_CHECK (default true), OPENSEARCH_EXPECTED_DATA_NODE_COUNT (default 3), OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT (default 3), OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT (default 3), OPENSEARCH_WAIT_MAX_RETRIES (default 100)
  • src/utils/opensearch_init.py — forward max_retries from lifespan caller
  • docker-compose.yml, CI workflows — set OPENSEARCH_NODE_COUNT_CHECK=false for single-node local/test clusters
  • tests/unit/test_opensearch_wait_node_count.py — unit tests covering all count-check branches (flag on/off, each count type short)
  • tests/unit/test_opensearch_init_wait_retries.py — unit tests verifying max_retries is forwarded correctly

Summary by CodeRabbit

  • New Features

    • Added configurable OpenSearch readiness checks, including optional node-count validation and retry limits.
    • Enabled a new setting to control whether cluster node counts are verified during startup and test runs.
  • Bug Fixes

    • Improved startup and E2E/integration reliability for single-node test environments by relaxing multi-node readiness checks when appropriate.
    • Increased readiness retry attempts to better handle slower OpenSearch startup.

…1913)

* add node count check

* add manager node check

* style: ruff autofix (auto)

* add coordinating nodes

* style: ruff autofix (auto)

* Update opensearch_utils.py

* style: ruff autofix (auto)

* Update opensearch_utils.py

* add max retries to wait for opensearch

* style: ruff autofix (auto)

* updating count to 3 for nodes

* add check nodes false for OSS and for tests

---------
@github-actions github-actions Bot added backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) ci ⬛ CI/CD, build, and infrastructure issues docker tests labels Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds an optional node-count readiness check to wait_for_opensearch controlled by a new OPENSEARCH_NODE_COUNT_CHECK_ENABLED setting. When enabled, the probe verifies expected counts for data, cluster-manager, and coordinating-only nodes via transport calls. Configurable max_retries is propagated through the call stack. The check is disabled for single-node CI and Docker Compose environments.

Changes

OpenSearch node-count readiness check with configurable retries

Layer / File(s) Summary
New readiness-check settings constants
src/config/settings.py
Defines OPENSEARCH_NODE_COUNT_CHECK_ENABLED, expected node-count thresholds for data, cluster-manager, and coordinating-only nodes, and OPENSEARCH_WAIT_MAX_RETRIES, all read from environment with numeric/boolean defaults.
Node-count probe logic, wrapper update, and lifespan wiring
src/utils/opensearch_utils.py, src/utils/opensearch_init.py, src/app/lifespan.py
Increases default max_retries to 30 and adds conditional transport calls to verify all three node-type counts when the flag is enabled in opensearch_utils. Updates the opensearch_init wrapper to accept and forward max_retries with start/completion logging. Wires OPENSEARCH_WAIT_MAX_RETRIES into the security-bootstrap wait_for_opensearch call in lifespan.py.
Unit tests for node-count probe and retry forwarding
tests/unit/test_opensearch_wait_node_count.py, tests/unit/test_opensearch_init_wait_retries.py
Adds a fake async client helper and five tests covering all-counts-met success, each node-type short count raising OpenSearchNotReadyError, and flag-disabled bypass. Adds two tests asserting the wrapper defaults max_retries to 30 and forwards explicit values.
Disable node-count check in CI and Docker Compose
.github/workflows/test-e2e.yml, .github/workflows/test-integration.yml, docker-compose.yml
Sets OPENSEARCH_NODE_COUNT_CHECK: "false" in the E2E job, integration test step, and docker-compose.yml to suppress the multi-node readiness gate for single-node environments.

Sequence Diagram(s)

sequenceDiagram
  participant lifespan as lifespan.py
  participant init as opensearch_init
  participant utils as opensearch_utils
  participant client as OpenSearch Client

  lifespan->>init: wait_for_opensearch(max_retries=OPENSEARCH_WAIT_MAX_RETRIES)
  init->>utils: _wait_for_opensearch(client, max_retries)
  loop up to max_retries
    utils->>client: cluster.health()
    alt OPENSEARCH_NODE_COUNT_CHECK_ENABLED=true
      utils->>client: transport GET _nodes/data
      utils->>client: transport GET _nodes/cluster_manager
      utils->>client: transport GET _nodes/coordinating_only
      alt all counts met
        utils-->>init: return (ready)
      else count short
        utils->>utils: log warning, retry with backoff
      end
    else OPENSEARCH_NODE_COUNT_CHECK_ENABLED=false
      utils-->>init: return (health-based ready)
    end
  end
  init-->>lifespan: OpenSearch is ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • langflow-ai/openrag#1743: Touches the same wait_for_opensearch call site in src/app/lifespan.py during the OpenSearch security bootstrap, where this PR now passes max_retries=OPENSEARCH_WAIT_MAX_RETRIES.

Suggested labels

enhancement, tests

Suggested reviewers

  • zzzming
  • phact
  • rodageve
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding node-count checks to the OpenSearch readiness probe.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PR-1913-opensearch-health-checks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/app/lifespan.py (1)

296-296: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Confirm the worst-case startup wait is acceptable.

OPENSEARCH_WAIT_MAX_RETRIES defaults to 100 and the underlying backoff caps each delay at max_delay=30s, so an unavailable cluster can block this synchronous bootstrap for up to ~50 minutes before raising. This runs before the rest of startup (the comment at Line 246-250 notes it's intentionally synchronous). Confirm this upper bound is acceptable for your orchestration/liveness timeouts, or consider a tighter cap for the bootstrap path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/lifespan.py` at line 296, The synchronous OpenSearch bootstrap in
wait_for_opensearch can block startup for far too long with the current
OPENSEARCH_WAIT_MAX_RETRIES and capped backoff. Review the startup path in
lifespan.py around the await wait_for_opensearch call and either reduce the
bootstrap retry/max-delay settings for this path or make them configurable so
the worst-case wait is bounded to match your orchestration and liveness
timeouts.
src/utils/opensearch_utils.py (1)

166-184: 🚀 Performance & Scalability | 🔵 Trivial

Move the config.settings import out of the retry loop. The _nodes.successful values already match the filtered node requests, so the node-count check is fine as-is.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/opensearch_utils.py` around lines 166 - 184, Move the
config.settings import out of the retry loop in the OpenSearch node-count check,
keeping the logic in the health-check path unchanged. Update the code around the
node-count validation in opensearch_utils so the OPENSEARCH_EXPECTED_* constants
and OPENSEARCH_NODE_COUNT_CHECK_ENABLED are imported once at module scope or
before the retry block, and leave the existing _nodes.successful handling for
cluster_manager and coordinating_only as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/settings.py`:
- Around line 44-57: The OpenSearch node-count readiness gate in settings is too
strict by default and can block startup for valid topologies. Update the
defaults around OPENSEARCH_NODE_COUNT_CHECK_ENABLED,
OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT, and
OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT so the check is non-blocking unless
explicitly configured, or make the expected counts safe for optional node types.
Also review wait_for_opensearch and the related config constants in settings.py
to ensure the gate only enforces counts when the deployment actually requires
dedicated cluster-manager or coordinating-only nodes, and document the required
tuning.

---

Nitpick comments:
In `@src/app/lifespan.py`:
- Line 296: The synchronous OpenSearch bootstrap in wait_for_opensearch can
block startup for far too long with the current OPENSEARCH_WAIT_MAX_RETRIES and
capped backoff. Review the startup path in lifespan.py around the await
wait_for_opensearch call and either reduce the bootstrap retry/max-delay
settings for this path or make them configurable so the worst-case wait is
bounded to match your orchestration and liveness timeouts.

In `@src/utils/opensearch_utils.py`:
- Around line 166-184: Move the config.settings import out of the retry loop in
the OpenSearch node-count check, keeping the logic in the health-check path
unchanged. Update the code around the node-count validation in opensearch_utils
so the OPENSEARCH_EXPECTED_* constants and OPENSEARCH_NODE_COUNT_CHECK_ENABLED
are imported once at module scope or before the retry block, and leave the
existing _nodes.successful handling for cluster_manager and coordinating_only
as-is.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 337381f3-aee6-4af7-9e15-032a4b5bb57d

📥 Commits

Reviewing files that changed from the base of the PR and between 14ea238 and e666b36.

📒 Files selected for processing (9)
  • .github/workflows/test-e2e.yml
  • .github/workflows/test-integration.yml
  • docker-compose.yml
  • src/app/lifespan.py
  • src/config/settings.py
  • src/utils/opensearch_init.py
  • src/utils/opensearch_utils.py
  • tests/unit/test_opensearch_init_wait_retries.py
  • tests/unit/test_opensearch_wait_node_count.py

Comment thread src/config/settings.py
Comment on lines +44 to +57
OPENSEARCH_NODE_COUNT_CHECK_ENABLED = os.getenv(
"OPENSEARCH_NODE_COUNT_CHECK", "true"
).strip().lower() in ("true", "1", "yes")

# Expected cluster size, used only when the node-count check is enabled.
OPENSEARCH_EXPECTED_DATA_NODE_COUNT = get_env_int("OPENSEARCH_EXPECTED_DATA_NODE_COUNT", 3)
# Minimum reachable cluster-manager (master) nodes, gated by the same flag.
OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT = get_env_int(
"OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT", 3
)
# Minimum reachable coordinating-only nodes, gated by the same flag.
OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT = get_env_int(
"OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT", 3
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Default-enabled node-count gate with expected counts of 3 can permanently block startup.

OPENSEARCH_NODE_COUNT_CHECK_ENABLED defaults to true, while OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT and OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT default to 3. Coordinating-only nodes and dedicated cluster-manager nodes are optional in many OpenSearch topologies. A cluster that does not deploy 3 dedicated coordinating-only nodes (or 3 dedicated cluster-managers) will report coordinating_count/cluster_manager_count below the threshold forever, so wait_for_opensearch will exhaust all retries and raise OpenSearchNotReadyError, failing startup.

The CI/compose overrides disable the flag, but any deployment that doesn't explicitly set OPENSEARCH_NODE_COUNT_CHECK=false inherits this risk. Consider defaulting the flag to false, or defaulting the coordinating/cluster-manager expected counts to a non-blocking value, and documenting that these must be tuned to the actual topology.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/settings.py` around lines 44 - 57, The OpenSearch node-count
readiness gate in settings is too strict by default and can block startup for
valid topologies. Update the defaults around
OPENSEARCH_NODE_COUNT_CHECK_ENABLED, OPENSEARCH_EXPECTED_CLUSTER_MANAGER_COUNT,
and OPENSEARCH_EXPECTED_COORDINATING_NODE_COUNT so the check is non-blocking
unless explicitly configured, or make the expected counts safe for optional node
types. Also review wait_for_opensearch and the related config constants in
settings.py to ensure the gate only enforces counts when the deployment actually
requires dedicated cluster-manager or coordinating-only nodes, and document the
required tuning.

@mpawlow mpawlow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review 1

  • ✅ Approved / LGTM 🚀

@github-actions github-actions Bot added the lgtm label Jun 24, 2026
@edwinjosechittilappilly
edwinjosechittilappilly merged commit 86bb096 into main Jun 24, 2026
23 checks passed
@github-actions
github-actions Bot deleted the PR-1913-opensearch-health-checks branch June 24, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) ci ⬛ CI/CD, build, and infrastructure issues docker ignore-for-release lgtm tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants