11from __future__ import annotations
22
3- from collections import deque
3+ from collections import OrderedDict
44from datetime import datetime , timezone
55from logging import getLogger
66from typing import TYPE_CHECKING
@@ -41,7 +41,7 @@ def __init__(
4141 """
4242 self ._metadata = metadata
4343
44- self ._pending_requests = deque [ Request ]()
44+ self ._pending_requests = OrderedDict [ str , Request ]()
4545 """Pending requests are those that have been added to the queue but not yet fetched for processing."""
4646
4747 self ._handled_requests = dict [str , Request ]()
@@ -174,23 +174,21 @@ async def add_batch_of_requests(
174174 )
175175 continue
176176
177- # If the request is already in the queue but not handled, we only reposition it when `forefront`
178- # is set; a regular re-add leaves the already-pending entry untouched.
177+ # If the request is already in the queue but not handled, update it without changing its position.
179178 if was_already_present and existing_request :
179+ self ._requests_by_unique_key [request .unique_key ] = request
180+ self ._pending_requests [request .unique_key ] = request
181+
180182 if forefront :
181- # Move the request to the front. The old entry is left in the deque instead of being
182- # located and removed (`deque.remove` is O(n), which makes a batch of forefront re-adds
183- # O(n^2)); registering the new object here supersedes the old one, and the stale entry is
184- # skipped lazily by `fetch_next_request` and `is_empty`.
185- self ._requests_by_unique_key [request .unique_key ] = request
186- self ._pending_requests .appendleft (request )
183+ self ._pending_requests .move_to_end (request .unique_key , last = False )
187184
188185 # Add the new request to the queue.
189186 else :
190187 if forefront :
191- self ._pending_requests .appendleft (request )
188+ self ._pending_requests [request .unique_key ] = request
189+ self ._pending_requests .move_to_end (request .unique_key , last = False )
192190 else :
193- self ._pending_requests . append ( request )
191+ self ._pending_requests [ request . unique_key ] = request
194192
195193 # Update indexes.
196194 self ._requests_by_unique_key [request .unique_key ] = request
@@ -218,12 +216,7 @@ async def add_batch_of_requests(
218216 @override
219217 async def fetch_next_request (self ) -> Request | None :
220218 while self ._pending_requests :
221- request = self ._pending_requests .popleft ()
222-
223- # Skip stale entries left behind when a request was repositioned to the forefront while already
224- # pending. Only the object currently registered for the unique key is live.
225- if self ._requests_by_unique_key .get (request .unique_key ) is not request :
226- continue
219+ _ , request = self ._pending_requests .popitem (last = False )
227220
228221 # Skip if already handled (shouldn't happen, but safety check).
229222 if request .was_already_handled :
@@ -290,11 +283,13 @@ async def reclaim_request(
290283 # Remove from in-progress.
291284 del self ._in_progress_requests [request .unique_key ]
292285
286+ # Update index with the possibly modified request.
287+ self ._requests_by_unique_key [request .unique_key ] = request
288+
293289 # Add request back to pending queue.
290+ self ._pending_requests [request .unique_key ] = request
294291 if forefront :
295- self ._pending_requests .appendleft (request )
296- else :
297- self ._pending_requests .append (request )
292+ self ._pending_requests .move_to_end (request .unique_key , last = False )
298293
299294 # Update metadata timestamps.
300295 await self ._update_metadata (update_modified_at = True )
@@ -309,13 +304,6 @@ async def reclaim_request(
309304 async def is_empty (self ) -> bool :
310305 await self ._update_metadata (update_accessed_at = True )
311306
312- # Discard stale entries left at the front by forefront repositioning; a live request is always
313- # enqueued ahead of the stale entry it supersedes, so pruning stops at the first live request.
314- while self ._pending_requests and (
315- self ._requests_by_unique_key .get (self ._pending_requests [0 ].unique_key ) is not self ._pending_requests [0 ]
316- ):
317- self ._pending_requests .popleft ()
318-
319307 # Queue is empty if there are no pending requests.
320308 return len (self ._pending_requests ) == 0
321309
0 commit comments