Skip to content

Commit 8e59daa

Browse files
committed
re-use same closing function
1 parent c2de0e1 commit 8e59daa

File tree

4 files changed

+55
-39
lines changed

4 files changed

+55
-39
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from aiohttp import web
66
from servicelib.logging_utils import log_catch, log_context
77

8-
from ..resource_manager.registry import RedisResourceRegistry, get_registry
8+
from ..resource_manager.registry import get_registry
99
from ._core_disconnected import remove_disconnected_user_resources
1010
from ._core_guests import remove_users_manually_marked_as_guests
1111
from ._core_orphans import remove_orphaned_services
@@ -34,24 +34,31 @@ async def collect_garbage(app: web.Application):
3434
The field `garbage_collection_interval_seconds` defines the interval at which this
3535
function will be called.
3636
"""
37-
registry: RedisResourceRegistry = get_registry(app)
37+
registry = get_registry(app)
3838

39-
with log_catch(_logger, reraise=False), log_context(
40-
_logger, logging.INFO, "Step 1: Removes disconnected user sessions"
39+
with (
40+
log_catch(_logger, reraise=False),
41+
log_context(
42+
_logger, logging.INFO, "Step 1: Removes disconnected user sessions"
43+
),
4144
):
4245
# Triggers signal to close possible pending opened projects
4346
# Removes disconnected GUEST users after they finished their sessions
4447
await remove_disconnected_user_resources(registry, app)
4548

46-
with log_catch(_logger, reraise=False), log_context(
47-
_logger, logging.INFO, "Step 2: Removes users manually marked for removal"
49+
with (
50+
log_catch(_logger, reraise=False),
51+
log_context(
52+
_logger, logging.INFO, "Step 2: Removes users manually marked for removal"
53+
),
4854
):
4955
# if a user was manually marked as GUEST it needs to be
5056
# removed together with all the associated projects
5157
await remove_users_manually_marked_as_guests(registry, app)
5258

53-
with log_catch(_logger, reraise=False), log_context(
54-
_logger, logging.INFO, "Step 3: Removes orphaned services"
59+
with (
60+
log_catch(_logger, reraise=False),
61+
log_context(_logger, logging.INFO, "Step 3: Removes orphaned services"),
5562
):
5663
# For various reasons, some services remain pending after
5764
# the projects are closed or the user was disconencted.

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

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import logging
22

33
from aiohttp import web
4-
from redis.asyncio import Redis
54
from servicelib.common_headers import UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
65
from servicelib.utils import logged_gather
76

8-
from ..projects._projects_service import remove_project_dynamic_services
7+
from ..projects import _projects_service
98
from ..projects.exceptions import ProjectLockError, ProjectNotFoundError
109
from ..redis import get_redis_lock_manager_client
1110
from ..resource_manager.registry import (
1211
RedisResourceRegistry,
13-
ResourcesDict,
14-
UserSessionDict,
1512
)
1613
from ._core_guests import remove_guest_user_with_all_its_resources
1714
from .settings import GUEST_USER_RC_LOCK_FORMAT
@@ -22,7 +19,7 @@
2219
async def remove_disconnected_user_resources(
2320
registry: RedisResourceRegistry, app: web.Application
2421
) -> None:
25-
lock_manager: Redis = get_redis_lock_manager_client(app)
22+
lock_manager = get_redis_lock_manager_client(app)
2623

2724
#
2825
# In redis jargon, every entry is denoted as "key"
@@ -40,10 +37,9 @@ async def remove_disconnected_user_resources(
4037

4138
# clean up all resources of expired keys
4239
for dead_session in all_sessions_dead:
43-
4440
try:
4541
user_id = int(dead_session["user_id"])
46-
except (KeyError, ValueError): # noqa: PERF203
42+
except (KeyError, ValueError):
4743
continue
4844

4945
if await lock_manager.lock(
@@ -56,7 +52,7 @@ async def remove_disconnected_user_resources(
5652
continue
5753

5854
# (0) If key has no resources => remove from registry and continue
59-
resources: ResourcesDict = await registry.get_resources(dead_session)
55+
resources = await registry.get_resources(dead_session)
6056
if not resources:
6157
await registry.remove_key(dead_session)
6258
continue
@@ -81,7 +77,7 @@ async def remove_disconnected_user_resources(
8177
# In that case, the resource is released by THE LAST DYING KEY
8278
# (we could call this the "last-standing-man" pattern! :-) )
8379
#
84-
other_sessions_with_this_resource: list[UserSessionDict] = [
80+
other_sessions_with_this_resource = [
8581
k
8682
for k in await registry.find_keys((resource_name, f"{resource_value}"))
8783
if k != dead_session
@@ -107,17 +103,15 @@ async def remove_disconnected_user_resources(
107103
#
108104
try:
109105
_logger.info(
110-
"Closing services for project '%s'", resource_value
106+
"Closing project '%s' of user %s", resource_value, user_id
111107
)
112-
await remove_project_dynamic_services(
113-
user_id=user_id,
114-
project_uuid=f"{resource_value}",
115-
app=app,
108+
await _projects_service.try_close_project_for_user(
109+
user_id,
110+
f"{resource_value}",
111+
dead_session["client_session_id"],
112+
app,
116113
simcore_user_agent=UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE,
117-
user_name={
118-
"first_name": "garbage",
119-
"last_name": "collector",
120-
},
114+
wait_for_service_closed=True,
121115
)
122116

123117
except (ProjectNotFoundError, ProjectLockError) as err:

services/web/server/src/simcore_service_webserver/projects/_projects_service.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,8 @@ async def try_close_project_for_user(
15901590
client_session_id: str,
15911591
app: web.Application,
15921592
simcore_user_agent: str,
1593+
*,
1594+
wait_for_service_closed: bool = False,
15931595
):
15941596
with managed_resource(user_id, client_session_id, app) as user_session:
15951597
current_user_session = user_session.get_id()
@@ -1609,23 +1611,33 @@ async def try_close_project_for_user(
16091611
return
16101612

16111613
# remove the project from our list of opened ones
1612-
with log_context(
1613-
log, logging.DEBUG, f"removing {user_id=} session for {project_uuid=}"
1614-
):
1615-
await user_session.remove(key=PROJECT_ID_KEY)
1614+
await user_session.remove(key=PROJECT_ID_KEY)
16161615

16171616
# check it is not opened by someone else
16181617
all_user_sessions_with_project.remove(current_user_session)
16191618
log.debug("remaining user_to_session_ids: %s", all_user_sessions_with_project)
16201619
if not all_user_sessions_with_project:
16211620
# NOTE: depending on the garbage collector speed, it might already be removing it
1622-
fire_and_forget_task(
1623-
remove_project_dynamic_services(
1624-
user_id, project_uuid, app, simcore_user_agent
1625-
),
1626-
task_suffix_name=f"remove_project_dynamic_services_{user_id=}_{project_uuid=}",
1627-
fire_and_forget_tasks_collection=app[APP_FIRE_AND_FORGET_TASKS_KEY],
1621+
remove_services_task = remove_project_dynamic_services(
1622+
user_id, project_uuid, app, simcore_user_agent
16281623
)
1624+
if wait_for_service_closed:
1625+
# wait for the task to finish
1626+
await remove_services_task
1627+
else:
1628+
fire_and_forget_task(
1629+
remove_services_task,
1630+
task_suffix_name=f"remove_project_dynamic_services_{user_id=}_{project_uuid=}",
1631+
fire_and_forget_tasks_collection=app[APP_FIRE_AND_FORGET_TASKS_KEY],
1632+
)
1633+
# notify users that project is now closed
1634+
project = await get_project_for_user(
1635+
app,
1636+
project_uuid,
1637+
user_id,
1638+
include_state=True,
1639+
)
1640+
await notify_project_state_update(app, project)
16291641

16301642

16311643
async def _get_project_share_state(

services/web/server/src/simcore_service_webserver/resource_manager/registry.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""
1515

1616
import logging
17+
from typing import TypeAlias
1718

1819
import redis.asyncio as aioredis
1920
from aiohttp import web
@@ -55,6 +56,10 @@ class ResourcesDict(TypedDict, total=False):
5556
socket_id: str
5657

5758

59+
AliveSessions: TypeAlias = list[UserSessionDict]
60+
DeadSessions: TypeAlias = list[UserSessionDict]
61+
62+
5863
class RedisResourceRegistry:
5964
"""Keeps a record of connected sockets per user
6065
@@ -158,9 +163,7 @@ async def remove_key(self, key: UserSessionDict) -> None:
158163
f"{self._hash_key(key)}:{_ALIVE_SUFFIX}",
159164
)
160165

161-
async def get_all_resource_keys(
162-
self,
163-
) -> tuple[list[UserSessionDict], list[UserSessionDict]]:
166+
async def get_all_resource_keys(self) -> tuple[AliveSessions, DeadSessions]:
164167
alive_keys = [
165168
self._decode_hash_key(hash_key)
166169
async for hash_key in self.client.scan_iter(match=f"*:{_ALIVE_SUFFIX}")

0 commit comments

Comments
 (0)