feat(flaky-detection): Add pytest-xdist support to flaky detection system#384
Conversation
Merge ProtectionsYour pull request matches the following merge protections and will not be merged until they are valid. 🔴 ApprovalThis rule is failing.
🔴 🔎 ReviewsThis rule is failing.
🟢 Continuous IntegrationWonderful, this rule succeeded.
🟢 Enforce conventional commitWonderful, this rule succeeded.Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 📕 PR descriptionWonderful, this rule succeeded.
|
There was a problem hiding this comment.
Pull request overview
Adds pytest-xdist compatibility to the flaky detection feature by moving context fetching/reporting responsibilities to the controller process and using xdist IPC (workerinput/workeroutput) for worker-side execution and metric aggregation.
Changes:
- Add worker-safe
FlakyDetector.from_context()construction andto_serializable_metrics()for IPC payloads. - Add controller-side aggregation/reporting via
make_report_from_aggregated()and xdist hooks (pytest_configure_node,pytest_testnodedown). - Introduce tests/docs and add
pytest-xdistas a dev dependency.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
pytest_mergify/flaky_detection.py |
Adds xdist-aware construction/serialization, static deadline mode, and aggregated report generation. |
pytest_mergify/__init__.py |
Implements controller/worker xdist hooks, context distribution, worker loading, and metrics export. |
pytest_mergify/ci_insights.py |
Adds worker-path loader to build the flaky detector from controller-provided context. |
tests/test_flaky_detection.py |
Adds unit tests for new serialization/from-context/reporting/static-deadline behavior. |
tests/test_xdist.py |
Adds xdist-focused tests and lifecycle simulations around controller/worker behavior. |
pyproject.toml |
Adds pytest-xdist to dev dependencies. |
uv.lock |
Locks pytest-xdist and transitive deps (execnet) with Python version markers. |
docs/superpowers/specs/2026-03-19-xdist-flaky-detection-design.md |
Documents the design and architecture for xdist support. |
docs/superpowers/plans/2026-03-19-xdist-flaky-detection.md |
Implementation plan for the xdist flaky detection work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
pytest_mergify/flaky_detection.py
Outdated
| available_budget_seconds = available_budget_duration_ms / 1000 | ||
| used_budget_ms = sum(m["total_duration_ms"] for m in test_metrics.values()) | ||
| used_budget_seconds = used_budget_ms / 1000 | ||
| result += ( | ||
| f"{os.linesep}- Used {used_budget_seconds / available_budget_seconds * 100:.2f} % of the budget " | ||
| f"({used_budget_seconds:.2f} s/{available_budget_seconds:.2f} s)" | ||
| ) |
pytest_mergify/flaky_detection.py
Outdated
| if tests_prevented_from_timeout: | ||
| result += ( | ||
| f"{os.linesep}⚠️ Reduced reruns for the following " | ||
| f"test{'s' if len(tests_prevented_from_timeout) else ''} to respect 'pytest-timeout':" |
| result += ( | ||
| f"{os.linesep}- Active for {len(test_metrics)} {mode} " | ||
| f"test{'s' if len(test_metrics) > 1 else ''}:" | ||
| ) | ||
| for test, m in test_metrics.items(): | ||
| if m["rerun_count"] < context.min_test_execution_count: |
pytest_mergify/__init__.py
Outdated
|
|
||
| if self.mergify_ci.flaky_detector: | ||
| if _is_xdist_controller(terminalreporter.config): | ||
| if self._xdist_flaky_context: |
| """ | ||
| ) | ||
|
|
||
| result = pytester.runpytest_inprocess( |
2a21e1d to
a84cc23
Compare
pytest_mergify/__init__.py
Outdated
| # xdist controller state. | ||
| self._xdist_flaky_context: typing.Optional[typing.Dict[str, typing.Any]] = None | ||
| self._xdist_flaky_mode: typing.Optional[str] = None | ||
| self._xdist_aggregated_metrics: typing.Dict[str, typing.Any] = { | ||
| "test_metrics": {}, | ||
| "over_length_tests": [], | ||
| "debug_logs": [], | ||
| } | ||
| self._xdist_available_budget_duration_ms: float = 0.0 | ||
|
|
||
| # On xdist controller, reuse the already-loaded detector's context | ||
| # for distribution to workers. No extra API call needed since | ||
| # MergifyCIInsights.__post_init__ already calls _load_flaky_detector(). | ||
| if _is_xdist_controller(config) and self.mergify_ci.flaky_detector: | ||
| self._xdist_flaky_context = dataclasses.asdict( | ||
| self.mergify_ci.flaky_detector._context | ||
| ) | ||
| self._xdist_flaky_mode = self.mergify_ci.flaky_detector.mode | ||
|
|
||
| # xdist worker: load flaky detector from controller-provided context. | ||
| if _is_xdist_worker(config): | ||
| workerinput = getattr(config, "workerinput", {}) | ||
| context = workerinput.get("flaky_detection_context") | ||
| mode = workerinput.get("flaky_detection_mode") | ||
| if context is not None and mode is not None: | ||
| self.mergify_ci.load_flaky_detector_from_context(context, mode) |
There was a problem hiding this comment.
Extract in another method to keep the main function small.
pytest_mergify/__init__.py
Outdated
| dist_mode = getattr(node.config.option, "dist", None) | ||
| if dist_mode == "each": |
There was a problem hiding this comment.
| dist_mode = getattr(node.config.option, "dist", None) | |
| if dist_mode == "each": | |
| if getattr(node.config.option, "dist", None) == "each": |
pytest_mergify/__init__.py
Outdated
| self.mergify_ci = MergifyCIInsights(**kwargs) | ||
|
|
||
| # xdist controller state. | ||
| self._xdist_flaky_context: typing.Optional[typing.Dict[str, typing.Any]] = None |
There was a problem hiding this comment.
We should probably move all this state in the flaky detector class to separate the concerns properly.
Code reviewFound 4 issues:
pytest-mergify/pytest_mergify/flaky_detection.py Lines 493 to 501 in a84cc23
pytest-mergify/pytest_mergify/__init__.py Lines 48 to 56 in a84cc23
pytest-mergify/pytest_mergify/__init__.py Lines 48 to 50 in a84cc23
pytest-mergify/pytest_mergify/__init__.py Lines 289 to 292 in a84cc23 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
a84cc23 to
145f31c
Compare
…stem Add full flaky detection support under pytest-xdist using controller- orchestrated pre-computed deadlines and xdist's built-in IPC. Architecture: - Controller fetches flaky detection context from API once, distributes it to workers via workerinput - Each worker constructs a FlakyDetector from the serialized context, computes the same global budget, runs tests with reruns - Workers send metrics back via workeroutput - Controller aggregates metrics and generates the terminal report Changes: - Add FlakyDetector.from_context() classmethod for worker-side construction - Add to_serializable_metrics() for xdist IPC (plain builtins only) - Add make_report_from_aggregated() for controller-side reporting - Add static deadline mode (_is_xdist flag) preserving dynamic for non-xdist - Add MergifyCIInsights.load_flaky_detector_from_context() - Add controller hooks (pytest_configure_node, pytest_testnodedown) - Add worker hooks (workerinput loading, workeroutput export) - Disable flaky detection under 'each' scheduling mode - Add pytest-xdist dev dependency and integration tests Fixes: MRGFY-6296 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Change-Id: I2811d2999ee3b51fb4649e2f60c33c200635435b
145f31c to
08a7051
Compare
Add full flaky detection support under pytest-xdist using controller-
orchestrated pre-computed deadlines and xdist's built-in IPC.
Architecture:
it to workers via workerinput
computes the same global budget, runs tests with reruns
Changes:
Fixes: MRGFY-6296
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com