Skip to content

fix(missing-test): Node Lock Contention Retry & Timeout Test - #2494

Closed
aniket866 wants to merge 7 commits into
Project-HAMi:masterfrom
aniket866:fix/Node-lock-test
Closed

fix(missing-test): Node Lock Contention Retry & Timeout Test#2494
aniket866 wants to merge 7 commits into
Project-HAMi:masterfrom
aniket866:fix/Node-lock-test

Conversation

@aniket866

@aniket866 aniket866 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Closes #2492
Node Lock Contention Retry & Timeout Test

Specification Field Details & Implementation Context
Target File nodelock.go
Target Functions TryLockNode, LockNode, ReleaseNodeLock
Missing Scenario High lock contention scenarios where multiple schedulers attempt to acquire a node lock with backoff retries (DefaultStrategy), combined with context cancellation / timeout.
Risk / Failure Mode Deadlocks, unreleased node lock annotations/leases, or goroutine leaks when a context is cancelled while waiting on exponential backoff retries.
Why It Is Critical Node locking prevents concurrent Extender instances from double-assigning GPUs on the same physical node in HA setups.
Proposed Test Write unit tests in nodelock_test.go using mock K8s client with simulated API delays to test lock contention, backoff exponential steps, and context cancellation behavior.

Architecture Flow: Before vs After Test Coverage

flowchart LR
    subgraph Before["Before: Untested & Vulnerable Flow"]
        direction TB
        B_Lock["LockNode / TryLockNode\n(nodelock.go)"]
        B_Delay["Simulated API Latency / Contention"]
        B_Cancel["Context Timeout / Cancellation"]
        B_Leak["Goroutine Leak / Unreleased Lease"]

        B_Lock --> B_Delay
        B_Delay --> B_Cancel
        B_Cancel -->|Missing Cancel Guard| B_Leak
    end

    Before ==>|nodelock_test.go Backoff & Cancel Test| After

    subgraph After["After: Validated & Hardened Flow"]
        direction TB
        A_Lock["LockNode / TryLockNode\n(nodelock.go)"]
        A_Test["nodelock_test.go Mock Suite"]
        A_Cancel["Context Cancellation Signal"]
        A_Clean["Backoff Loop Terminated"]
        A_Release["Lease Cleanly Released"]

        A_Lock --> A_Test
        A_Test --> A_Cancel
        A_Cancel --> A_Clean
        A_Clean --> A_Release
    end

    classDef danger fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#991b1b;
    classDef success fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#166534;
    classDef neutral fill:#f3f4f6,stroke:#4b5563,stroke-width:1.5px,color:#1f2937;

    class B_Leak danger;
    class A_Clean,A_Release success;
    class B_Lock,B_Delay,B_Cancel,A_Lock,A_Test,A_Cancel neutral;
Loading

o-generated comment: release notes by coderabbit.ai -->

Summary by CodeRabbit

  • New Features

    • Added context-aware node-lock operations that support cancellation.
    • Added non-blocking lock acquisition with improved contention and retry handling.
    • Existing lock APIs continue to work with the enhanced behavior.
  • Bug Fixes

    • Improved handling of stale, expired, malformed, and dangling locks.
    • Lock failures and cancellations are reported more reliably.
  • Tests

    • Expanded coverage for cancellation, contention, retries, release scenarios, and edge cases.

Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
@hami-robot

hami-robot Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: aniket866
Once this PR has been reviewed and has the lgtm label, please assign wawa0210 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 Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 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

NodeLock APIs now accept caller-provided contexts. Mutex acquisition, retries, release, stale-lock replacement, and try-lock operations handle cancellation and contention. Tests cover concurrency, failures, malformed annotations, nil contexts, and helper behavior.

Changes

NodeLock context handling

Layer / File(s) Summary
Context-aware lock retries
pkg/util/nodelock/nodelock.go
Added context-aware mutex acquisition and cancellation checks for set and release retry loops. Existing APIs delegate to context-aware implementations.
Context-aware lock orchestration
pkg/util/nodelock/nodelock.go
Added context-aware lock and try-lock APIs. Stale-lock replacement and node annotation patching now use the supplied context.
Contention and cancellation validation
pkg/util/nodelock/nodelock_test.go, CHANGELOG.md
Added tests for contention, cancellation, retries, stale locks, API failures, malformed annotations, nil-pod keys, and test helpers. Updated the v2.9.0 changelog.

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

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant LockNodeWithContext
  participant TryLockNodeWithContext
  participant KubernetesAPI
  Scheduler->>LockNodeWithContext: request node lock with context
  LockNodeWithContext->>TryLockNodeWithContext: attempt non-blocking lock
  TryLockNodeWithContext->>KubernetesAPI: read and patch node annotation
  KubernetesAPI-->>TryLockNodeWithContext: success or conflict
  TryLockNodeWithContext-->>LockNodeWithContext: lock result
  LockNodeWithContext-->>Scheduler: success, contention, or cancellation
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: archlitchi, dsfans2014, wawa0210

Poem

I’m a rabbit guarding locks tonight,
Context hops in, and waits take flight.
Backoff pauses, then yields with care,
One winner claims the node fair and square.
Tests thump softly: contention is gone—
Cancellation helps the queue move on.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #2492 with contention, backoff, cancellation, timeout, retry termination, and release-path test coverage.
Out of Scope Changes check ✅ Passed The production changes add context-aware node-lock APIs that directly support the linked issue's cancellation and timeout requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding Node Lock contention, retry, and timeout test coverage.
✨ 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.

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

Actionable comments posted: 4

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

Inline comments:
In `@pkg/util/nodelock/nodelock_test.go`:
- Around line 981-1000: Strengthen the SetNodeLockWithContext cancellation test
by recording the start time before launching the goroutine, requiring
errors.Is(err, context.DeadlineExceeded), and enforcing completion within a
small scheduling allowance after the 50 ms deadline instead of allowing two
seconds. Update the timeout assertion to fail if cancellation is not observed
promptly.
- Around line 1015-1041: Update the high-contention test around
LockNodeWithContext to create each pod fixture in clientSet before launching
goroutines, then synchronize their start so acquisitions compete concurrently.
Track successful and failed lock attempts while keeping the successful owner
lock held, and assert exactly one success; require every other result to equal
ErrNodeLockContention rather than only logging totalErrors.

In `@pkg/util/nodelock/nodelock.go`:
- Around line 321-327: Implement TryLockNodeWithContext as a nonblocking
operation instead of delegating to LockNodeWithContext: perform one immediate
per-node lock acquisition, return ErrNodeLockContention when unavailable, and
make only one node-lock API attempt without stale-lock replacement or retry
logic.
- Around line 129-132: Make per-node lock acquisition context-aware by adding or
reusing a helper that waits on the mutex while honoring context cancellation.
Update SetNodeLockWithContext, ReleaseNodeLockWithContext, and
LockNodeWithContext to use this helper instead of directly calling
sync.Mutex.Lock(), returning the context error when cancellation occurs; apply
the change at pkg/util/nodelock/nodelock.go lines 129-132 and 190-196.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c7bf07b4-b12f-4dae-9c1c-9239a28fe096

📥 Commits

Reviewing files that changed from the base of the PR and between 3616313 and fff333b.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • pkg/util/nodelock/nodelock.go
  • pkg/util/nodelock/nodelock_test.go

Comment thread pkg/util/nodelock/nodelock_test.go
Comment thread pkg/util/nodelock/nodelock_test.go Outdated
Comment thread pkg/util/nodelock/nodelock.go
Comment thread pkg/util/nodelock/nodelock.go
Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
pkg/util/nodelock/nodelock_test.go (1)

1084-1098: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test local mutex contention for TryLockNode.

The test only observes an existing node annotation. The previous blocking LockNodeWithContext delegation can satisfy these assertions.

Hold nodeLocks.getLock(nodeName) before the call. Invoke TryLockNode in a goroutine. Assert that it returns ErrNodeLockContention within a short timeout.

🤖 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/util/nodelock/nodelock_test.go` around lines 1084 - 1098, Update the
TryLockNode contention test to exercise the local mutex: acquire
nodeLocks.getLock(nodeName) before invoking TryLockNode, call TryLockNode in a
goroutine, and assert it returns ErrNodeLockContention within a short timeout.
Keep the existing node and pod setup only where needed, and release the mutex
after the assertion.
pkg/util/nodelock/nodelock.go (1)

162-170: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make retry backoff interruptible by context cancellation.

retry.OnError waits with time.Sleep between attempts, so checks in the callback do not stop the current backoff delay. When SetNodeLockWithContext or ReleaseNodeLockWithContext is canceled, the node-lock RPC can still block for the remainder of that delay. Use a context-aware retry loop that selects on ctx.Done() during backoff.

  • pkg/util/nodelock/nodelock.go#L162-L170
  • pkg/util/nodelock/nodelock.go#L237-L245
🤖 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/util/nodelock/nodelock.go` around lines 162 - 170, The retry loops in
SetNodeLockWithContext (pkg/util/nodelock/nodelock.go:162-170) and
ReleaseNodeLockWithContext (pkg/util/nodelock/nodelock.go:237-245) must make
backoff cancellation-aware. Replace retry.OnError’s time.Sleep-based behavior
with a context-aware retry loop that selects on ctx.Done() during each delay,
while preserving the existing contention checks, retry strategy, and immediate
cancellation error propagation.
🤖 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.

Inline comments:
In `@pkg/util/nodelock/nodelock_test.go`:
- Around line 1043-1045: Update the lock-release path in the test around
ReleaseNodeLockWithContext to record and assert its error instead of discarding
it. After wg.Wait(), read the node and assert that NodeLockKey is absent,
ensuring the lock was cleanly released.

---

Outside diff comments:
In `@pkg/util/nodelock/nodelock_test.go`:
- Around line 1084-1098: Update the TryLockNode contention test to exercise the
local mutex: acquire nodeLocks.getLock(nodeName) before invoking TryLockNode,
call TryLockNode in a goroutine, and assert it returns ErrNodeLockContention
within a short timeout. Keep the existing node and pod setup only where needed,
and release the mutex after the assertion.

In `@pkg/util/nodelock/nodelock.go`:
- Around line 162-170: The retry loops in SetNodeLockWithContext
(pkg/util/nodelock/nodelock.go:162-170) and ReleaseNodeLockWithContext
(pkg/util/nodelock/nodelock.go:237-245) must make backoff cancellation-aware.
Replace retry.OnError’s time.Sleep-based behavior with a context-aware retry
loop that selects on ctx.Done() during each delay, while preserving the existing
contention checks, retry strategy, and immediate cancellation error propagation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a86e4709-06f7-424d-98d7-7f095639ad96

📥 Commits

Reviewing files that changed from the base of the PR and between fff333b and efad2c6.

📒 Files selected for processing (2)
  • pkg/util/nodelock/nodelock.go
  • pkg/util/nodelock/nodelock_test.go

Comment thread pkg/util/nodelock/nodelock_test.go
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.69231% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/util/nodelock/nodelock.go 87.69% 4 Missing and 4 partials ⚠️
Flag Coverage Δ
unittests 64.68% <87.69%> (+0.46%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/util/nodelock/nodelock.go 96.13% <87.69%> (+14.19%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
@hami-robot hami-robot Bot added size/XL and removed size/L labels Aug 8, 2026

@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)
pkg/util/nodelock/test_helpers_test.go (1)

28-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider delegating to the production accessor.

EnsureNodeLockForTest reimplements the lazy-creation logic of the internal getLock accessor. If getLock changes its locking or initialization behavior, this helper drifts. Call nodeLocks.getLock(nodeName) instead to keep one source of truth.

♻️ Proposed refactor
 func EnsureNodeLockForTest(nodeName string) {
-	nodeLocks.mu.Lock()
-	if _, ok := nodeLocks.locks[nodeName]; !ok {
-		nodeLocks.locks[nodeName] = &sync.Mutex{}
-	}
-	nodeLocks.mu.Unlock()
+	_ = nodeLocks.getLock(nodeName)
 }

Remove the sync import if it becomes unused.

🤖 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/util/nodelock/test_helpers_test.go` around lines 28 - 34, Update
EnsureNodeLockForTest to delegate directly to nodeLocks.getLock(nodeName)
instead of duplicating lock initialization logic, and remove the sync import if
it is no longer used.
🤖 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 `@pkg/util/nodelock/test_helpers_test.go`:
- Around line 28-34: Update EnsureNodeLockForTest to delegate directly to
nodeLocks.getLock(nodeName) instead of duplicating lock initialization logic,
and remove the sync import if it is no longer used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 17110f9c-09e4-4013-a8af-e867888ca9af

📥 Commits

Reviewing files that changed from the base of the PR and between efad2c6 and b9ad6ec.

📒 Files selected for processing (2)
  • pkg/util/nodelock/nodelock_test.go
  • pkg/util/nodelock/test_helpers_test.go

Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
…n nodelock_test.go

Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
…ontext test

Signed-off-by: aniket866 <iamaniketkumarmaner@gmail.com>
@aniket866

Copy link
Copy Markdown
Contributor Author

@archlitchi @Eshiv-Pandey
do check out this whenever you get time

@aniket866 aniket866 changed the title Node Lock Contention Retry & Timeout Test fix(missing-test): Node Lock Contention Retry & Timeout Test Aug 9, 2026
@github-actions github-actions Bot added the kind/bug Something isn't working label Aug 9, 2026
@mesutoezdil

Copy link
Copy Markdown
Contributor

You can view the relevant rule here.
https://github.com/Project-HAMi/HAMi/blob/master/CONTRIBUTING.md#contribution-gates
"4. Review replies. The reply you post must be written by you and must address the specific point raised. Verbatim or canned AI replies, or replies that do not engage the comment, lead to the PR being closed."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node Lock Contention Retry & Timeout Test

2 participants