Skip to content

Commit 4bea31f

Browse files
committed
🎨 Refactor background task management: Introduce setup_periodic_task utility for cleaner task setup and teardown
1 parent 9103583 commit 4bea31f

File tree

5 files changed

+58
-88
lines changed

5 files changed

+58
-88
lines changed

services/web/server/src/simcore_service_webserver/garbage_collector/_tasks_api_keys.py

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,22 @@
33
44
"""
55

6-
import asyncio
76
import logging
8-
from collections.abc import AsyncIterator, Callable
7+
from collections.abc import AsyncIterator
98
from datetime import timedelta
109

1110
from aiohttp import web
12-
from servicelib.async_utils import cancel_wait_task
1311
from servicelib.background_task_utils import exclusive_periodic
1412
from servicelib.logging_utils import log_context
1513
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk
1614

1715
from ..api_keys import api_keys_service
16+
from ._tasks_utils import CleanupContextFunc, setup_periodic_task
1817

1918
_logger = logging.getLogger(__name__)
2019

21-
CleanupContextFunc = Callable[[web.Application], AsyncIterator[None]]
2220

23-
24-
async def _run_task(app: web.Application):
25-
"""Checks expiration dates and updates user status"""
21+
async def _prune_expired_api_keys(app: web.Application):
2622
if deleted := await api_keys_service.prune_expired_api_keys(app):
2723
# broadcast force logout of user_id
2824
for api_key in deleted:
@@ -46,24 +42,9 @@ async def _cleanup_ctx_fun(app: web.Application) -> AsyncIterator[None]:
4642
)
4743
async def _prune_expired_api_keys_periodically() -> None:
4844
with log_context(_logger, logging.INFO, "Pruning expired API keys"):
49-
await _run_task(app)
50-
51-
# setup
52-
task_name = _prune_expired_api_keys_periodically.__name__
53-
54-
task = asyncio.create_task(
55-
_prune_expired_api_keys_periodically(),
56-
name=task_name,
57-
)
58-
59-
# prevents premature garbage collection of the task
60-
app_task_key = f"tasks.{task_name}"
61-
app[app_task_key] = task
62-
63-
yield
45+
await _prune_expired_api_keys(app)
6446

65-
# tear-down
66-
await cancel_wait_task(task)
67-
app.pop(app_task_key, None)
47+
async for _ in setup_periodic_task(app, _prune_expired_api_keys_periodically):
48+
yield
6849

6950
return _cleanup_ctx_fun

services/web/server/src/simcore_service_webserver/garbage_collector/_tasks_core.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,21 @@
44
Specifics of the gc implementation should go into garbage_collector_core.py
55
"""
66

7-
import asyncio
87
import logging
9-
from collections.abc import AsyncIterator, Callable
8+
from collections.abc import AsyncIterator
109
from datetime import timedelta
1110

1211
from aiohttp import web
13-
from servicelib.async_utils import cancel_wait_task
1412
from servicelib.background_task_utils import exclusive_periodic
1513
from servicelib.logging_utils import log_context
1614
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk
1715

1816
from ._core import collect_garbage
17+
from ._tasks_utils import CleanupContextFunc, setup_periodic_task
1918
from .settings import GarbageCollectorSettings, get_plugin_settings
2019

2120
_logger = logging.getLogger(__name__)
2221

23-
CleanupContextFunc = Callable[[web.Application], AsyncIterator[None]]
24-
2522

2623
def create_background_task_for_garbage_collection() -> CleanupContextFunc:
2724

@@ -38,22 +35,7 @@ async def _collect_garbage_periodically() -> None:
3835
with log_context(_logger, logging.INFO, "Garbage collect cycle"):
3936
await collect_garbage(app)
4037

41-
# setup
42-
task_name = _collect_garbage_periodically.__name__
43-
44-
task = asyncio.create_task(
45-
_collect_garbage_periodically(),
46-
name=task_name,
47-
)
48-
49-
# prevents premature garbage collection of the task
50-
app_task_key = f"tasks.{task_name}"
51-
app[app_task_key] = task
52-
53-
yield
54-
55-
# tear-down
56-
await cancel_wait_task(task)
57-
app.pop(app_task_key, None)
38+
async for _ in setup_periodic_task(app, _collect_garbage_periodically):
39+
yield
5840

5941
return _cleanup_ctx_fun

services/web/server/src/simcore_service_webserver/garbage_collector/_tasks_trash.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,20 @@
33
44
"""
55

6-
import asyncio
76
import logging
8-
from collections.abc import AsyncIterator, Callable
7+
from collections.abc import AsyncIterator
98
from datetime import timedelta
109

1110
from aiohttp import web
12-
from servicelib.async_utils import cancel_wait_task
1311
from servicelib.background_task_utils import exclusive_periodic
1412
from servicelib.logging_utils import log_context
1513
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk
1614

1715
from ..trash import trash_service
16+
from ._tasks_utils import CleanupContextFunc, setup_periodic_task
1817

1918
_logger = logging.getLogger(__name__)
2019

21-
CleanupContextFunc = Callable[[web.Application], AsyncIterator[None]]
22-
2320

2421
def create_background_task_to_prune_trash(wait_s: float) -> CleanupContextFunc:
2522

@@ -35,22 +32,7 @@ async def _prune_trash_periodically() -> None:
3532
with log_context(_logger, logging.INFO, "Deleting expired trashed items"):
3633
await trash_service.safe_delete_expired_trash_as_admin(app)
3734

38-
# setup
39-
task_name = _prune_trash_periodically.__name__
40-
41-
task = asyncio.create_task(
42-
_prune_trash_periodically(),
43-
name=task_name,
44-
)
45-
46-
# prevents premature garbage collection of the task
47-
app_task_key = f"tasks.{task_name}"
48-
app[app_task_key] = task
49-
50-
yield
51-
52-
# tear-down
53-
await cancel_wait_task(task)
54-
app.pop(app_task_key, None)
35+
async for _ in setup_periodic_task(app, _prune_trash_periodically):
36+
yield
5537

5638
return _cleanup_ctx_fun

services/web/server/src/simcore_service_webserver/garbage_collector/_tasks_users.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@
33
44
"""
55

6-
import asyncio
76
import logging
87
from collections.abc import AsyncIterator, Callable
98
from datetime import timedelta
109

1110
from aiohttp import web
1211
from models_library.users import UserID
13-
from servicelib.async_utils import cancel_wait_task
1412
from servicelib.background_task_utils import exclusive_periodic
1513
from servicelib.logging_utils import get_log_record_extra, log_context
1614
from simcore_service_webserver.redis import get_redis_lock_manager_client_sdk
1715

1816
from ..login import login_service
1917
from ..security import security_service
2018
from ..users.api import update_expired_users
19+
from ._tasks_utils import CleanupContextFunc, setup_periodic_task
2120

2221
_logger = logging.getLogger(__name__)
2322

@@ -84,22 +83,7 @@ async def _update_expired_users_periodically() -> None:
8483
with log_context(_logger, logging.INFO, "Updating expired users"):
8584
await _update_expired_users(app)
8685

87-
# setup
88-
task_name = _update_expired_users_periodically.__name__
89-
90-
task = asyncio.create_task(
91-
_update_expired_users_periodically(),
92-
name=task_name,
93-
)
94-
95-
# prevents premature garbage collection of the task
96-
app_task_key = f"tasks.{task_name}"
97-
app[app_task_key] = task
98-
99-
yield
100-
101-
# tear-down
102-
await cancel_wait_task(task)
103-
app.pop(app_task_key, None)
86+
async for _ in setup_periodic_task(app, _update_expired_users_periodically):
87+
yield
10488

10589
return _cleanup_ctx_fun
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
Common utilities for background task management in garbage collector
3+
"""
4+
5+
import asyncio
6+
from collections.abc import AsyncIterator, Callable, Coroutine
7+
8+
from aiohttp import web
9+
from servicelib.async_utils import cancel_wait_task
10+
11+
CleanupContextFunc = Callable[[web.Application], AsyncIterator[None]]
12+
13+
14+
async def setup_periodic_task(
15+
app: web.Application,
16+
periodic_task_coro: Callable[[], Coroutine[None, None, None]],
17+
) -> AsyncIterator[None]:
18+
"""
19+
Generic setup and teardown for periodic background tasks.
20+
21+
Args:
22+
app: The aiohttp web application
23+
periodic_task_coro: The periodic task coroutine function (already decorated with @exclusive_periodic)
24+
"""
25+
# setup
26+
task_name = periodic_task_coro.__name__
27+
28+
task = asyncio.create_task(
29+
periodic_task_coro(),
30+
name=task_name,
31+
)
32+
33+
# prevents premature garbage collection of the task
34+
app_task_key = f"tasks.{task_name}"
35+
app[app_task_key] = task
36+
37+
yield
38+
39+
# tear-down
40+
await cancel_wait_task(task)
41+
app.pop(app_task_key, None)

0 commit comments

Comments
 (0)