Skip to content

Commit 653a3b3

Browse files
pedro-psbclaude
authored andcommitted
Add regression test for finalizer task racing ahead of waiting siblings
A finalizer task (exclusive resource lock) can start before sibling tasks (shared resource lock) have completed if they are blocked on a separate exclusive resource. In the Redis worker, this bypasses FIFO ordering because the sibling's shared resource is never added to blocked_shared when the blocking reason is its exclusive resource. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 42190f4 commit 653a3b3

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

pulpcore/app/tasks/test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import signal
55
import time
66
from pulpcore.app.models import TaskGroup
7+
from pulpcore.app.models import Task
78
from pulpcore.tasking.tasks import dispatch
9+
from pulpcore.constants import TASK_STATES
810

911

1012
def dummy_task():
@@ -36,6 +38,32 @@ def dummy_group_task(inbetween=3, intervals=None):
3638
time.sleep(inbetween)
3739

3840

41+
def group_finalizer_task():
42+
"""Raise if any sibling task in the group has not yet completed."""
43+
task = Task.current()
44+
task_group = TaskGroup.current()
45+
if task_group.tasks.exclude(pk=task.pk).exclude(state=TASK_STATES.COMPLETED).exists():
46+
raise Exception("Not all sibling tasks have completed.")
47+
48+
49+
def group_task_with_finalizer(resource):
50+
"""Dispatch one sibling (shared on resource) then a finalizer (exclusive on resource)."""
51+
task_group = TaskGroup.current()
52+
unique = f"{resource}:0"
53+
# This first task will hold some lock
54+
dispatch(sleep, args=(0.5,), exclusive_resources=[unique])
55+
# Which will force the non-finalizer group task to wait for it
56+
dispatch(
57+
sleep,
58+
args=(0,),
59+
task_group=task_group,
60+
shared_resources=[resource],
61+
exclusive_resources=[unique],
62+
)
63+
# The finalizer is trigerred very closely, but should not start before
64+
dispatch(group_finalizer_task, task_group=task_group, exclusive_resources=[resource])
65+
66+
3967
def missing_worker():
4068
"""
4169
Simulates a worker crash by sending SIGKILL to parent process and itself.

pulpcore/tests/functional/api/test_tasking.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,18 @@ def test_scope_task_groups(pulpcore_bindings, task_group, gen_user):
462462
assert response.count == 1
463463

464464

465+
@pytest.mark.parallel
466+
def test_finalizer_task_runs_after_all_siblings(dispatch_task_group, monitor_task_group):
467+
"""
468+
A finalizer task that holds an exclusive resource lock should not start before all
469+
sibling tasks holding shared locks on that resource have completed.
470+
"""
471+
resource = str(uuid4())
472+
group_task = "pulpcore.app.tasks.test.group_task_with_finalizer"
473+
tgroup_href = dispatch_task_group(group_task, args=(resource,))
474+
monitor_task_group(tgroup_href)
475+
476+
465477
@pytest.mark.parallel
466478
def test_cancel_task_group(pulpcore_bindings, dispatch_task_group, gen_user):
467479
"""Test that task groups can be canceled."""

0 commit comments

Comments
 (0)