fix(connectors): Harden Elasticsearch sink test readiness - #3729
fix(connectors): Harden Elasticsearch sink test readiness#3729ryerraguntla wants to merge 9 commits into
Conversation
Add a timeout to the Elasticsearch sink transport, make connectors-runtime readiness check `/health`, and strengthen the Elasticsearch test fixture startup path with retry, cluster-health polling, and stale-index cleanup. Also refresh the sink index before counting documents so test assertions see newly indexed data sooner.
Use short-timeout probes without retries for cluster health and stale index sweep, and cap docker rm so a wedged daemon cannot stall setup.
|
Thanks for the PR. It is labeled Slash commands (own line, regular comment) move it around the queue:
See CONTRIBUTING.md for details. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3729 +/- ##
=============================================
- Coverage 75.10% 39.73% -35.37%
Complexity 969 969
=============================================
Files 1307 1305 -2
Lines 152751 131680 -21071
Branches 128185 107116 -21069
=============================================
- Hits 114726 52329 -62397
- Misses 34494 76581 +42087
+ Partials 3531 2770 -761
🚀 New features to boost your workflow:
|
|
/request-review @user-or-team |
There was a problem hiding this comment.
start() force-removes the shared container after any try_start() error, not only after confirming it is wedged. Transient health, port-inspection, or Docker errors can therefore remove an Elasticsearch container still used by another process. The fixed name limits which container is removed but does not provide ownership or concurrency safety. Could this recovery path verify the container is unhealthy and coordinate removal with a cross-process lock before running docker rm -f?
There was a problem hiding this comment.
Let me add the logic to remove when required.
Only docker rm the shared reuse container after inspect shows a removable state, and serialize recovery with a cross-process lock so transient start flakes cannot yank a healthy instance from a peer nextest worker.
|
/ready |
hubcio
left a comment
There was a problem hiding this comment.
not in this diff, same class of problem:
- elasticsearch source
create_client()(core/connectors/sources/elasticsearch_source/src/lib.rs:304) still builds its transport with no timeout - sameopen()-hang failure mode this PR fixes for the sink, so source tests keep the #3728 flake class. worth a follow-up. mcp.rswait_ready()still uses the pattern this PR fixed for connectors: GET/, anyOk(_)counts as ready, no process-crash fast-fail. consistency follow-up.
| .unwrap_or(DEFAULT_TIMEOUT_SECONDS) | ||
| .max(1); | ||
| let mut transport_builder = | ||
| TransportBuilder::new(conn_pool).timeout(Duration::from_secs(timeout_seconds)); |
There was a problem hiding this comment.
this timeout is client-global in elasticsearch-rs, so it bounds bulk requests in consume() too, not just open(). that changes the failure mode: before, a bulk on degraded ES stalled until it eventually succeeded; now anything over timeout_seconds returns Err, which the runtime discards (#2927) with the offset already committed at poll (#2928) - the batch is silently dropped. mechanism is pre-existing and tracked there, but this PR is what arms it for slow-ES bulk. worth an upgrade note in the PR description: raise timeout_seconds for slow bulk workloads, and note the tradeoff until #2927/#2928 land.
separately, the comment slightly oversells the timeout as the flake fix - with the 30s default the harness readiness budget (~20s) expires first, so what fixes #3728 is the fixture readiness gate; this is the infinite-hang backstop. maybe say that instead.
short-timeout health clients, wait_for_status , cluster readiness, async try_lock recovery, and timeout docs clarifying client-global clamp vs apache#3728 flake fix.
|
/ready |
| matches!( | ||
| status, | ||
| "exited" | "dead" | "created" | "paused" | "restarting" | ||
| ) || health == "unhealthy" |
There was a problem hiding this comment.
This predicate cannot fire for the wedge state the recovery path was written for.
docker.io/library/elasticsearch:9.3.0 ships no HEALTHCHECK: docker image inspect docker.io/library/elasticsearch:9.3.0 --format '{{json .Config.Healthcheck}}' returns null, and GenericImage does not add one. So .State.Health is always nil, the {{if .State.Health}} branch in inspect_format yields the empty string, and health == "unhealthy" is unreachable. The predicate reduces to the status list alone.
That list covers container-level death (an OOM-killed JVM exits, so exited catches it), but not the case wait_until_ready was added to detect: ES process up, cluster red or HTTP wedged from a corrupted data dir, which is exactly what the doc comment above names as motivation. Docker reports running, this returns false, and every Elasticsearch test then fails at fixture setup until someone runs docker rm -f iggy-test-elasticsearch by hand. That manual step is what the recovery path was meant to remove.
Two ways out:
- Treat
runningas removable when readiness already exhausted allCLUSTER_READY_ATTEMPTS. Pass that signal in so it stays distinct from a transient start or port flake, and keep the removal underRecoveryLock. - If leaving a running container alone is the deliberate call, drop the unreachable
healthleg and put the remediation command in thewarn!at the caller. It currently says "leaving it in place" with no next step, so whoever hits it has to work outdocker rm -f iggy-test-elasticsearchthemselves.
Summary
iggy-connectorsnever becomes healthy because sink
open()can hang on Elasticsearch HTTPwith no client timeout, while the connectors HTTP API only binds after
plugin open.
127.0.0.1, alwaysre-check
/_cluster/healthon reuse attach, self-heal a wedgediggy-test-elasticsearchcontainer only, and sweep stale harness indicesolder than 30 minutes.
/healthand fail fast if thechild process dies during retries; refresh the index before document-count
polls in the sink fixture.
Closes #3728
Test plan
cargo fmt --allcargo sort --no-format --workspacecargo clippy -p iggy_connector_elasticsearch_sink -p integration --all-features --all-targets -- -D warningscargo test -p integration -- connectors::elasticsearch::elasticsearch_sink::elasticsearch_sink_stores_json_messagesdocker rm -frecovery targets onlyiggy-test-elasticsearch(no other Elasticsearch containers)
iggy-test-elasticsearchrunning and re-runthe sink test to verify reuse + readiness path
Notes
iggy-test-elasticsearchonly.tests sharing the reused container are not disrupted.