Pin ports in tests#1162
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1162 +/- ##
==========================================
+ Coverage 82.78% 82.85% +0.06%
==========================================
Files 123 123
Lines 10127 10127
==========================================
+ Hits 8384 8391 +7
+ Misses 1743 1736 -7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR reduces CI flakes caused by Dapr sidecar startup failures where multiple daprd listeners can end up attempting to bind the same randomly-chosen port, leading to bind: address already in use and a fatal sidecar exit. It does this primarily by pinning additional daprd listener ports in the integration test harness and by making the example-test retry detection cover both internal and API gRPC startup failures.
Changes:
- Pin daprd internal gRPC and metrics ports (in addition to existing gRPC/HTTP pins) for integration test sidecars.
- Broaden the example-test “port bind failure” detection marker to match both internal and API gRPC fatal-start messages.
- Update
DaprRunnerunit tests to validate retry behavior for both internal and API gRPC server startup failures.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
tests/integration/conftest.py |
Adds pinned internal_grpc_port and metrics_port to the integration sidecar launcher and passes the corresponding dapr run flags. |
tests/integration/AGENTS.md |
Documents the expanded set of pinned ports and the motivation (avoiding daprd listener port-collision flakes). |
tests/examples/test_dapr_runner.py |
Updates unit tests to cover both internal and API gRPC fatal-start variants for retry behavior. |
tests/examples/conftest.py |
Broadens the port-bind failure marker to match the fatal line regardless of whether it references internal or API gRPC. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
19d371d to
21854e9
Compare
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Description
Fixes the "bind: address already in use" flakes that kill
validateCI jobs at sidecar startup. What began as a single root cause turned out to be three distinct mechanisms.Root cause 1: the Dapr CLI can assign the same random port to two listeners
When
dapr runis launched without explicit ports, the CLI picks random free ports for every daprd listener. Its picker can hand the same port to two listeners of the same daprd — every sampled CI failure shows the metrics server successfully binding the exact port that the internal (or API) gRPC server then fatally fails to bind:Observed in runs 28439709999, 28198520326, 27741369607 (integration, internal gRPC) and 29419165370 (examples, API gRPC).
Root cause 2: teardown only waited for the
daprCLI processdapr runspawns daprd and the app as siblings of the CLI, which can exit while they are still shutting down gracefully — leaving ports and mDNS registrations alive into the next test module. Pinning made this old race consequential: every module now reuses the exact same ports, so any teardown lag becomes a deterministic startup failure.Root cause 3: pinned ports inside the OS ephemeral range get stolen
Dapr's conventional 500xx ports sit inside Linux's ephemeral source-port range (32768–60999). Any process's outbound localhost connection — the test runner's gRPC dials, health-check polls, daprd→redis/scheduler connections — can be assigned one of them as its source port, and an established socket blocks daprd from binding a listener there. This hit this PR's own CI twice (validate 3.14, validate 3.10): daprd retries the internal bind for 10×1s and dies fatally; in the second run the CLI's preflight reported
Port 50002 is not availablemore than a minute after every managed process was gone, ruling out any lingering daprd. dapr/dapr#10149 doesn't cover this — it only stops daprd's own outbound dials from stealing its ports, not other processes'.