Skip to content

[cuebot] Prevent Lost procs from being deferred forever - #2508

Merged
DiegoTavares merged 1 commit into
AcademySoftwareFoundation:masterfrom
DiegoTavares:double_booking_05
Aug 12, 2026
Merged

DiegoTavares merged 1 commit into
AcademySoftwareFoundation:masterfrom
DiegoTavares:double_booking_05

Conversation

@DiegoTavares

@DiegoTavares DiegoTavares commented Aug 12, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #2506: a restarted RQD answers kill-before-release with NOT_FOUND, which was wrapped as a failure, so lostProc deferred provably dead frames indefinitely and re-logged the "Deferring release of lost proc" WARN every 30s.

  • RqdClientGrpc.killFrame now treats NOT_FOUND as confirmed-stopped (matching isFrameRunning); only unknown outcomes throw RqdClientException.
  • Bound the deferral: after dispatcher.lost_proc_max_defer_ms (default 20 min) without a ping proving the frame alive, release the proc and fail the frame closed to DEAD (manual retry) instead of deferring again. Negative disables the bound.
  • Boot-time fence: a host whose reported boot time is later than the proc's dispatch (plus a clock-skew margin) cannot be running the frame, so release it to WAITING even when the kill cannot be delivered.
  • Orphaned-proc reaper: order candidates oldest-ping first (deterministic batch turnover) and make the batch size configurable (maintenance.orphaned_proc_batch_size, default 100) so unconfirmable procs cannot starve releasable orphans.

New unit tests cover the fail-closed bound, the boot fence, and killFrame status classification against a real localhost gRPC server; DAO tests cover the new queries.

LLM usage disclosure

Claude Opus was used for investigating the logs and implementing this fix.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of lost processes by detecting stale pings and host reboots.
    • Automatically marks frames as DEAD when lost-process recovery cannot be confirmed within the configured time limit.
    • Treats already-missing frames as successfully stopped during kill operations.
    • Orphaned-process cleanup now processes oldest entries first.
  • Configuration

    • Added settings for lost-process deferral limits and orphaned-process cleanup batch size.

Follow-up to AcademySoftwareFoundation#2506: a restarted RQD answers kill-before-release with NOT_FOUND, which was
wrapped as a failure, so lostProc deferred provably dead frames indefinitely and re-logged
the "Deferring release of lost proc" WARN every 30s.

- RqdClientGrpc.killFrame now treats NOT_FOUND as confirmed-stopped (matching
  isFrameRunning); only unknown outcomes throw RqdClientException.
- Bound the deferral: after dispatcher.lost_proc_max_defer_ms (default 20 min) without a
  ping proving the frame alive, release the proc and fail the frame closed to DEAD
  (manual retry) instead of deferring again. Negative disables the bound.
- Boot-time fence: a host whose reported boot time is later than the proc's dispatch
  (plus a clock-skew margin) cannot be running the frame, so release it to WAITING even
  when the kill cannot be delivered.
- Orphaned-proc reaper: order candidates oldest-ping first (deterministic batch turnover)
  and make the batch size configurable (maintenance.orphaned_proc_batch_size, default 100)
  so unconfirmable procs cannot starve releasable orphans.

New unit tests cover the fail-closed bound, the boot fence, and killFrame status
classification against a real localhost gRPC server; DAO tests cover the new queries.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds proc ping and reboot checks, bounds deferred lost-proc handling, treats RQD NOT_FOUND as a successful stop, and makes orphaned-proc batch size configurable.

Changes

Proc recovery and maintenance

Layer / File(s) Summary
Proc detection queries
cuebot/src/main/java/com/imageworks/spcue/dao/ProcDao.java, cuebot/src/main/java/com/imageworks/spcue/dao/postgres/ProcDaoJdbc.java, cuebot/src/test/java/com/imageworks/spcue/test/dao/postgres/ProcDaoTests.java
The DAO detects stale pings and host reboots after dispatch. Orphaned-proc results are ordered by oldest ping. PostgreSQL tests cover both checks.
Bounded lost-proc release
cuebot/src/main/java/com/imageworks/spcue/dispatcher/DispatchSupportService.java, cuebot/src/main/resources/opencue.properties, cuebot/src/test/java/com/imageworks/spcue/test/dispatcher/DispatchSupportServiceLostProcTests.java
Lost-proc handling supports reboot confirmation, configurable deferral bounds, indefinite deferral when disabled, and DEAD state assignment after expiry.
RQD kill outcome contract
cuebot/src/main/java/com/imageworks/spcue/rqd/RqdClient.java, cuebot/src/main/java/com/imageworks/spcue/rqd/RqdClientGrpc.java, cuebot/src/test/java/com/imageworks/spcue/test/rqd/RqdClientGrpcTests.java
RQD NOT_FOUND now confirms that a frame is stopped. Other failures still raise RqdClientException. Integration-style tests cover these outcomes.
Configurable orphaned-proc batches
cuebot/src/main/java/com/imageworks/spcue/service/MaintenanceManagerSupport.java, cuebot/src/main/resources/opencue.properties
clearOrphanedProcs reads maintenance.orphaned_proc_batch_size and falls back to 100.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DispatchSupportService
  participant ProcDaoJdbc
  participant RqdClientGrpc
  DispatchSupportService->>ProcDaoJdbc: Check ping age and host reboot
  DispatchSupportService->>RqdClientGrpc: Kill frame
  RqdClientGrpc-->>DispatchSupportService: Return success, NOT_FOUND, or error
  DispatchSupportService->>DispatchSupportService: Stop frame as WAITING or DEAD
Loading

Possibly related PRs

Suggested reviewers: lithorus, ramonfigueiredo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: preventing lost processes from being deferred indefinitely.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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
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 `@cuebot/src/main/java/com/imageworks/spcue/dao/postgres/ProcDaoJdbc.java`:
- Around line 582-586: Update the orphan query’s ORDER BY clause in ProcDaoJdbc
to sort first by proc.ts_ping and then by proc.pk_proc, preserving oldest-first
ordering while making limited batches deterministic for equal timestamps.

In
`@cuebot/src/main/java/com/imageworks/spcue/service/MaintenanceManagerSupport.java`:
- Around line 206-208: Validate batchSize in checkHardwareState() before calling
procDao.findOrphanedVirtualProcs(); when maintenance.orphaned_proc_batch_size is
non-positive, replace it with DEFAULT_ORPHANED_PROC_BATCH_SIZE so the cleanup
query always receives a valid limit.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b2f8c61b-17d0-4fe6-aa67-44fa3a27c6aa

📥 Commits

Reviewing files that changed from the base of the PR and between d9f5ba2 and 2600eb5.

📒 Files selected for processing (10)
  • cuebot/src/main/java/com/imageworks/spcue/dao/ProcDao.java
  • cuebot/src/main/java/com/imageworks/spcue/dao/postgres/ProcDaoJdbc.java
  • cuebot/src/main/java/com/imageworks/spcue/dispatcher/DispatchSupportService.java
  • cuebot/src/main/java/com/imageworks/spcue/rqd/RqdClient.java
  • cuebot/src/main/java/com/imageworks/spcue/rqd/RqdClientGrpc.java
  • cuebot/src/main/java/com/imageworks/spcue/service/MaintenanceManagerSupport.java
  • cuebot/src/main/resources/opencue.properties
  • cuebot/src/test/java/com/imageworks/spcue/test/dao/postgres/ProcDaoTests.java
  • cuebot/src/test/java/com/imageworks/spcue/test/dispatcher/DispatchSupportServiceLostProcTests.java
  • cuebot/src/test/java/com/imageworks/spcue/test/rqd/RqdClientGrpcTests.java

@DiegoTavares DiegoTavares changed the title [cuebot] Lost procs from being deferred forever [cuebot] Prevent Lost procs from being deferred forever Aug 12, 2026
@DiegoTavares
DiegoTavares marked this pull request as ready for review August 12, 2026 05:33
@DiegoTavares
DiegoTavares merged commit 48a7b70 into AcademySoftwareFoundation:master Aug 12, 2026
23 of 25 checks passed
DiegoTavares added a commit that referenced this pull request Aug 26, 2026
Close the remaining free-then-rebook path that survived #2506/#2508: the
generic launch rollback released the booking first (proc deleted, frame
back to WAITING) and fired a best-effort kill whose failure was
swallowed. When a launchFrame RPC fails on the Cuebot side after the
request was delivered (deadline expired waiting for the response,
connection dropped mid-call), the render is alive on the host, the kill
fails on the same broken network, and the freed frame is instantly
re-booked by another Cuebot -- each failed launch becomes a
double-booking under RPC instability.

- RqdClientGrpc.launchFrame classifies failures: transport-level codes
(DEADLINE_EXCEEDED, UNAVAILABLE, CANCELLED, INTERNAL, DATA_LOSS) throw
the new RqdLaunchUnknownOutcomeException; application-level responses
(RQD rejects with ABORTED/FAILED_PRECONDITION/INVALID_ARGUMENT) prove
the frame never started and keep the plain RqdClientException.
- Both copies of the release-first rollback route unknown-outcome launch
failures through DispatchSupport.resolveUnknownLaunchOutcome instead:
CoreUnitDispatcher's DispatchFrameTemplate (the production dispatch
path, used by dispatchHost and dispatchProcToJob) and
AbstractDispatcher.dispatchProc/dispatchHost (the LocalDispatcher and
RedirectManager paths). The legacy rollback is preserved for proven
rejections.
- resolveUnknownLaunchOutcome confirms before releasing anything: a
frame confirmed running keeps its booking and finishes through its own
frame complete report; a frame confirmed not running (two consecutive
isFrameRunning polls within dispatcher.launch_confirm_budget_ms) is
released for re-dispatch; anything unconfirmable keeps the booking (fail
closed) and is reclaimed by the orphaned-proc reaper's own
kill-and-confirm path. A non-positive budget restores the legacy
behavior.
- New counter cue_frame_launch_outcome_unknown_total{resolution} plus
DispatchSupport.unknownLaunchOutcomes.

Unit tests cover the status classification against a real localhost gRPC
server, the resolver's four resolutions, and the rollback routing in
both dispatcher rollback sites.


## LLM usage disclosure

Claude Opus was used for implementing the fix and unit tests.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Prevented frames from being double-booked when launch results are
uncertain.
* Verified frame status before releasing bookings after uncertain launch
failures.
  * Preserved rollback behavior for confirmed launch failures.
* Prevented rollback from clearing frames belonging to newer dispatches.

* **New Features**
* Added configurable launch-outcome confirmation with a 20-second
default window and 7-second polling interval.
  * Added Prometheus metrics for launches with unknown outcomes.
  * Improved monitoring and handling of uncertain launch failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
DiegoTavares added a commit to akheffache/OpenCue that referenced this pull request Aug 28, 2026
Brings in AcademySoftwareFoundation#2473, AcademySoftwareFoundation#2497, AcademySoftwareFoundation#2501, AcademySoftwareFoundation#2502, AcademySoftwareFoundation#2504, AcademySoftwareFoundation#2506, AcademySoftwareFoundation#2508, AcademySoftwareFoundation#2510, AcademySoftwareFoundation#2511 and
the cueweb dependency bumps.

Conflict resolutions:

- applicationContext-service.xml, FrameDao: keep both sides.
- DispatchQuery: keep the branch's layer.pk_layer fix plus master's
  ts_start_after predicate.
- RqdClientGrpc: master's split catch and unknown-outcome throw, keeping the
  branch's warn log of the underlying launch failure on both paths.
- FrameCompleteHandler: master refactored the class into small methods, so the
  scheduler hooks were re-applied on top of the new structure rather than
  merged line by line. The drain path (resolveForDrain) now also runs master's
  orphan-finalize and run-ownership fences, and handleStaleCompletion delegates
  to handleStaleReport so scheduler-owned shows get the same duplicate check
  and re-read guard as legacy ones; its Scheduler call site passes the report.
  The bookingOff guards became isBookingOff() calls inside the extracted
  booking methods, and the per-frame OOM bump moved into
  retryFrameWithRaisedMemory.

FrameCompleteHandlerOwnershipTests builds the handler from a bare Environment
mock; the constructor now unboxes numeric properties, so the mock returns the
supplied defaults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114CS5RrhDjTJse5faC4Pcb
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