Skip to content

Commit babea4a

Browse files
committed
prune trash
1 parent 0571243 commit babea4a

File tree

3 files changed

+34
-39
lines changed

3 files changed

+34
-39
lines changed

packages/service-library/src/servicelib/background_task.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ def periodic(
3838
*,
3939
interval: datetime.timedelta,
4040
raise_on_error: bool = False,
41-
early_wake_up_event: asyncio.Event | None = None,
41+
early_wake_up_event: (
42+
asyncio.Event | None
43+
) = None, # TODO: i would argue that this should be a different decorator instead of an optional arguments since with this event,
44+
# the funciton is not periodic anymore but rahter repeatedly triggered by the event
4245
) -> Callable[
4346
[Callable[P, Coroutine[Any, Any, None]]], Callable[P, Coroutine[Any, Any, None]]
4447
]:
45-
"""Calls the function periodically with a given interval.
48+
"""Calls the function periodically with a given interval or triggered by an early wake-up event.
4649
4750
Arguments:
4851
interval -- the interval between calls
@@ -58,7 +61,7 @@ def periodic(
5861
"""
5962

6063
def _decorator(
61-
func: Callable[P, Coroutine[Any, Any, None]],
64+
coro: Callable[P, Coroutine[Any, Any, None]],
6265
) -> Callable[P, Coroutine[Any, Any, None]]:
6366
class _InternalTryAgain(TryAgain):
6467
# Local exception to prevent reacting to similarTryAgain exceptions raised by the wrapped func
@@ -82,10 +85,10 @@ class _InternalTryAgain(TryAgain):
8285
),
8386
before_sleep=before_sleep_log(_logger, logging.DEBUG),
8487
)
85-
@functools.wraps(func)
88+
@functools.wraps(coro)
8689
async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
8790
with log_catch(_logger, reraise=True):
88-
await func(*args, **kwargs)
91+
await coro(*args, **kwargs)
8992
raise _InternalTryAgain
9093

9194
return _wrapper

packages/service-library/src/servicelib/background_task_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def _decorator(
4343
# Replicas will raise CouldNotAcquireLockError
4444
# SEE https://github.com/ITISFoundation/osparc-simcore/issues/7574
4545
(CouldNotAcquireLockError,),
46-
reason="Multiple instances of the periodic task `{coro.__module__}.{coro.__name__}` are running.",
46+
reason=f"Multiple instances of the periodic task `{coro.__module__}.{coro.__name__}` are running.",
4747
)
4848
@exclusive(
4949
redis_client,
Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""
2-
Scheduled tasks addressing users
2+
Scheduled tasks addressing users
33
44
"""
55

66
import asyncio
77
import logging
88
from collections.abc import AsyncIterator, Callable
9+
from datetime import timedelta
910

1011
from aiohttp import web
12+
from servicelib.async_utils import cancel_wait_task
13+
from servicelib.background_task_utils import exclusive_periodic
1114
from servicelib.logging_utils import log_context
12-
from tenacity import retry
13-
from tenacity.before_sleep import before_sleep_log
14-
from tenacity.wait import wait_exponential
15+
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk
1516

1617
from ..trash import trash_service
1718

@@ -20,45 +21,36 @@
2021
CleanupContextFunc = Callable[[web.Application], AsyncIterator[None]]
2122

2223

23-
_PERIODIC_TASK_NAME = f"{__name__}"
24-
_APP_TASK_KEY = f"{_PERIODIC_TASK_NAME}.task"
24+
def create_background_task_to_prune_trash(wait_s: float) -> CleanupContextFunc:
2525

26+
async def _cleanup_ctx_fun(app: web.Application) -> AsyncIterator[None]:
2627

27-
@retry(
28-
wait=wait_exponential(min=5, max=20),
29-
before_sleep=before_sleep_log(_logger, logging.WARNING),
30-
)
31-
async def _run_task(app: web.Application):
32-
with log_context(_logger, logging.INFO, "Deleting expired trashed items"):
33-
await trash_service.safe_delete_expired_trash_as_admin(app)
34-
35-
36-
async def _run_periodically(app: web.Application, wait_interval_s: float):
37-
while True:
38-
await _run_task(app)
39-
await asyncio.sleep(wait_interval_s)
40-
28+
@exclusive_periodic(
29+
# Function-exclusiveness is required to avoid multiple tasks like thisone running concurrently
30+
get_redis_lock_manager_client_sdk(app),
31+
task_interval=timedelta(seconds=wait_s),
32+
retry_after=timedelta(minutes=5),
33+
)
34+
async def _prune_trash_periodically() -> None:
35+
with log_context(_logger, logging.INFO, "Deleting expired trashed items"):
36+
await trash_service.safe_delete_expired_trash_as_admin(app)
4137

42-
def create_background_task_to_prune_trash(
43-
wait_s: float, task_name: str = _PERIODIC_TASK_NAME
44-
) -> CleanupContextFunc:
45-
async def _cleanup_ctx_fun(
46-
app: web.Application,
47-
) -> AsyncIterator[None]:
4838
# setup
39+
task_name = _prune_trash_periodically.__name__
40+
4941
task = asyncio.create_task(
50-
_run_periodically(app, wait_s),
42+
_prune_trash_periodically(),
5143
name=task_name,
5244
)
53-
app[_APP_TASK_KEY] = task
45+
46+
# prevents premature garbage collection of the task
47+
app_task_key = f"tasks.{task_name}"
48+
app[app_task_key] = task
5449

5550
yield
5651

5752
# tear-down
58-
task.cancel()
59-
try:
60-
await task
61-
except asyncio.CancelledError:
62-
assert task.cancelled() # nosec
53+
await cancel_wait_task(task)
54+
app.pop(app_task_key, None)
6355

6456
return _cleanup_ctx_fun

0 commit comments

Comments
 (0)