Skip to content

fix: skip processing for already-ended leases#862

Open
evakhoni wants to merge 2 commits into
jumpstarter-dev:mainfrom
evakhoni:fix/skip-stale-lease-processing
Open

fix: skip processing for already-ended leases#862
evakhoni wants to merge 2 commits into
jumpstarter-dev:mainfrom
evakhoni:fix/skip-stale-lease-processing

Conversation

@evakhoni

@evakhoni evakhoni commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

When the exporter falls behind processing leases (e.g. temporary unavailability, resource starvation, or slow hooks), the client retry loop creates new leases faster than the exporter can process them. Each stale lease triggered full session creation, hook execution, and teardown — a cascading backlog that prevented recovery.

Three layers of stale lease detection:

  1. hooks.py: check lease_ended before executing hook subprocess, and during the is_ready wait loop. Extracted _wait_for_lease_ready helper to keep complexity under C901 limit. Both hooks (before and after lease) are skipped for stale leases.

  2. exporter.py handle_lease: yield (sleep(0)) then check lease_ended before session creation; check again after session creation but before Listen stream and connection handling. Extracted _skip_stale_lease helper for both check sites.

  3. exporter.py serve: skip 0.2s inter-lease sleep when no session was created (no connections to overlap).

Also fixes a race condition in handle_lease's finally block where before_lease_hook.set() was called unconditionally, racing with run_before_lease_hook's skip_after_lease_hook flag update. Now conditioned on not self.hook_executor.

Test plan

  • 545 unit tests pass (make pkg-test-jumpstarter)
  • Lint clean (make lint-fix)
  • Type check clean (make pkg-ty-jumpstarter)
  • Manual test: SIGSTOP/SIGCONT with on-demand lease — stale leases drained in ~3s (16 leases), session skipped for all but the first
  • Manual test: SIGSTOP/SIGCONT with on-demand lease, client connects after resume — stale backlog drained, live lease processed normally with both hooks
  • Manual test: named lease (--lease) — no stale backlog, normal lifecycle unaffected

Check lease_ended before executing hooks and creating sessions so
stale leases are drained without spawning subprocesses or allocating
sockets. Fix race in handle_lease finally block where unconditional
before_lease_hook.set() could override skip_after_lease_hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b054c3a-c71c-40b1-a710-4edb329877e1

📥 Commits

Reviewing files that changed from the base of the PR and between d67bc41 and e4e307b.

📒 Files selected for processing (2)
  • python/packages/jumpstarter/jumpstarter/exporter/exporter_test.py
  • python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py
  • python/packages/jumpstarter/jumpstarter/exporter/exporter_test.py

📝 Walkthrough

Walkthrough

Exporter lease handling now detects stale leases earlier, skips hook/session work when a lease has already ended, and tightens cleanup around lease completion. Hook readiness waiting was also split out so before-lease execution can stop cleanly on lease-end or timeout, with tests updated for both paths.

Changes

Stale lease handling in exporter and hooks

Layer / File(s) Summary
Exporter stale-lease helper and lease exit handling
python/packages/jumpstarter/jumpstarter/exporter/exporter.py
Adds stale-lease detection, early return checks, and conditional lease-end cleanup in exporter lease handling.
Hook readiness wait and stale-lease guard
python/packages/jumpstarter/jumpstarter/exporter/hooks.py
Adds readiness polling with timeout handling and skips before-lease execution when the lease is already ended.
Exporter stale-lease tests
python/packages/jumpstarter/jumpstarter/exporter/exporter_test.py
Adds coverage for stale leases before session creation, during session setup, and during shutdown.
Hook stale-lease tests
python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py
Updates ended-lease assertions and adds coverage for readiness timeout and lease-ended polling races.

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

Sequence Diagram(s)

sequenceDiagram
  participant Serve
  participant HandleLease
  participant SkipStaleLease
  participant SessionForLease
  participant ReportStatus

  Serve->>HandleLease: start lease handling
  HandleLease->>HandleLease: await sleep(0)
  HandleLease->>SkipStaleLease: check lease_ended before session
  alt lease already ended
    SkipStaleLease->>ReportStatus: report AVAILABLE
    SkipStaleLease-->>HandleLease: return early
  else lease active
    HandleLease->>SessionForLease: enter session context
    HandleLease->>SkipStaleLease: re-check lease_ended during setup
    alt lease ended during setup
      SkipStaleLease->>ReportStatus: report AVAILABLE
      SkipStaleLease-->>HandleLease: return early
    else lease still active
      HandleLease->>HandleLease: continue connection handling
    end
  end
Loading

Possibly related PRs

Suggested labels: backport release-0.7

Suggested reviewers: kirkbrauer, raballew, mangelajo

Poem

A lease went stale, a hop, a blink,
The hooks stepped back before they’d sync.
No deadlock now, just AVAILABLE light,
And bunny-safe burrows hum all night. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: skipping processing for leases that have already ended.
Description check ✅ Passed The description matches the changeset and explains the stale-lease detection and race 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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py`:
- Around line 1627-1669: Add a regression test around
HookExecutor.run_before_lease_hook for the case where config.before_lease is
unset and only after_lease is configured, with lease_scope.lease_ended already
set while lease_scope.is_ready() is still true. The current test covers the
_wait_for_lease_ready bail-out path, but not this ordering case; create a
focused test that verifies the executor exits immediately without waiting, does
not report status, and still leaves the lease_scope state consistent for
downstream hooks.

In `@python/packages/jumpstarter/jumpstarter/exporter/hooks.py`:
- Around line 666-684: The lease-ended check in the before-lease flow should run
before the no-hook fast path in the hook handling logic. Update the
`before_lease` path around `_wait_for_lease_ready()` so
`lease_scope.lease_ended.is_set()` is evaluated first, then skip the hook and
set `skip_after_lease_hook` if needed, before any
`self.config.before_lease`/`LEASE_READY` handling. This ensures stale leases do
not report ready when only `after_lease` is configured and `hook_executor` is
present.
🪄 Autofix (Beta)

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

Run ID: 56bbc204-5696-496b-a500-10a214ef940d

📥 Commits

Reviewing files that changed from the base of the PR and between ededacb and d67bc41.

📒 Files selected for processing (4)
  • python/packages/jumpstarter/jumpstarter/exporter/exporter.py
  • python/packages/jumpstarter/jumpstarter/exporter/exporter_test.py
  • python/packages/jumpstarter/jumpstarter/exporter/hooks.py
  • python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py

Comment thread python/packages/jumpstarter/jumpstarter/exporter/hooks_test.py
Comment thread python/packages/jumpstarter/jumpstarter/exporter/hooks.py
New tests: handle_lease skips session for stale leases, suppresses
AVAILABLE on shutdown, bails after session creation when lease ends
during setup. Hook bails during is_ready wait when lease_ended is set.
_wait_for_lease_ready times out and reports BEFORE_LEASE_HOOK_FAILED
when session is never populated.

Updated tests: assert hook subprocess is never executed (not just
LEASE_READY skipped) when lease is already ended, matching the new
pre-execution lease_ended check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@evakhoni evakhoni force-pushed the fix/skip-stale-lease-processing branch from d67bc41 to e4e307b Compare July 7, 2026 17:19
@evakhoni evakhoni requested a review from mangelajo July 7, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant