What happened?
WorkflowTransition::selectRuleForItem() can return null without ever updating the item's next_transition_at, leaving the schedule row permanently overdue.
Cause
Two paths continue out of the candidate loop without contributing to any of the three tracking variables ($eligibleRules, $hasConditionBlocked, $earliestFutureDue):
- a candidate that is due but excluded by its item_filter
- a candidate whose computeDeadline() returns null (e.g. an empty cron expression)
When every candidate for an item takes one of those paths, the reschedule at the end of the method is skipped, because it is guarded by !$hasConditionBlocked && $earliestFutureDue !== null and both are still falsy.
Impact
next_transition_at stays in the past indefinitely. The item is re-fetched and fully re-evaluated on every scheduler run, forever.
This is more than wasted work: fetchOverdueCandidates() applies setLimit(50). Permanently-stuck rows occupy slots in that window on every run, so as they accumulate they crowd out items that could actually fire. Under load the scheduler starves.
Open design question
Leaving a filter-excluded item due may be intentional — the article could gain the matching tag later without changing stage, exactly like a condition-blocked item. If so, the two cases should be treated consistently and documented; right now condition-blocked is a deliberate, commented decision while filter-excluded is silent fallthrough.
Either way the starvation problem needs solving. One option: give stuck items a short re-check interval rather than leaving next_transition_at in the past.
Version
5.4
Expected result
No response
Actual result
No response
System Information
No response
Additional Comments
No response
What happened?
WorkflowTransition::selectRuleForItem() can return null without ever updating the item's next_transition_at, leaving the schedule row permanently overdue.
Cause
Two paths continue out of the candidate loop without contributing to any of the three tracking variables ($eligibleRules, $hasConditionBlocked, $earliestFutureDue):
When every candidate for an item takes one of those paths, the reschedule at the end of the method is skipped, because it is guarded by !$hasConditionBlocked && $earliestFutureDue !== null and both are still falsy.
Impact
next_transition_at stays in the past indefinitely. The item is re-fetched and fully re-evaluated on every scheduler run, forever.
This is more than wasted work: fetchOverdueCandidates() applies setLimit(50). Permanently-stuck rows occupy slots in that window on every run, so as they accumulate they crowd out items that could actually fire. Under load the scheduler starves.
Open design question
Leaving a filter-excluded item due may be intentional — the article could gain the matching tag later without changing stage, exactly like a condition-blocked item. If so, the two cases should be treated consistently and documented; right now condition-blocked is a deliberate, commented decision while filter-excluded is silent fallthrough.
Either way the starvation problem needs solving. One option: give stuck items a short re-check interval rather than leaving next_transition_at in the past.
Version
5.4
Expected result
No response
Actual result
No response
System Information
No response
Additional Comments
No response