Skip to content

ci(test): fix grpc server GPU-memory leak and run cuopt tests in parallel#1512

Merged
rapids-bot[bot] merged 8 commits into
mainfrom
fix/incumbent-callback-flaky-test
Jul 7, 2026
Merged

ci(test): fix grpc server GPU-memory leak and run cuopt tests in parallel#1512
rapids-bot[bot] merged 8 commits into
mainfrom
fix/incumbent-callback-flaky-test

Conversation

@ramakrishnap-nv

@ramakrishnap-nv ramakrishnap-nv commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Reap cuopt_grpc_server reliably — start it in its own process group and kill the whole group on teardown, so the server and its --workers child are cleaned up instead of leaking their GPU context across tests/runs.
  • Group grpc-server-backed test classes onto a single xdist worker (--dist loadgroup) so at most one server runs at a time.
  • Run the cuopt Python tests in parallel at -n 4, with --max-worker-restart=0 so a crashed worker fails once instead of respawning into a crash loop.
  • Skip the incumbent-callback test when the problem is solved at the root node (no branch-and-bound, so no incumbent callbacks fire), and fix a latent always-true termination assertion.

…olution

test_incumbent_get_set_callback[swath1.mps] fails intermittently with
assert 0 > 0 because swath1.mps is sometimes solved at the root node
(integral LP relaxation or presolve), which produces no branch-and-bound
iterations and therefore never triggers incumbent callbacks.

- Skip gracefully when n_callbacks == 0 instead of failing, with a
  message indicating root-node solve
- Fix the always-true assertion on termination status
  (== FeasibleFound or MILPTerminationStatus.Optimal was always True
  because the or operand is a truthy enum value)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv ramakrishnap-nv requested a review from a team as a code owner July 2, 2026 20:39
@ramakrishnap-nv ramakrishnap-nv requested a review from tmckayus July 2, 2026 20:39
@ramakrishnap-nv ramakrishnap-nv self-assigned this Jul 2, 2026
@ramakrishnap-nv ramakrishnap-nv added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

This PR updates incumbent callback handling, increases CI pytest parallelism, and refactors gRPC test server startup and shutdown around shared process-group helpers and xdist grouping markers.

Changes

Incumbent callback, CI, and gRPC test orchestration

Layer / File(s) Summary
Callback skip and CI xdist
python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py, ci/run_cuopt_pytests.sh
The incumbent callback test checks termination status before skipping when no get-callback fires, and CI runs pytest with four workers, loadgroup distribution, and disabled worker restarts.
gRPC server spawn and shutdown helpers
python/cuopt/cuopt/tests/fixtures/grpc_server_fixtures.py
Adds process-group server spawning with parent-death handling and group termination via SIGTERM/SIGKILL, then wires start/stop helpers through that shared shutdown path.
CPU-only, TLS, and mTLS fixtures
python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py
Imports the shared helpers, adjusts subprocess import handling, and updates CPU-only, TLS, and mTLS fixtures to start and stop servers through spawn_server and kill_server.
xdist grouping markers
python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py, python/cuopt/cuopt/tests/linear_programming/test_grpc_client.py
Adds xdist_group(name="grpc_server") markers to CPU CLI, TLS, mTLS, and gRPC client test classes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • NVIDIA/cuopt#1519: Also changes ci/run_cuopt_pytests.sh xdist execution settings, so the CI test runner behavior is directly related.

Suggested reviewers: mlubin, kaatish, Iroy30

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: skipping the incumbent callback test when a root-node solution occurs.
Description check ✅ Passed The description accurately matches the changes to the incumbent callback test and the termination-status assertion fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/incumbent-callback-flaky-test

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py (1)

95-105: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Tighten the skip guard. n_callbacks == 0 also covers a broken callback path, so use solution.get_milp_stats()["num_nodes"] == 0 before skipping; otherwise this test can hide a regression.

🤖 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 `@python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py`
around lines 95 - 105, The skip condition in the incumbent callback test is too
broad because get_callback.n_callbacks == 0 can also indicate a callback
regression; tighten the guard in test_incumbent_callbacks by checking
solution.get_milp_stats()["num_nodes"] == 0 before calling pytest.skip. Keep the
existing callback assertions tied to get_callback and set_callback, but only
skip when the solve finished at the root node, not just when no callbacks fired.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py`:
- Around line 95-105: The skip condition in the incumbent callback test is too
broad because get_callback.n_callbacks == 0 can also indicate a callback
regression; tighten the guard in test_incumbent_callbacks by checking
solution.get_milp_stats()["num_nodes"] == 0 before calling pytest.skip. Keep the
existing callback assertions tied to get_callback and set_callback, but only
skip when the solve finished at the root node, not just when no callbacks fired.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 978aaa9e-f825-44ea-adf1-74ff68693f89

📥 Commits

Reviewing files that changed from the base of the PR and between a740647 and e71b69f.

📒 Files selected for processing (1)
  • python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py

@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/merge

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

CI Test Summary

✅ All 31 test job(s) passed.

ramakrishnap-nv and others added 3 commits July 6, 2026 20:31
All four tests run on a single worker so they don't compete for GPU
resources with each other under -n 4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
…karound

Add a lightweight background GPU-memory sampler (ci/utils/gpu_monitor.sh)
wrapped around the parallel (pytest-xdist -n 2) path in
run_cuopt_pytests.sh. It records device memory.used/free/total and
per-process (per xdist worker) usage on an interval and prints the peak on
exit, so the ramp to the rmm::out_of_memory abort is visible in the job log.
Only the -n 2 branch is instrumented; the nightly serial path is unchanged.

Remove the @pytest.mark.xdist_group(name="incumbent_callbacks") markers from
test_incumbent_callbacks.py — grouping onto one worker was a workaround for
the same contention and is superseded by this diagnostic.
@ramakrishnap-nv ramakrishnap-nv requested a review from a team as a code owner July 6, 2026 19:54
@ramakrishnap-nv ramakrishnap-nv requested review from KyleFromNVIDIA and removed request for KyleFromNVIDIA July 6, 2026 19:54
Fix a GPU-memory leak and stabilize the parallel wheel-test run:

- Reap cuopt_grpc_server reliably: launch it in its own process group
  (start_new_session) with PR_SET_PDEATHSIG=SIGKILL, and tear it down with
  os.killpg (SIGTERM then SIGKILL). Previously only the parent was signalled,
  so the --workers child was orphaned and kept its ~7 GiB CUDA context; a
  crashed worker leaked the whole server. These accumulated across tests/runs
  until the device OOMed. Shared spawn_server/kill_server helpers now back
  every server start/stop (client, CPU-only, CLI, TLS, mTLS).
- Group all cuopt_grpc_server-backed classes with xdist_group and run pytest
  with --dist loadgroup, so at most one server runs at a time instead of one
  duplicated per xdist worker.
- Run cuopt Python tests at -n 4 (was -n 2) and add --max-worker-restart=0 so
  a crashed worker fails once instead of respawning into a crash spiral.
- Make test_cpu_only_execution import grpc_server_fixtures when re-executed as
  a standalone subprocess (add the fixtures dir to sys.path).
- Remove the temporary GPU-memory sampler used to diagnose the above.
…k-flaky-test

# Conflicts:
#	ci/run_cuopt_pytests.sh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
python/cuopt/cuopt/tests/fixtures/grpc_server_fixtures.py (1)

135-135: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add type hints (and param/return docs) to the new public helpers.

spawn_server and kill_server (Line 156) are new public functions but lack type annotations and their docstrings don't document parameters/returns. Suggest spawn_server(cmd: list[str], env: dict[str, str] | None = None) -> subprocess.Popen and kill_server(proc: subprocess.Popen | None) -> None.

As per path instructions: "Type hints on NEW public functions/classes" and "Docstring CONTENT on new public APIs — params, returns, raises".

🤖 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 `@python/cuopt/cuopt/tests/fixtures/grpc_server_fixtures.py` at line 135, The
new public helpers spawn_server and kill_server need proper type annotations and
API docs. Update spawn_server to use explicit parameter and return types, and do
the same for kill_server with a nullable process argument and None return;
ensure both functions’ docstrings document their parameters and return values in
the existing fixture helpers module.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@python/cuopt/cuopt/tests/fixtures/grpc_server_fixtures.py`:
- Line 135: The new public helpers spawn_server and kill_server need proper type
annotations and API docs. Update spawn_server to use explicit parameter and
return types, and do the same for kill_server with a nullable process argument
and None return; ensure both functions’ docstrings document their parameters and
return values in the existing fixture helpers module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b0a6081d-c40e-4f92-bb8d-7de31be93cd0

📥 Commits

Reviewing files that changed from the base of the PR and between 31f40bb and 49a07c2.

📒 Files selected for processing (4)
  • ci/run_cuopt_pytests.sh
  • python/cuopt/cuopt/tests/fixtures/grpc_server_fixtures.py
  • python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py
  • python/cuopt/cuopt/tests/linear_programming/test_grpc_client.py
✅ Files skipped from review due to trivial changes (1)
  • python/cuopt/cuopt/tests/linear_programming/test_grpc_client.py

@ramakrishnap-nv ramakrishnap-nv changed the title test(mip): skip incumbent callback test when solver finds root-node solution ci(test): fix grpc server GPU-memory leak and run cuopt tests in parallel Jul 7, 2026
@ramakrishnap-nv ramakrishnap-nv requested a review from tmckayus July 7, 2026 18:22
@rapids-bot rapids-bot Bot merged commit 9b40707 into main Jul 7, 2026
93 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants