@@ -118,9 +118,12 @@ def __init__(
118
118
self ._request_cache = deque [Request ]()
119
119
"""Cache for requests: forefront requests at the beginning, regular requests at the end."""
120
120
121
- self ._cache_needs_refresh = True
121
+ self ._request_cache_needs_refresh = True
122
122
"""Flag indicating whether the cache needs to be refreshed from filesystem."""
123
123
124
+ self ._is_empty_cache : bool | None = None
125
+ """Cache for is_empty result: None means unknown, True/False is cached state."""
126
+
124
127
@property
125
128
@override
126
129
def metadata (self ) -> RequestQueueMetadata :
@@ -202,11 +205,7 @@ async def open(
202
205
metadata_path = rq_path / METADATA_FILENAME
203
206
204
207
# If the RQ directory exists, reconstruct the client from the metadata file.
205
- if rq_path .exists ():
206
- # If metadata file is missing, raise an error.
207
- if not metadata_path .exists ():
208
- raise ValueError (f'Metadata file not found for request queue "{ name } "' )
209
-
208
+ if rq_path .exists () and metadata_path .exists ():
210
209
file = await asyncio .to_thread (open , metadata_path )
211
210
try :
212
211
file_content = json .load (file )
@@ -260,7 +259,10 @@ async def drop(self) -> None:
260
259
261
260
self ._in_progress .clear ()
262
261
self ._request_cache .clear ()
263
- self ._cache_needs_refresh = True
262
+ self ._request_cache_needs_refresh = True
263
+
264
+ # Invalidate is_empty cache.
265
+ self ._is_empty_cache = None
264
266
265
267
@override
266
268
async def purge (self ) -> None :
@@ -272,15 +274,17 @@ async def purge(self) -> None:
272
274
273
275
self ._in_progress .clear ()
274
276
self ._request_cache .clear ()
275
- self ._cache_needs_refresh = True
277
+ self ._request_cache_needs_refresh = True
276
278
277
- # Update metadata counts
278
279
await self ._update_metadata (
279
280
update_modified_at = True ,
280
281
update_accessed_at = True ,
281
282
new_pending_request_count = 0 ,
282
283
)
283
284
285
+ # Invalidate is_empty cache.
286
+ self ._is_empty_cache = None
287
+
284
288
@override
285
289
async def add_batch_of_requests (
286
290
self ,
@@ -298,6 +302,7 @@ async def add_batch_of_requests(
298
302
Response containing information about the added requests.
299
303
"""
300
304
async with self ._lock :
305
+ self ._is_empty_cache = None
301
306
new_total_request_count = self ._metadata .total_request_count
302
307
new_pending_request_count = self ._metadata .pending_request_count
303
308
processed_requests = list [ProcessedRequest ]()
@@ -409,7 +414,10 @@ async def add_batch_of_requests(
409
414
410
415
# Invalidate the cache if we added forefront requests.
411
416
if forefront :
412
- self ._cache_needs_refresh = True
417
+ self ._request_cache_needs_refresh = True
418
+
419
+ # Invalidate is_empty cache.
420
+ self ._is_empty_cache = None
413
421
414
422
return AddRequestsResponse (
415
423
processed_requests = processed_requests ,
@@ -450,7 +458,7 @@ async def fetch_next_request(self) -> Request | None:
450
458
"""
451
459
async with self ._lock :
452
460
# Refresh cache if needed or if it's empty.
453
- if self ._cache_needs_refresh or not self ._request_cache :
461
+ if self ._request_cache_needs_refresh or not self ._request_cache :
454
462
await self ._refresh_cache ()
455
463
456
464
next_request : Request | None = None
@@ -481,6 +489,8 @@ async def mark_request_as_handled(self, request: Request) -> ProcessedRequest |
481
489
Information about the queue operation. `None` if the given request was not in progress.
482
490
"""
483
491
async with self ._lock :
492
+ self ._is_empty_cache = None
493
+
484
494
# Check if the request is in progress.
485
495
if request .id not in self ._in_progress :
486
496
logger .warning (f'Marking request { request .id } as handled that is not in progress.' )
@@ -537,6 +547,8 @@ async def reclaim_request(
537
547
Information about the queue operation. `None` if the given request was not in progress.
538
548
"""
539
549
async with self ._lock :
550
+ self ._is_empty_cache = None
551
+
540
552
# Check if the request is in progress.
541
553
if request .id not in self ._in_progress :
542
554
logger .info (f'Reclaiming request { request .id } that is not in progress.' )
@@ -587,28 +599,35 @@ async def reclaim_request(
587
599
588
600
@override
589
601
async def is_empty (self ) -> bool :
590
- """Check if the queue is empty.
591
-
592
- Returns:
593
- True if the queue is empty, False otherwise.
594
- """
602
+ """Check if the queue is empty, using a cached value if available and valid."""
595
603
async with self ._lock :
604
+ # If we have a cached value, return it immediately.
605
+ if self ._is_empty_cache is not None :
606
+ return self ._is_empty_cache
607
+
608
+ # If we have a cached requests, check them first (fast path).
609
+ if self ._request_cache :
610
+ for req in self ._request_cache :
611
+ if req .handled_at is None :
612
+ self ._is_empty_cache = False
613
+ return False
614
+ self ._is_empty_cache = True
615
+ return True
616
+
617
+ # Fallback: check files on disk (slow path).
596
618
await self ._update_metadata (update_accessed_at = True )
597
619
request_files = await self ._get_request_files (self .path_to_rq )
598
620
599
- # Check each file to see if there are any unhandled requests.
600
621
for request_file in request_files :
601
622
request = await self ._parse_request_file (request_file )
602
-
603
623
if request is None :
604
624
continue
605
-
606
- # If any request is not handled, the queue is not empty.
607
625
if request .handled_at is None :
626
+ self ._is_empty_cache = False
608
627
return False
609
628
610
- # If we got here, all requests are handled or there are no requests.
611
- return True
629
+ self . _is_empty_cache = True
630
+ return True
612
631
613
632
def _get_request_path (self , request_id : str ) -> Path :
614
633
"""Get the path to a specific request file.
@@ -732,7 +751,7 @@ async def _refresh_cache(self) -> None:
732
751
break
733
752
self ._request_cache .append (request )
734
753
735
- self ._cache_needs_refresh = False
754
+ self ._request_cache_needs_refresh = False
736
755
737
756
@classmethod
738
757
async def _get_request_files (cls , path_to_rq : Path ) -> list [Path ]:
0 commit comments