@@ -217,6 +217,69 @@ impl LockMngr {
217217 ) )
218218 }
219219
220+ /// Acquire the earliest dependence-chain entry for processing
221+ /// sorted by last_updated_at (FIFO).
222+ /// Returns the dependence_chain_id if a lock was acquired
223+ pub async fn acquire_early_lock (
224+ & mut self ,
225+ ) -> Result < ( Option < Vec < u8 > > , LockingReason ) , sqlx:: Error > {
226+ if self . disable_locking {
227+ debug ! ( "Locking is disabled" ) ;
228+ return Ok ( ( None , LockingReason :: Missing ) ) ;
229+ }
230+
231+ let started_at = SystemTime :: now ( ) ;
232+ let row = sqlx:: query_as :: < _ , DatabaseChainLock > (
233+ r#"
234+ WITH candidate AS (
235+ SELECT dependence_chain_id, 'updated_unowned' AS match_reason, dependency_count
236+ FROM dependence_chain
237+ WHERE
238+ status = 'updated' -- Marked as updated by host-listener
239+ AND
240+ worker_id IS NULL -- Ensure no other workers own it
241+ ORDER BY last_updated_at ASC -- FIFO
242+ FOR UPDATE SKIP LOCKED -- Ensure no other worker is currently trying to lock it
243+ LIMIT 1
244+ )
245+ UPDATE dependence_chain AS dc
246+ SET
247+ worker_id = $1,
248+ status = 'processing',
249+ lock_acquired_at = NOW(),
250+ lock_expires_at = NOW() + make_interval(secs => $2)
251+ FROM candidate
252+ WHERE dc.dependence_chain_id = candidate.dependence_chain_id
253+ RETURNING dc.*, candidate.match_reason, candidate.dependency_count;
254+ "# ,
255+ )
256+ . bind ( self . worker_id )
257+ . bind ( self . lock_ttl_sec )
258+ . fetch_optional ( & self . pool )
259+ . await ?;
260+
261+ let row = if let Some ( row) = row {
262+ row
263+ } else {
264+ return Ok ( ( None , LockingReason :: Missing ) ) ;
265+ } ;
266+
267+ self . lock . replace ( ( row. clone ( ) , SystemTime :: now ( ) ) ) ;
268+ ACQUIRED_DEPENDENCE_CHAIN_ID_COUNTER . inc ( ) ;
269+
270+ let elapsed = started_at. elapsed ( ) . map ( |d| d. as_secs_f64 ( ) ) . unwrap_or ( 0.0 ) ;
271+ if elapsed > 0.0 {
272+ ACQUIRE_DEPENDENCE_CHAIN_ID_QUERY_HISTOGRAM . observe ( elapsed) ;
273+ }
274+
275+ info ! ( ?row, query_elapsed = %elapsed, "Acquired lock on earliest DCID" ) ;
276+
277+ Ok ( (
278+ Some ( row. dependence_chain_id ) ,
279+ LockingReason :: from ( row. match_reason . as_str ( ) ) ,
280+ ) )
281+ }
282+
220283 /// Release all locks held by this worker
221284 ///
222285 /// If host-listener has marked the dependence chain as 'updated' in the meantime,
0 commit comments