Repository navigation
Conversation
c397396 to
9259d8b
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 4.20 #14039 +/- ##
============================================
+ Coverage 16.34% 16.42% +0.07%
- Complexity 13574 13666 +92
============================================
Files 5669 5669
Lines 501368 501633 +265
Branches 60903 60943 +40
============================================
+ Hits 81964 82396 +432
+ Misses 410219 410006 -213
- Partials 9185 9231 +46
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ue item In executeQueueItem, when persisting the executing MS id fails (the DB-deadlock case the catch block exists for), the queue item is returned to the queue so it can be retried. Execution then fell through to scheduleExecution(job), so the job was dispatched now AND re-dequeued and dispatched again by the heartbeat, running the same job (VM start/deploy, volume create, snapshot, etc.) twice concurrently and defeating the sync queue's serialization. Return after returning the item.
9259d8b to
8c06db5
Compare
|
@nagaboinaramgopal the method you are touching has two consecutive try blocks, both of which have nested try block. If you have time, do you feel like cleaning up the code? (does not need to be in the scope of this PR) |
sure, will do |
Flatten executeQueueItem with an early return for the missing-job case and extract the duplicated return-item-to-queue and clear-executing-msid bookkeeping into helpers, removing the nested try blocks. No behavior change.
|
@nagaboinaramgopal do you have reproduction steps for the issue PR is fixing? |
Thanks @shwstppr . No linked issue, it came from reading The scenario is the DB deadlock path that method already documents in its own TODO comment:
The fix returns right after the item is put back on the queue, so a failed dispatch only requeues and does not also execute.
On the unpatched code the test fails, which is the double execution: With the change it passes: |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19172 |
Explain why a failed executing-msid update returns the item to the queue and what the proper fix would be, as asked in review.
| _jobDao.update(job.getId(), job); | ||
| } catch (Exception e) { | ||
| logger.warn("Unexpected exception while dispatching job-" + item.getContentId(), e); | ||
| returnItemToQueue(item); |
There was a problem hiding this comment.
what happens if putting the item back in the queue also fails? that only logs the error, so the job never runs and just sits there until cleanup fails it about an hour later
There was a problem hiding this comment.
Good point. On the requeue itself failing: that logging-only path predates this PR and is the same DB-failure class as the dispatch update, so if the return fails the job is left for the JobCancelThreshold cleanup to fail it on its next pass, as today. I kept a different recovery policy out of this fix to keep it focused, but I'm happy to look at hardening it as a follow up, likely alongside removing the dispatch deadlock at its source, which is the real reason the requeue exists.
There was a problem hiding this comment.
can we return early only when putting it back worked? on 4.20 the job still runs if that fails too, now it just sits until cleanup fails it
There was a problem hiding this comment.
You're right, and that's on me. My early return strands the job when the requeue itself fails, where the original code at least ran it once by falling through. Fixed in b38a0c7: returnItemToQueue now reports whether the item went back on the queue, and the dispatch-failure path returns only on success, falling through to schedule the job otherwise. So the double-execution stays fixed without losing that safety. Added a test for the requeue-also-fails case.
| } catch (RejectedExecutionException e) { | ||
| logger.warn("Execution for job-" + job.getId() + " is rejected, return it to the queue for next turn"); | ||
| returnItemToQueue(item); | ||
| clearExecutingMsid(job, item); |
There was a problem hiding this comment.
should we clear the server id before putting the item back? another server could pick it up in between and we would wipe its id
There was a problem hiding this comment.
Good catch, thanks @Damans227, you're right that was a real cross-server race. I moved clearing the executing msid ahead of returning the item in fd11c31, so it is nulled while we still hold the item and another management server cannot dequeue it and have its msid wiped by our update. Added an InOrder test that fails on the old order and passes on the new one.
|
@rootnomad , can you answer @Damans227 's questions? |
…o the queue When scheduleExecution rejects the job, executeQueueItem returned the sync queue item and then cleared the job's executing management-server id. Another management server could dequeue the returned item and stamp its own msid in that window, and the clearing update (a full-row update of the job VO) would then wipe it. Clear the msid while we still hold the item, then return it.
|
Thanks @Damans227 for the careful read, and @DaanHoogland. Answered both inline. The first, clearing the executing msid before returning the item, was a real cross-server race, now fixed in fd11c31 with an InOrder test. The second, the requeue itself failing, is a pre-existing path left to the JobCancelThreshold cleanup, so I'd fold that into the deadlock-at-source follow up rather than widen this fix here. |
|
thanks @rootnomad, CLGTM. |
The double-execution fix returned right after returnItem(), but if returnItem itself failed the job was neither requeued nor run and sat until the JobCancelThreshold cleanup. The original code fell through and still ran it once. Return only when the item was put back on the queue successfully, and fall through to schedule it otherwise, so the double-execution stays fixed without stranding the job.
Description
In executeQueueItem, when persisting the executing management-server id fails
(the DB-deadlock case the catch block exists for), the queue item is returned to
the queue so it can be retried later. Execution then fell through to
scheduleExecution(job), so the job was dispatched now AND re-dequeued and
dispatched again by the heartbeat, running the same job (VM start/deploy, volume
create, snapshot, etc.) twice concurrently and defeating the sync queue's
serialization.
Return after returning the item so the job runs once, on the retry.
Types of changes
Feature/Enhancement Scale or Bug Severity
Bug Severity
How Has This Been Tested?
Added a unit test that makes the executing-msid update throw and asserts the
queue item is returned and the job is not also scheduled. Also built the standard
packages and deployed on a KVM advanced zone.