@@ -39,6 +39,10 @@ def __init__(
39
39
self ,
40
40
* ,
41
41
api_client : RequestQueueClientAsync ,
42
+ id : str ,
43
+ name : str | None ,
44
+ total_request_count : int ,
45
+ handled_request_count : int ,
42
46
) -> None :
43
47
"""Initialize a new instance.
44
48
@@ -47,6 +51,12 @@ def __init__(
47
51
self ._api_client = api_client
48
52
"""The Apify request queue client for API operations."""
49
53
54
+ self ._id = id
55
+ """The ID of the request queue."""
56
+
57
+ self ._name = name
58
+ """The name of the request queue."""
59
+
50
60
self ._queue_head = deque [str ]()
51
61
"""A deque to store request IDs in the queue head."""
52
62
@@ -59,10 +69,38 @@ def __init__(
59
69
self ._should_check_for_forefront_requests = False
60
70
"""Whether to check for forefront requests in the next list_head call."""
61
71
72
+ self ._had_multiple_clients = False
73
+ """Whether the request queue has been accessed by multiple clients."""
74
+
75
+ self ._initial_total_count = total_request_count
76
+ """The initial total request count (from the API) when the queue was opened."""
77
+
78
+ self ._initial_handled_count = handled_request_count
79
+ """The initial handled request count (from the API) when the queue was opened."""
80
+
81
+ self ._assumed_total_count = 0
82
+ """The number of requests we assume are in the queue (tracked manually for this instance)."""
83
+
84
+ self ._assumed_handled_count = 0
85
+ """The number of requests we assume have been handled (tracked manually for this instance)."""
86
+
62
87
@override
63
88
async def get_metadata (self ) -> RequestQueueMetadata :
64
- metadata = await self ._api_client .get ()
65
- return RequestQueueMetadata .model_validate (metadata )
89
+ total_count = self ._initial_total_count + self ._assumed_total_count
90
+ handled_count = self ._initial_handled_count + self ._assumed_handled_count
91
+ pending_count = total_count - handled_count
92
+
93
+ return RequestQueueMetadata (
94
+ id = self ._id ,
95
+ name = self ._name ,
96
+ total_request_count = total_count ,
97
+ handled_request_count = handled_count ,
98
+ pending_request_count = pending_count ,
99
+ created_at = datetime .now (timezone .utc ),
100
+ modified_at = datetime .now (timezone .utc ),
101
+ accessed_at = datetime .now (timezone .utc ),
102
+ had_multiple_clients = self ._had_multiple_clients ,
103
+ )
66
104
67
105
@classmethod
68
106
async def open (
@@ -136,6 +174,8 @@ async def open(
136
174
apify_rq_client = apify_client_async .request_queue (request_queue_id = id )
137
175
138
176
# If both id and name are None, try to get the default storage ID from environment variables.
177
+ # The default storage ID environment variable is set by the Apify platform. It also contains
178
+ # a new storage ID after Actor's reboot or migration.
139
179
if id is None and name is None :
140
180
id = configuration .default_request_queue_id
141
181
apify_rq_client = apify_client_async .request_queue (request_queue_id = id )
@@ -155,8 +195,20 @@ async def open(
155
195
if metadata is None :
156
196
raise ValueError (f'Opening request queue with id={ id } and name={ name } failed.' )
157
197
198
+ metadata_model = RequestQueueMetadata .model_validate (
199
+ await apify_rqs_client .get_or_create (),
200
+ )
201
+
202
+ # Ensure we have a valid ID.
203
+ if id is None :
204
+ raise ValueError ('Request queue ID cannot be None.' )
205
+
158
206
return cls (
159
207
api_client = apify_rq_client ,
208
+ id = id ,
209
+ name = name ,
210
+ total_request_count = metadata_model .total_request_count ,
211
+ handled_request_count = metadata_model .handled_request_count ,
160
212
)
161
213
162
214
@override
@@ -195,10 +247,19 @@ async def add_batch_of_requests(
195
247
for request in requests
196
248
]
197
249
198
- # Send requests to API
250
+ # Send requests to API.
199
251
response = await self ._api_client .batch_add_requests (requests = requests_dict , forefront = forefront )
200
252
201
- return AddRequestsResponse .model_validate (response )
253
+ # Update assumed total count for newly added requests.
254
+ api_response = AddRequestsResponse .model_validate (response )
255
+ new_request_count = 0
256
+ for processed_request in api_response .processed_requests :
257
+ if not processed_request .was_already_present and not processed_request .was_already_handled :
258
+ new_request_count += 1
259
+
260
+ self ._assumed_total_count += new_request_count
261
+
262
+ return api_response
202
263
203
264
@override
204
265
async def get_request (self , request_id : str ) -> Request | None :
@@ -288,6 +349,10 @@ async def mark_request_as_handled(self, request: Request) -> ProcessedRequest |
288
349
processed_request = await self ._update_request (request )
289
350
processed_request .unique_key = request .unique_key
290
351
352
+ # Update assumed handled count if this wasn't already handled
353
+ if not processed_request .was_already_handled :
354
+ self ._assumed_handled_count += 1
355
+
291
356
# Update the cache with the handled request
292
357
cache_key = unique_key_to_request_id (request .unique_key )
293
358
self ._cache_request (
@@ -320,11 +385,21 @@ async def reclaim_request(
320
385
Returns:
321
386
Information about the queue operation. `None` if the given request was not in progress.
322
387
"""
388
+ # Check if the request was marked as handled and clear it. When reclaiming,
389
+ # we want to put the request back for processing.
390
+ if request .was_already_handled :
391
+ request .handled_at = None
392
+
323
393
try :
324
- # Update the request in the API
394
+ # Update the request in the API.
325
395
processed_request = await self ._update_request (request , forefront = forefront )
326
396
processed_request .unique_key = request .unique_key
327
397
398
+ # If the request was previously handled, decrement our handled count since
399
+ # we're putting it back for processing.
400
+ if request .was_already_handled and not processed_request .was_already_handled :
401
+ self ._assumed_handled_count -= 1
402
+
328
403
# Update the cache
329
404
cache_key = unique_key_to_request_id (request .unique_key )
330
405
self ._cache_request (
0 commit comments