@@ -681,6 +681,8 @@ pub struct QueueAddPayload {
681681 pub value : String ,
682682 pub priority : i64 ,
683683 pub orphaned : Option < u32 > ,
684+ pub process_id : Option < String > ,
685+ pub exclusive : bool ,
684686}
685687
686688#[ derive( Clone , Serialize , Deserialize , Debug , Eq , PartialEq ) ]
@@ -831,6 +833,7 @@ pub trait CacheStore: DIService + Send + Sync {
831833 & self ,
832834 path : String ,
833835 allow_concurrency : u32 ,
836+ caller_process_id : Option < String > ,
834837 ) -> Result < QueueRetrieveResponse , CubeError > ;
835838 async fn queue_ack ( & self , key : QueueKey , result : Option < String > ) -> Result < bool , CubeError > ;
836839 async fn queue_result_by_path (
@@ -1103,6 +1106,8 @@ impl CacheStore for RocksCacheStore {
11031106 QueueItem :: status_default ( ) ,
11041107 payload. priority ,
11051108 payload. orphaned . clone ( ) ,
1109+ payload. process_id ,
1110+ payload. exclusive ,
11061111 ) ,
11071112 batch_pipe,
11081113 ) ?;
@@ -1300,6 +1305,7 @@ impl CacheStore for RocksCacheStore {
13001305 & self ,
13011306 path : String ,
13021307 allow_concurrency : u32 ,
1308+ caller_process_id : Option < String > ,
13031309 ) -> Result < QueueRetrieveResponse , CubeError > {
13041310 self . write_operation_queue ( "queue_retrieve_by_path" , move |db_ref, batch_pipe| {
13051311 let queue_schema = QueueItemRocksTable :: new ( db_ref. clone ( ) ) ;
@@ -1334,9 +1340,33 @@ impl CacheStore for RocksCacheStore {
13341340 } ;
13351341
13361342 if id_row. get_row ( ) . get_status ( ) == & QueueItemStatus :: Pending {
1343+ if id_row. get_row ( ) . get_exclusive ( ) {
1344+ match ( id_row. get_row ( ) . get_process_id ( ) , & caller_process_id) {
1345+ ( Some ( _) , None ) => return Err ( CubeError :: user (
1346+ "QUEUE RETRIEVE requires a process_id in the connection context (x-process-id header)" . to_string ( ) ,
1347+ ) ) ,
1348+ ( None , Some ( _) ) => {
1349+ log:: warn!( "Incorrect queue_item with exclusive flag, empty process_id, id: {:?}" , caller_process_id) ;
1350+
1351+ return Ok ( QueueRetrieveResponse :: NotFound { pending, active } )
1352+ }
1353+ ( Some ( item_process_id) , Some ( caller_id) ) => if item_process_id == caller_id {
1354+ // OK, caller matches the exclusive item owner
1355+ } else {
1356+ return Ok ( QueueRetrieveResponse :: ExclusiveAccessFailed {
1357+ pending,
1358+ active,
1359+ } )
1360+ } ,
1361+ ( None , None ) => {
1362+ // No process_id on item and no caller — allow retrieval
1363+ }
1364+ }
1365+ }
1366+
13371367 let mut new = id_row. get_row ( ) . clone ( ) ;
13381368 new. status = QueueItemStatus :: Active ;
1339- // It's important to insert heartbeat, because
1369+ // It's important to insert heartbeat, because
13401370 // without that created datetime will be used for orphaned filtering
13411371 new. update_heartbeat ( ) ;
13421372
@@ -1652,6 +1682,7 @@ impl CacheStore for ClusterCacheStoreClient {
16521682 & self ,
16531683 _path : String ,
16541684 _allow_concurrency : u32 ,
1685+ _caller_process_id : Option < String > ,
16551686 ) -> Result < QueueRetrieveResponse , CubeError > {
16561687 panic ! ( "CacheStore cannot be used on the worker node! queue_retrieve_by_path was used." )
16571688 }
@@ -2074,19 +2105,47 @@ mod tests {
20742105 let now = Utc :: now ( ) ;
20752106 let item_pending_custom_orphaned = IdRow :: new (
20762107 1 ,
2077- QueueItem :: new ( "1" . to_string ( ) , QueueItemStatus :: Pending , 1 , Some ( 10 ) ) ,
2108+ QueueItem :: new (
2109+ "1" . to_string ( ) ,
2110+ QueueItemStatus :: Pending ,
2111+ 1 ,
2112+ Some ( 10 ) ,
2113+ None ,
2114+ false ,
2115+ ) ,
20782116 ) ;
20792117 let item_pending_custom_orphaned_expired = IdRow :: new (
20802118 2 ,
2081- QueueItem :: new ( "2" . to_string ( ) , QueueItemStatus :: Pending , 1 , Some ( 1 ) ) ,
2119+ QueueItem :: new (
2120+ "2" . to_string ( ) ,
2121+ QueueItemStatus :: Pending ,
2122+ 1 ,
2123+ Some ( 1 ) ,
2124+ None ,
2125+ false ,
2126+ ) ,
20822127 ) ;
20832128 let item_active_custom_orphaned = IdRow :: new (
20842129 3 ,
2085- QueueItem :: new ( "3" . to_string ( ) , QueueItemStatus :: Active , 1 , Some ( 10 ) ) ,
2130+ QueueItem :: new (
2131+ "3" . to_string ( ) ,
2132+ QueueItemStatus :: Active ,
2133+ 1 ,
2134+ Some ( 10 ) ,
2135+ None ,
2136+ false ,
2137+ ) ,
20862138 ) ;
20872139 let mut item_active_custom_orphaned_expired = IdRow :: new (
20882140 4 ,
2089- QueueItem :: new ( "4" . to_string ( ) , QueueItemStatus :: Active , 1 , Some ( 1 ) ) ,
2141+ QueueItem :: new (
2142+ "4" . to_string ( ) ,
2143+ QueueItemStatus :: Active ,
2144+ 1 ,
2145+ Some ( 1 ) ,
2146+ None ,
2147+ false ,
2148+ ) ,
20902149 ) ;
20912150
20922151 assert_eq ! (
0 commit comments