From 86d563c20bda40fd26ddce988df0f8ca1a482b39 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Thu, 18 Dec 2025 16:06:55 +0100 Subject: [PATCH] fix: ensure database commits complete before HTTP responses Use scope="function" for database dependencies to prevent race conditions where DIRAC queries data immediately after DiracX writes it. With the default scope="request", commits were happening after sending responses, causing intermittent "No sandbox matches the requirements" errors. Introduced DBDepends helper using functools.partial to consistently apply scope="function" to all database dependencies (AuthDB, JobDB, JobLoggingDB, PilotAgentsDB, SandboxMetadataDB, TaskQueueDB, JobParametersDB). This ensures transaction commits complete before HTTP responses are sent, eliminating timing-based race conditions in DIRAC-DiracX interactions. --- .../src/diracx/routers/dependencies.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/diracx-routers/src/diracx/routers/dependencies.py b/diracx-routers/src/diracx/routers/dependencies.py index 8eb2bd265..86d26b79d 100644 --- a/diracx-routers/src/diracx/routers/dependencies.py +++ b/diracx-routers/src/diracx/routers/dependencies.py @@ -12,6 +12,7 @@ "AvailableSecurityProperties", ) +from functools import partial from typing import Annotated, TypeVar from fastapi import Depends @@ -32,6 +33,10 @@ T = TypeVar("T") +# Use scope="function" to ensure DB commits happen before sending HTTP responses +# This prevents race conditions when DIRAC immediately queries data after DiracX writes it +DBDepends = partial(Depends, scope="function") + def add_settings_annotation(cls: T) -> T: """Add a `Depends` annotation to a class that has a `create` classmethod.""" @@ -39,17 +44,17 @@ def add_settings_annotation(cls: T) -> T: # Databases -AuthDB = Annotated[_AuthDB, Depends(_AuthDB.transaction)] -JobDB = Annotated[_JobDB, Depends(_JobDB.transaction)] -JobLoggingDB = Annotated[_JobLoggingDB, Depends(_JobLoggingDB.transaction)] -PilotAgentsDB = Annotated[_PilotAgentsDB, Depends(_PilotAgentsDB.transaction)] +AuthDB = Annotated[_AuthDB, DBDepends(_AuthDB.transaction)] +JobDB = Annotated[_JobDB, DBDepends(_JobDB.transaction)] +JobLoggingDB = Annotated[_JobLoggingDB, DBDepends(_JobLoggingDB.transaction)] +PilotAgentsDB = Annotated[_PilotAgentsDB, DBDepends(_PilotAgentsDB.transaction)] SandboxMetadataDB = Annotated[ - _SandboxMetadataDB, Depends(_SandboxMetadataDB.transaction) + _SandboxMetadataDB, DBDepends(_SandboxMetadataDB.transaction) ] -TaskQueueDB = Annotated[_TaskQueueDB, Depends(_TaskQueueDB.transaction)] +TaskQueueDB = Annotated[_TaskQueueDB, DBDepends(_TaskQueueDB.transaction)] # Opensearch databases -JobParametersDB = Annotated[_JobParametersDB, Depends(_JobParametersDB.session)] +JobParametersDB = Annotated[_JobParametersDB, DBDepends(_JobParametersDB.session)] # Miscellaneous