Skip to content

fix: restore pod device usage in scheduler Filter on error paths (#1491) - #2044

Closed
pingxin403 wants to merge 10 commits into
Project-HAMi:masterfrom
pingxin403:fix/filter-pod-leak
Closed

fix: restore pod device usage in scheduler Filter on error paths (#1491)#2044
pingxin403 wants to merge 10 commits into
Project-HAMi:masterfrom
pingxin403:fix/filter-pod-leak

Conversation

@pingxin403

@pingxin403 pingxin403 commented Jul 9, 2026

Copy link
Copy Markdown

What type of PR is this?
/kind bug

What this PR does / why we need it:
Fixes a GPU accounting leak in the scheduler Filter path. When TakeAndDeletePod successfully removes a pod from podManager but a subsequent step fails (getNodesUsage, calcScore, no candidate nodes, or PatchPodAnnotations), the pod is permanently removed from the cache without restoring its device/quota state. A subsequent retry of the same pod may land on a different card, or the quota reservation is silently lost.

Which issue(s) this PR fixes:
Fixes #1491

Special notes for your reviewer:
The fix introduces a restorePod closure that re-adds the pod to podManager and restores its quota usage on the four error paths after TakeAndDeletePod succeeds.

A regression test Test_Filter_PodLeakOnFailure validates that the pod remains in podManager after a failed Filter attempt on a fully occupied node.

AI Assistance Disclosure:
This contribution was created with the assistance of AI tools (OpenCode/Anthropic Claude). The code logic, testing strategy, and final implementation were reviewed and verified by a human contributor.

Summary by CodeRabbit

  • Bug Fixes
    • Improved scheduler rollback so pod tracking and related quota/device usage are reliably restored across more Filter failure scenarios, including when no nodes are viable and when pod annotation patching fails.
    • Prevented stale tentative cache cleanup from leaving pods missing or mis-accounted after scheduling errors.
    • Updated pod cache handling to keep the stored node assignment consistent when re-adding an existing pod.
  • Tests
    • Added unit tests covering pod-tracking leak prevention during Filter failures and annotation patch failures.

@hami-robot hami-robot Bot added kind/bug Something isn't working dco-signoff: yes labels Jul 9, 2026
@hami-robot
hami-robot Bot requested review from FouoF and wawa0210 July 9, 2026 13:14
@hami-robot

hami-robot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: pingxin403
Once this PR has been reviewed and has the lgtm label, please assign dsfans2014 for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@hami-robot hami-robot Bot added the size/L label Jul 9, 2026
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The scheduler’s Filter flow now tracks tentative pod and quota removal, restores state across failure paths, updates cached node assignments, and tests restoration after scheduling and annotation-patch failures.

Changes

Pod rollback fix in scheduler Filter

Layer / File(s) Summary
Rollback closure and failure paths
pkg/scheduler/scheduler.go
Filter conditionally removes pod and quota state, restores it after usage or scoring failures, and preserves the empty unrestricted-candidate cleanup behavior.
Patch rollback and cache restoration
pkg/scheduler/scheduler.go, pkg/device/pods.go
Patch failure rollback reverts tentative quota usage and restores or removes pod cache entries as appropriate; re-adding an existing pod now updates its NodeID.
Regression coverage
pkg/scheduler/scheduler_test.go
Tests verify pod retention after failed scheduling and restoration of the original node and device state after annotation patch failure.

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

Sequence Diagram(s)

sequenceDiagram
    participant Filter as Filter()
    participant PodManager as podManager
    participant QuotaManager as quotaManager
    participant Util as util.PatchPodAnnotations

    Filter->>PodManager: remove pod conditionally
    Filter->>QuotaManager: remove usage conditionally
    Filter->>Filter: getNodesUsage()
    alt usage or scoring failure
        Filter->>PodManager: restorePod()
        Filter->>QuotaManager: restore usage
    end

    Filter->>Util: PatchPodAnnotations()
    alt patch fails
        Filter->>QuotaManager: revert tentative usage
        Filter->>PodManager: restore original pod or delete new entry
    end
Loading

Possibly related PRs

Suggested reviewers: fouof, wawa0210

Poem

A bunny hopped through Filter’s gate,
Restoring pods before too late.
When patches failed or scores ran dry,
The cache kept every pod nearby.
With quotas mended, cards stayed bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 describes the main fix: restoring pod device usage in scheduler Filter error paths.
Linked Issues check ✅ Passed The change restores pod and quota state on Filter failures, addressing the GPU accounting leak behind issue #1491.
Out of Scope Changes check ✅ Passed The PodManager and test updates support the same Filter rollback fix and do not appear unrelated.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request addresses a pod leak issue (bug #1491) where a pod is permanently removed from the scheduler's cache if the Filter operation fails. It introduces a restorePod helper function to re-add the pod and its quota usage on failure paths, and adds a corresponding unit test. The review feedback highlights a critical bug in the error handling of util.PatchPodAnnotations: calling restorePod() alone does not clean up the newly added pod allocation and quota usage, leading to state mismatch and resource leaks. A code suggestion is provided to properly revert the new allocation before restoring the old state.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pkg/scheduler/scheduler.go Outdated

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/scheduler/scheduler.go (1)

821-831: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Rollback the tentative pod allocation before restoring the old entry.

  • restorePod() only re-adds pi.Devices; after AddPod(args.Pod, m.NodeID, m.Devices) and AddUsage(args.Pod, m.Devices), a patch failure leaves the new quota charge in place and the cached pod entry on m.NodeID. Undo the tentative allocation first, then call restorePod().
🤖 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 `@pkg/scheduler/scheduler.go` around lines 821 - 831, The patch-failure
rollback in scheduler.go is incomplete because `restorePod()` only restores
`pi.Devices` and does not undo the tentative allocation made by
`podManager.AddPod` and `quotaManager.AddUsage` in the scheduling flow. In the
`AddPod(args.Pod, m.NodeID, m.Devices)` block, first roll back the newly added
pod and quota usage from `podManager` and `quotaManager`, then call
`restorePod()` so the old entry is restored after the tentative allocation is
fully reverted.
🧹 Nitpick comments (1)
pkg/scheduler/scheduler_test.go (1)

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

Consider also asserting quota state and covering the patch-failure path.

The test only verifies the pod count/presence in podManager on the empty-score branch. Adding an assertion on quotaManager.GetResourceQuota() (as Test_Filter_EvictsStaleEntry does) would guard against quota drift, and a case that fails at PatchPodAnnotations would cover the other rollback branch — which is currently untested and where the quota double-count noted in scheduler.go occurs.

🤖 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 `@pkg/scheduler/scheduler_test.go` around lines 1503 - 1510, The current test
only checks podManager recovery on the empty-score path in the Filter flow;
extend it to also assert quota state via quotaManager.GetResourceQuota, matching
the existing stale-entry coverage. Add a separate test case around the scheduler
Filter path that forces PatchPodAnnotations to fail so the rollback branch is
exercised, and verify both pod re-addition and quota consistency for the
affected podA/scheduler.go logic.
🤖 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.

Outside diff comments:
In `@pkg/scheduler/scheduler.go`:
- Around line 821-831: The patch-failure rollback in scheduler.go is incomplete
because `restorePod()` only restores `pi.Devices` and does not undo the
tentative allocation made by `podManager.AddPod` and `quotaManager.AddUsage` in
the scheduling flow. In the `AddPod(args.Pod, m.NodeID, m.Devices)` block, first
roll back the newly added pod and quota usage from `podManager` and
`quotaManager`, then call `restorePod()` so the old entry is restored after the
tentative allocation is fully reverted.

---

Nitpick comments:
In `@pkg/scheduler/scheduler_test.go`:
- Around line 1503-1510: The current test only checks podManager recovery on the
empty-score path in the Filter flow; extend it to also assert quota state via
quotaManager.GetResourceQuota, matching the existing stale-entry coverage. Add a
separate test case around the scheduler Filter path that forces
PatchPodAnnotations to fail so the rollback branch is exercised, and verify both
pod re-addition and quota consistency for the affected podA/scheduler.go logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa8a36cf-742c-4f83-84d4-ea8e4e88f60d

📥 Commits

Reviewing files that changed from the base of the PR and between 4e23779 and 2d71717.

📒 Files selected for processing (2)
  • pkg/scheduler/scheduler.go
  • pkg/scheduler/scheduler_test.go

Comment thread pkg/scheduler/scheduler.go Outdated
Comment thread pkg/scheduler/scheduler_test.go
Comment thread pkg/scheduler/scheduler.go
pingxin403 pushed a commit to pingxin403/HAMi that referenced this pull request Jul 12, 2026
- Fix nodeID not restored in AddPod else branch (mesutoezdil)
- Fix orphaned pod entry when removed=false on PatchPodAnnotations failure (mesutoezdil)
- Add test coverage for PatchPodAnnotations failure path (mesutoezdil)
- All existing Filter tests pass
pingxin403 pushed a commit to pingxin403/HAMi that referenced this pull request Jul 12, 2026
- Fix nodeID not restored in AddPod else branch (mesutoezdil)
- Fix orphaned pod entry when removed=false on PatchPodAnnotations failure (mesutoezdil)
- Add test coverage for PatchPodAnnotations failure path (mesutoezdil)
- All existing Filter tests pass

Signed-off-by: pingxin403 <pingxin403@163.com>
@pingxin403
pingxin403 force-pushed the fix/filter-pod-leak branch from 5096de7 to fa4e6eb Compare July 12, 2026 04:36
pingxin403 added 4 commits July 19, 2026 00:58
Signed-off-by: pingxin403 <pingxin403@163.com>
…ns failure path (Project-HAMi#1491)

Signed-off-by: pingxin403 <pingxin403@163.com>
- Fix nodeID not restored in AddPod else branch (mesutoezdil)
- Fix orphaned pod entry when removed=false on PatchPodAnnotations failure (mesutoezdil)
- Add test coverage for PatchPodAnnotations failure path (mesutoezdil)
- All existing Filter tests pass

Signed-off-by: pingxin403 <pingxin403@163.com>
@pingxin403
pingxin403 force-pushed the fix/filter-pod-leak branch from fa4e6eb to e75cb89 Compare July 18, 2026 17:00
@mesutoezdil

Copy link
Copy Markdown
Contributor

if you wouldn't mind doing merge commits instead of force pushing, that would make it a bit clearer what (if anything has changed).
and resolve the issues above

Comment thread pkg/scheduler/scheduler.go Outdated
@DSFans2014

Copy link
Copy Markdown
Member

A subsequent retry of the same pod may land on a different card, or the quota reservation is silently lost.

I'm a bit confused here: how is it guaranteed that the retry will be scheduled to the same card?

Comment thread pkg/scheduler/scheduler_test.go Outdated
Signed-off-by: yunpenghan <yunpenghan@futunn.com>
@pingxin403

Copy link
Copy Markdown
Author

Closing this PR. The issue does not meet the contribution guidelines and the fix is no longer needed. Thanks for the reviews.

@pingxin403 pingxin403 closed this Aug 2, 2026
@pingxin403
pingxin403 deleted the fix/filter-pod-leak branch August 2, 2026 02:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HAMi cannot allocate GPU correctly: new pod is scheduled to an already occupied card

5 participants