Skip to content

Commit b3d1213

Browse files
review @pcrespov
1 parent 88402f5 commit b3d1213

File tree

1 file changed

+81
-78
lines changed

1 file changed

+81
-78
lines changed

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

Lines changed: 81 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from models_library.projects import ProjectID, ProjectTemplateType
1717
from models_library.projects import ProjectType as ProjectTypeAPI
1818
from servicelib.logging_errors import create_troubleshootting_log_kwargs
19+
from servicelib.logging_utils import log_context
1920
from servicelib.redis import (
2021
PROJECT_DB_UPDATE_REDIS_LOCK_KEY,
2122
exclusive,
@@ -102,99 +103,101 @@ async def remove_project_documents_as_admin(app: web.Application) -> None:
102103
checks if there are any users currently connected to the project room via socketio,
103104
and removes documents that have no connected users.
104105
"""
105-
# Get Redis document manager client to access the DOCUMENTS database
106-
redis_client = get_redis_document_manager_client_sdk(app)
106+
with log_context(
107+
_logger,
108+
logging.INFO,
109+
msg="Project document cleanup started",
110+
):
111+
# Get Redis document manager client to access the DOCUMENTS database
112+
redis_client = get_redis_document_manager_client_sdk(app)
107113

108-
# Pattern to match project document keys - looking for keys that contain project UUIDs
109-
project_document_pattern = "projects:*:version"
114+
# Pattern to match project document keys - looking for keys that contain project UUIDs
115+
project_document_pattern = "projects:*:version"
110116

111-
# Get socketio server instance
112-
sio = get_socket_server(app)
117+
# Get socketio server instance
118+
sio = get_socket_server(app)
113119

114-
# Get known opened projects ids based on Redis resources table
115-
registry = get_registry(app)
116-
known_opened_project_ids = await list_opened_project_ids(registry)
117-
known_opened_project_ids_set = set(known_opened_project_ids)
120+
# Get known opened projects ids based on Redis resources table
121+
registry = get_registry(app)
122+
known_opened_project_ids = await list_opened_project_ids(registry)
123+
known_opened_project_ids_set = set(known_opened_project_ids)
118124

119-
projects_removed = 0
125+
projects_removed = 0
120126

121-
# Scan through all project document keys
122-
async for key in redis_client.redis.scan_iter(
123-
match=project_document_pattern, count=1000
124-
):
125-
# Extract project UUID from the key pattern "projects:{project_uuid}:version"
126-
key_str = key.decode("utf-8") if isinstance(key, bytes) else key
127-
match = re.match(r"projects:(?P<project_uuid>[0-9a-f-]+):version", key_str)
128-
129-
if not match:
130-
continue
131-
132-
project_uuid_str = match.group("project_uuid")
133-
project_uuid = ProjectID(project_uuid_str)
134-
project_room = SocketIORoomStr.from_project_id(project_uuid)
135-
136-
# 1. CHECK - Check if the project UUID is in the known opened projects
137-
if project_uuid in known_opened_project_ids_set:
138-
_logger.debug(
139-
"Project %s is in Redis Resources table (which means Project is opened), keeping document",
140-
project_uuid,
141-
)
142-
continue
143-
144-
# 2. CHECK - Check if there are any users connected to this project room
145-
try:
146-
# Get all session IDs (socket IDs) in the project room
147-
room_sessions = list(
148-
sio.manager.get_participants(namespace="/", room=project_room)
149-
)
150-
151-
# If no users are connected to this project room, remove the document
152-
if not room_sessions:
153-
await redis_client.redis.delete(key_str)
154-
projects_removed += 1
155-
_logger.info(
156-
"Removed project document for project %s (no connected users)",
127+
# Scan through all project document keys
128+
async for key in redis_client.redis.scan_iter(
129+
match=project_document_pattern, count=1000
130+
):
131+
# Extract project UUID from the key pattern "projects:{project_uuid}:version"
132+
key_str = key.decode("utf-8") if isinstance(key, bytes) else key
133+
match = re.match(r"projects:(?P<project_uuid>[0-9a-f-]+):version", key_str)
134+
135+
if not match:
136+
continue
137+
138+
project_uuid_str = match.group("project_uuid")
139+
project_uuid = ProjectID(project_uuid_str)
140+
project_room = SocketIORoomStr.from_project_id(project_uuid)
141+
142+
# 1. CHECK - Check if the project UUID is in the known opened projects
143+
if project_uuid in known_opened_project_ids_set:
144+
_logger.debug(
145+
"Project %s is in Redis Resources table (which means Project is opened), keeping document",
157146
project_uuid,
158147
)
159-
else:
160-
# Create a synthetic exception for this unexpected state
161-
unexpected_state_error = RuntimeError(
162-
f"Project {project_uuid} has {len(room_sessions)} connected users but is not in Redis Resources table"
148+
continue
149+
150+
# 2. CHECK - Check if there are any users connected to this project room
151+
try:
152+
# Get all session IDs (socket IDs) in the project room
153+
room_sessions = list(
154+
sio.manager.get_participants(namespace="/", room=project_room)
163155
)
164-
_logger.error(
156+
157+
# If no users are connected to this project room, remove the document
158+
if not room_sessions:
159+
await redis_client.redis.delete(key_str)
160+
projects_removed += 1
161+
_logger.info(
162+
"Removed project document for project %s (no connected users)",
163+
project_uuid,
164+
)
165+
else:
166+
# Create a synthetic exception for this unexpected state
167+
unexpected_state_error = RuntimeError(
168+
f"Project {project_uuid} has {len(room_sessions)} connected users but is not in Redis Resources table"
169+
)
170+
_logger.error(
171+
**create_troubleshootting_log_kwargs(
172+
user_error_msg=f"Project {project_uuid} has {len(room_sessions)} 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",
173+
error=unexpected_state_error,
174+
error_context={
175+
"project_uuid": str(project_uuid),
176+
"project_room": project_room,
177+
"key_str": key_str,
178+
"connected_users_count": len(room_sessions),
179+
"room_sessions": room_sessions[
180+
:5
181+
], # Limit to first 5 sessions for debugging
182+
},
183+
tip="This indicates a potential race condition or inconsistency between the Redis Resources table and socketio room state. Check if the project was recently closed but users are still connected, or if there's a synchronization issue between services.",
184+
)
185+
)
186+
continue
187+
188+
except (KeyError, AttributeError, ValueError) as exc:
189+
_logger.exception(
165190
**create_troubleshootting_log_kwargs(
166-
user_error_msg=f"Project {project_uuid} has {len(room_sessions)} 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",
167-
error=unexpected_state_error,
191+
user_error_msg=f"Failed to check room participants for project {project_uuid}",
192+
error=exc,
168193
error_context={
169194
"project_uuid": str(project_uuid),
170195
"project_room": project_room,
171196
"key_str": key_str,
172-
"connected_users_count": len(room_sessions),
173-
"room_sessions": room_sessions[
174-
:5
175-
], # Limit to first 5 sessions for debugging
176197
},
177-
tip="This indicates a potential race condition or inconsistency between the Redis Resources table and socketio room state. Check if the project was recently closed but users are still connected, or if there's a synchronization issue between services.",
198+
tip="Check if socketio server is properly initialized and the room exists. This could indicate a socketio manager issue or invalid room format.",
178199
)
179200
)
180201
continue
181202

182-
except (KeyError, AttributeError, ValueError) as exc:
183-
_logger.exception(
184-
**create_troubleshootting_log_kwargs(
185-
user_error_msg=f"Failed to check room participants for project {project_uuid}",
186-
error=exc,
187-
error_context={
188-
"project_uuid": str(project_uuid),
189-
"project_room": project_room,
190-
"key_str": key_str,
191-
},
192-
tip="Check if socketio server is properly initialized and the room exists. This could indicate a socketio manager issue or invalid room format.",
193-
)
194-
)
195-
continue
196-
197-
_logger.info(
198-
"Project document cleanup completed: removed %d project documents",
199-
projects_removed,
200-
)
203+
_logger.info("Completed: removed %d project documents", projects_removed)

0 commit comments

Comments
 (0)