Skip to content

Commit 9b717ce

Browse files
author
Andrei Neagu
committed
added new on_cancelled event for deferred tasks
1 parent 749b1ea commit 9b717ce

File tree

4 files changed

+25
-5
lines changed

4 files changed

+25
-5
lines changed

packages/service-library/src/servicelib/deferred_tasks/__init__.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,16 @@
2222
- `cancel`: (called by the user) [optional]:
2323
send a message to cancel the current task. A warning will be logged but no call to either
2424
`on_result` or `on_finished_with_error` will occur.
25+
- `on_cancelled` (called by state `ManuallyCancelled`) [optional] {can be overwritten by the user}:
26+
called after the cancellation is handled by the worker executing the `run`
2527
2628
2729
## DeferredHandler lifecycle
2830
2931
```mermaid
3032
stateDiagram-v2
31-
* --> Scheduled: via [start]
32-
** --> ManuallyCancelled: via [cancel]
33+
(1) --> Scheduled: via [start]
34+
(2) --> ManuallyCancelled: via [cancel]
3335
3436
ManuallyCancelled --> Worker: attempts to cancel task in
3537
@@ -41,9 +43,10 @@
4143
ErrorResult --> FinishedWithError: gives up when out of retries or if cancelled
4244
Worker --> DeferredResult: success
4345
44-
DeferredResult --> °: calls [on_result]
45-
FinishedWithError --> °°: calls [on_finished_with_error]
46-
Worker --> °°°: task cancelled
46+
DeferredResult --> (3): calls [on_result]
47+
FinishedWithError --> (4): calls [on_finished_with_error]
48+
Worker --> Removed*: task cancelled
49+
Removed* --> (5): calls [on_cancelled]
4750
```
4851
4952
### States
@@ -57,6 +60,7 @@
5760
- `FinishedWIthError`: logs error, invokes `on_finished_with_error` and removes the schedule
5861
- `DeferredResult`: invokes `on_result` and removes the schedule
5962
- `ManuallyCancelled`: sends message to all instances to cancel. The instance handling the task will cancel the task and remove the schedule
63+
- `Removed*`: is a fake state that does not exist only used to convey the information that the cancellation event is triggered after removal
6064
"""
6165

6266
from ._base_deferred_handler import (

packages/service-library/src/servicelib/deferred_tasks/_base_deferred_handler.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ async def on_finished_with_error(
9595
NOTE: by design the default action is to do nothing
9696
"""
9797

98+
@classmethod
99+
@abstractmethod
100+
async def on_cancelled(cls, context: DeferredContext) -> None:
101+
"""called after handling ``cancel`` request by the copy executing ``run``"""
102+
98103
@classmethod
99104
async def cancel(cls, task_uid: TaskUID) -> None:
100105
"""cancels a deferred"""

packages/service-library/src/servicelib/deferred_tasks/_deferred_manager.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,11 @@ async def _fs_handle_manually_cancelled( # pylint:disable=method-hidden
579579
_logger.info("Found and cancelled run for '%s'", task_uid)
580580
await self.__remove_task(task_uid, task_schedule)
581581

582+
subclass = self.__get_subclass(task_schedule.class_unique_reference)
583+
deferred_context = self.__get_deferred_context(task_schedule.start_context)
584+
with log_catch(_logger, reraise=False):
585+
await subclass.on_cancelled(deferred_context)
586+
582587
async def __is_present(self, task_uid: TaskUID) -> bool:
583588
task_schedule: TaskScheduleModel | None = await self._task_tracker.get(task_uid)
584589
return task_schedule is not None

packages/service-library/tests/deferred_tasks/test__base_deferred_handler.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class MockKeys(StrAutoEnum):
4646
RUN_DEFERRED_AFTER_HANDLER = auto()
4747
ON_DEFERRED_RESULT = auto()
4848
ON_FINISHED_WITH_ERROR = auto()
49+
ON_CANCELLED = auto()
4950

5051

5152
@pytest.fixture
@@ -132,6 +133,10 @@ async def run(cls, context: DeferredContext) -> Any:
132133
async def on_result(cls, result: Any, context: DeferredContext) -> None:
133134
mocks[MockKeys.ON_DEFERRED_RESULT](result, context)
134135

136+
@classmethod
137+
async def on_cancelled(cls, context: DeferredContext) -> None:
138+
mocks[MockKeys.ON_CANCELLED](context)
139+
135140
@classmethod
136141
async def on_finished_with_error(
137142
cls, error: TaskResultError, context: DeferredContext
@@ -324,6 +329,7 @@ async def _run_to_cancel(_: DeferredContext) -> None:
324329
await _assert_mock_call(mocks, key=MockKeys.RUN_DEFERRED_BEFORE_HANDLER, count=1)
325330
await mocked_deferred_handler.cancel(task_uid)
326331

332+
await _assert_mock_call(mocks, key=MockKeys.ON_CANCELLED, count=1)
327333
await _assert_mock_call(mocks, key=MockKeys.ON_FINISHED_WITH_ERROR, count=0)
328334

329335
assert (

0 commit comments

Comments
 (0)