Skip to content

Commit c039a94

Browse files
committed
fix(node): prevent error in single status update operation
1 parent b0881b6 commit c039a94

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

app/jobs/node_checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def update_node_connection_status(node_id: int, node: PasarGuardNode):
7070
await NodeOperation._update_single_node_status(db, node_id, NodeStatus.error, message=e.detail)
7171
if e.code > 0:
7272
async with GetDB() as db:
73-
await node_operator.connect_node_wrapper(db, node_id)
73+
await node_operator.connect_single_node(db, node_id)
7474

7575

7676
async def process_node_health_check(db_node: Node, node: PasarGuardNode):
@@ -86,7 +86,7 @@ async def process_node_health_check(db_node: Node, node: PasarGuardNode):
8686

8787
if node.requires_hard_reset():
8888
async with GetDB() as db:
89-
await node_operator.connect_node_wrapper(db, db_node.id)
89+
await node_operator.connect_single_node(db, db_node.id)
9090
return
9191

9292
try:

app/operation/node.py

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ async def create_node(self, db: AsyncSession, new_node: NodeCreate, admin: Admin
182182

183183
try:
184184
await node_manager.update_node(db_node)
185-
asyncio.create_task(self.connect_node_wrapper(db, db_node.id))
185+
asyncio.create_task(self.connect_single_node(db, db_node.id))
186186
except NodeAPIError as e:
187187
await self._update_single_node_status(db, db_node.id, NodeStatus.error, message=e.detail)
188188

@@ -211,7 +211,7 @@ async def modify_node(
211211
else:
212212
try:
213213
await node_manager.update_node(db_node)
214-
asyncio.create_task(self.connect_node_wrapper(db, db_node.id))
214+
asyncio.create_task(self.connect_single_node(db, db_node.id))
215215
except NodeAPIError as e:
216216
await self._update_single_node_status(db, db_node.id, NodeStatus.error, message=e.detail)
217217

@@ -306,9 +306,12 @@ async def connect_single(node: Node) -> dict | None:
306306
elif notif["status"] == NodeStatus.error and notif["old_status"] != NodeStatus.error:
307307
asyncio.create_task(notification.error_node(notif["node"]))
308308

309-
async def connect_node_wrapper(self, db: AsyncSession, node_id: int) -> None:
309+
async def connect_single_node(self, db: AsyncSession, node_id: int) -> None:
310310
"""
311-
Connect single node by wrapping it in a list for bulk operation.
311+
Connect a single node and update its status (optimized for single-node operations).
312+
313+
Uses simple UPDATE statement instead of bulk update to avoid deadlock risks
314+
and unnecessary complexity.
312315
313316
Args:
314317
db (AsyncSession): Database session.
@@ -318,10 +321,65 @@ async def connect_node_wrapper(self, db: AsyncSession, node_id: int) -> None:
318321
if db_node is None:
319322
return
320323

321-
await self.connect_nodes_bulk(db, [db_node])
324+
# Get core users once
325+
users = await core_users(db=db)
326+
327+
# Update node manager
328+
try:
329+
await node_manager.update_node(db_node)
330+
except NodeAPIError as e:
331+
# Update status to error using simple CRUD
332+
await update_node_status(
333+
db=db,
334+
db_node=db_node,
335+
status=NodeStatus.error,
336+
message=e.detail,
337+
)
338+
339+
# Send error notification
340+
node_notif = NodeNotification(
341+
id=db_node.id,
342+
name=db_node.name,
343+
message=e.detail,
344+
)
345+
asyncio.create_task(notification.error_node(node_notif))
346+
return
347+
348+
# Connect the node
349+
result = await NodeOperation.connect_node(db_node, users)
350+
351+
if not result:
352+
return
353+
354+
# Update status using simple CRUD (NOT bulk!)
355+
await update_node_status(
356+
db=db,
357+
db_node=db_node,
358+
status=result["status"],
359+
message=result.get("message", ""),
360+
xray_version=result.get("xray_version", ""),
361+
node_version=result.get("node_version", ""),
362+
)
363+
364+
# Send appropriate notification
365+
if result["status"] == NodeStatus.connected:
366+
node_notif = NodeNotification(
367+
id=db_node.id,
368+
name=db_node.name,
369+
xray_version=result.get("xray_version"),
370+
node_version=result.get("node_version"),
371+
)
372+
asyncio.create_task(notification.connect_node(node_notif))
373+
elif result["status"] == NodeStatus.error and result["old_status"] != NodeStatus.error:
374+
node_notif = NodeNotification(
375+
id=db_node.id,
376+
name=db_node.name,
377+
message=result.get("message"),
378+
)
379+
asyncio.create_task(notification.error_node(node_notif))
322380

323381
async def restart_node(self, db: AsyncSession, node_id: Node, admin: AdminDetails) -> None:
324-
await self.connect_node_wrapper(db, node_id)
382+
await self.connect_single_node(db, node_id)
325383
logger.info(f'Node "{node_id}" restarted by admin "{admin.username}"')
326384

327385
async def restart_all_node(self, db: AsyncSession, admin: AdminDetails, core_id: int | None = None) -> None:

0 commit comments

Comments
 (0)