Skip to content

Commit 73332c5

Browse files
committed
simplify
1 parent cf94f98 commit 73332c5

File tree

1 file changed

+19
-76
lines changed

1 file changed

+19
-76
lines changed

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

Lines changed: 19 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
from models_library.projects import ProjectID
55
from pydantic import TypeAdapter
66
from servicelib.common_headers import UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
7-
from servicelib.utils import logged_gather
7+
from servicelib.logging_utils import log_catch, log_context
88

99
from ..projects import _projects_service
10-
from ..projects.exceptions import ProjectLockError, ProjectNotFoundError
1110
from ..redis import get_redis_lock_manager_client
1211
from ..resource_manager.registry import (
1312
RedisResourceRegistry,
1413
)
15-
from .settings import GUEST_USER_RC_LOCK_FORMAT
1614

1715
_logger = logging.getLogger(__name__)
1816

@@ -22,15 +20,12 @@ async def remove_disconnected_user_resources(
2220
) -> None:
2321
lock_manager = get_redis_lock_manager_client(app)
2422

25-
#
26-
# In redis jargon, every entry is denoted as "key"
27-
# - A key can contain one or more fields: name-value pairs
28-
# - A key can have a limited livespan by setting the Time-to-live (TTL) which
29-
# is automatically decreasing
30-
# - Every user can open multiple sessions (e.g. in different tabs and/or browser) and
31-
# each session is hierarchically represented in the redis registry with two keys:
32-
# - "alive" is a string that keeps a TLL of the user session
33-
# - "resources" is a hash toto keep project and websocket ids
23+
# NOTE:
24+
# Each user session is represented in the redis registry with two keys:
25+
# - "alive" is a string that keeps a TTL of the user session
26+
# - "resources" is a redis hash to keep project and websocket ids attached to the user session
27+
# when the alive key expires, it means the user session is disconnected
28+
# and the resources attached to that user session shall be closed and removed
3429
#
3530

3631
_, dead_user_sessions = await registry.get_all_resource_keys()
@@ -40,38 +35,15 @@ async def remove_disconnected_user_resources(
4035
for dead_session in dead_user_sessions:
4136
user_id = int(dead_session["user_id"])
4237

43-
if await lock_manager.lock(
44-
GUEST_USER_RC_LOCK_FORMAT.format(user_id=user_id)
45-
).locked():
46-
_logger.info(
47-
"Skipping garbage-collecting %s since it is still locked",
48-
f"{user_id=}",
49-
)
50-
continue
51-
5238
# (0) If key has no resources => remove from registry and continue
5339
resources = await registry.get_resources(dead_session)
5440
if not resources:
5541
await registry.remove_key(dead_session)
5642
continue
5743

58-
# (1,2) CAREFULLY releasing every resource acquired by the expired key
59-
_logger.info(
60-
"%s expired. Checking resources to cleanup",
61-
f"{dead_session=}",
62-
)
63-
6444
for resource_name, resource_value in resources.items():
65-
# Releasing a resource consists of two steps
66-
# - (1) release actual resource (e.g. stop service, close project, deallocate memory, etc)
67-
# - (2) remove resource field entry in expired key registry after (1) is completed.
68-
69-
# collects a list of keys for (2)
70-
keys_to_update = [
71-
dead_session,
72-
]
73-
74-
# (1) releasing acquired resources
45+
# (1) releasing acquired resources (currently only projects),
46+
# that means closing project for the disconnected user
7547
_logger.info(
7648
"(1) Releasing resource %s:%s acquired by expired %s",
7749
f"{resource_name=}",
@@ -82,46 +54,17 @@ async def remove_disconnected_user_resources(
8254
if resource_name == "project_id":
8355
# inform that the project can be closed on the backend side
8456
#
85-
try:
86-
_logger.info(
87-
"Closing project '%s' of user %s", resource_value, user_id
88-
)
57+
project_id = TypeAdapter(ProjectID).validate_python(resource_value)
58+
with log_catch(_logger, reraise=False), log_context(
59+
_logger,
60+
logging.INFO,
61+
"Closing project {project_id} for user {user_id=}",
62+
):
8963
await _projects_service.close_project_for_user(
90-
user_id,
91-
TypeAdapter(ProjectID).validate_python(f"{resource_value}"),
92-
dead_session["client_session_id"],
93-
app,
64+
user_id=user_id,
65+
project_uuid=project_id,
66+
client_session_id=dead_session["client_session_id"],
67+
app=app,
9468
simcore_user_agent=UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE,
9569
wait_for_service_closed=True,
9670
)
97-
98-
except (ProjectNotFoundError, ProjectLockError) as err:
99-
_logger.warning(
100-
(
101-
"Could not remove project interactive services user_id=%s "
102-
"project_uuid=%s. Check the logs above for details [%s]"
103-
),
104-
user_id,
105-
resource_value,
106-
err,
107-
)
108-
109-
# (2) remove resource field in collected keys since (1) is completed
110-
_logger.info(
111-
"(2) Removing field for released resource %s:%s from registry keys: %s",
112-
f"{resource_name=}",
113-
f"{resource_value=}",
114-
keys_to_update,
115-
)
116-
await logged_gather(
117-
*[
118-
registry.remove_resource(key, resource_name)
119-
for key in keys_to_update
120-
],
121-
reraise=False,
122-
)
123-
124-
# NOTE:
125-
# - if releasing a resource (1) fails, the resource is not removed from the registry and it allows GC to try in next round
126-
# - if any task in (2) fails, GC will clean them up in next round as well
127-
# - if all resource fields are removed from a key, next GC iteration will remove the key (see (0))

0 commit comments

Comments
 (0)