Skip to content

Commit ed21eb0

Browse files
Refactors project document cleanup to use shared registry utility
Simplifies logic for identifying opened projects by introducing a shared utility function. Improves reliability of project document removal by ensuring active projects are preserved. Adds comprehensive unit tests to verify correct behavior in various edge cases.
1 parent 04110d4 commit ed21eb0

File tree

4 files changed

+513
-19
lines changed

4 files changed

+513
-19
lines changed

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
88
DynamicServiceStop,
99
)
10-
from models_library.projects import ProjectID
1110
from models_library.projects_nodes_io import NodeID
1211
from servicelib.common_headers import UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
1312
from servicelib.logging_errors import create_troubleshootting_log_kwargs
@@ -21,6 +20,7 @@
2120
)
2221
from ..projects.api import has_user_project_access_rights
2322
from ..resource_manager.registry import RedisResourceRegistry
23+
from ..resource_manager.registry_utils import list_opened_project_ids
2424
from ..users import users_service
2525
from ..users.exceptions import UserNotFoundError
2626

@@ -71,16 +71,6 @@ async def _remove_service(
7171
)
7272

7373

74-
async def _list_opened_project_ids(registry: RedisResourceRegistry) -> list[ProjectID]:
75-
opened_projects: list[ProjectID] = []
76-
all_session_alive, _ = await registry.get_all_resource_keys()
77-
for alive_session in all_session_alive:
78-
resources = await registry.get_resources(alive_session)
79-
if "project_id" in resources:
80-
opened_projects.append(ProjectID(resources["project_id"]))
81-
return opened_projects
82-
83-
8474
async def remove_orphaned_services(
8575
registry: RedisResourceRegistry, app: web.Application
8676
) -> None:
@@ -105,7 +95,7 @@ async def remove_orphaned_services(
10595
service.node_uuid: service for service in running_services
10696
}
10797

108-
known_opened_project_ids = await _list_opened_project_ids(registry)
98+
known_opened_project_ids = await list_opened_project_ids(registry)
10999

110100
# NOTE: Always skip orphan repmoval when `list_node_ids_in_project` raises an error.
111101
# Why? If a service is running but the nodes form the correspondign project cannot be listed,

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
get_redis_document_manager_client_sdk,
2626
get_redis_lock_manager_client_sdk,
2727
)
28+
from ..resource_manager.registry import get_registry
29+
from ..resource_manager.registry_utils import list_opened_project_ids
2830
from ..socketio._utils import get_socket_server
2931
from . import _projects_repository
3032

@@ -108,6 +110,11 @@ async def remove_project_documents_as_admin(app: web.Application) -> None:
108110
# Get socketio server instance
109111
sio = get_socket_server(app)
110112

113+
# Get known opened projects ids based on Redis resources table
114+
registry = get_registry(app)
115+
known_opened_project_ids = await list_opened_project_ids(registry)
116+
known_opened_project_ids = set(known_opened_project_ids)
117+
111118
projects_removed = 0
112119

113120
# Scan through all project document keys
@@ -125,7 +132,15 @@ async def remove_project_documents_as_admin(app: web.Application) -> None:
125132
project_uuid = ProjectID(project_uuid_str)
126133
project_room = SocketIORoomStr.from_project_id(project_uuid)
127134

128-
# Check if there are any users connected to this project room
135+
# 1. CHECK - Check if the project UUID is in the known opened projects
136+
if project_uuid in known_opened_project_ids:
137+
_logger.debug(
138+
"Project %s is in Redis Resources table (which means Project is opened), keeping document",
139+
project_uuid,
140+
)
141+
continue
142+
143+
# 2. CHECK - Check if there are any users connected to this project room
129144
try:
130145
# Get all session IDs (socket IDs) in the project room
131146
room_sessions = list(
@@ -141,17 +156,16 @@ async def remove_project_documents_as_admin(app: web.Application) -> None:
141156
project_uuid,
142157
)
143158
else:
144-
_logger.debug(
145-
"Project %s has %d connected users, keeping document",
159+
_logger.error(
160+
"Project %s has %d connected users in the socket io room (This is not expected, as project resource is not in the Redis Resources table), keeping document just in case",
146161
project_uuid,
147162
len(room_sessions),
148163
)
149164

150-
except (KeyError, AttributeError, ValueError) as e:
151-
_logger.warning(
152-
"Failed to check room participants for project %s: %s",
165+
except (KeyError, AttributeError, ValueError):
166+
_logger.exception(
167+
"Failed to check room participants for project %s",
153168
project_uuid,
154-
str(e),
155169
)
156170
continue
157171

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import logging
2+
3+
from models_library.projects import ProjectID
4+
5+
from .registry import RedisResourceRegistry
6+
7+
_logger = logging.getLogger(__name__)
8+
9+
10+
async def list_opened_project_ids(registry: RedisResourceRegistry) -> list[ProjectID]:
11+
"""Lists all project IDs that are currently opened in active sessions."""
12+
opened_projects: list[ProjectID] = []
13+
all_session_alive, _ = await registry.get_all_resource_keys()
14+
for alive_session in all_session_alive:
15+
resources = await registry.get_resources(alive_session)
16+
if "project_id" in resources:
17+
opened_projects.append(ProjectID(resources["project_id"]))
18+
return opened_projects

0 commit comments

Comments
 (0)