1313import platform
1414import subprocess
1515
16- from .condition import AndList , Not
16+ from .condition import AndList , Not , make_condition
1717from .errors import DataJointError , DuplicateError
1818from .heading import Heading
1919from .table import Table
@@ -435,8 +435,10 @@ def reserve(self, key: dict) -> bool:
435435 """
436436 Attempt to reserve a pending job for processing.
437437
438- Updates status to ``'reserved'`` if currently ``'pending'`` and
439- ``scheduled_time <= now``.
438+ Atomically updates status to ``'reserved'`` if currently ``'pending'``
439+ and ``scheduled_time <= now``, using a single UPDATE with a WHERE clause
440+ that includes the status check. This prevents race conditions where
441+ multiple workers could reserve the same job simultaneously.
440442
441443 Parameters
442444 ----------
@@ -448,33 +450,26 @@ def reserve(self, key: dict) -> bool:
448450 bool
449451 True if reservation successful, False if job not available.
450452 """
451- # Check if job is pending and scheduled (use CURRENT_TIMESTAMP(3) for datetime(3) precision)
452- job = (self & key & "status='pending'" & "scheduled_time <= CURRENT_TIMESTAMP(3)" ).to_dicts ()
453-
454- if not job :
455- return False
456-
457- # Get server time for reserved_time
458- server_now = self .connection .query ("SELECT CURRENT_TIMESTAMP" ).fetchone ()[0 ]
459-
460- # Build update row with primary key and new values
461453 pk = self ._get_pk (key )
462- update_row = {
463- ** pk ,
464- "status" : "reserved" ,
465- "reserved_time" : server_now ,
466- "host" : platform .node (),
467- "pid" : os .getpid (),
468- "connection_id" : self .connection .connection_id ,
469- "user" : self .connection .get_user (),
470- "version" : _get_job_version (self .connection ._config ),
471- }
472-
473- try :
474- self .update1 (update_row )
475- return True
476- except Exception :
477- return False
454+ where = make_condition (self , pk , set ())
455+ qi = self .adapter .quote_identifier
456+ assignments = ", " .join (f"{ qi (k )} =%s" for k in ("status" , "host" , "pid" , "connection_id" , "user" , "version" ))
457+ query = (
458+ f"UPDATE { self .full_table_name } "
459+ f"SET { assignments } , { qi ('reserved_time' )} =CURRENT_TIMESTAMP(3) "
460+ f"WHERE { where } AND { qi ('status' )} ='pending' "
461+ f"AND { qi ('scheduled_time' )} <= CURRENT_TIMESTAMP(3)"
462+ )
463+ args = [
464+ "reserved" ,
465+ platform .node (),
466+ os .getpid (),
467+ self .connection .connection_id ,
468+ self .connection .get_user (),
469+ _get_job_version (self .connection ._config ),
470+ ]
471+ cursor = self .connection .query (query , args = args )
472+ return cursor .rowcount == 1
478473
479474 def complete (self , key : dict , duration : float | None = None ) -> None :
480475 """
0 commit comments