fix: prolong and track shared request queue locks to prevent duplicate processing#1062
fix: prolong and track shared request queue locks to prevent duplicate processing#1062vdusek wants to merge 5 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1062 +/- ##
==========================================
- Coverage 91.89% 91.85% -0.05%
==========================================
Files 51 51
Lines 3232 3265 +33
==========================================
+ Hits 2970 2999 +29
- Misses 262 266 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Btw. this is a different case than #887 |
| # Prolong the lock so the consumer gets a full lock window starting now instead of sharing the batch lock | ||
| # acquired when the head was listed. If the lock can no longer be (re)acquired, skip the request. | ||
| try: | ||
| lock_info = await self._api_client.prolong_request_lock( |
There was a problem hiding this comment.
What about the cost increase? Won't this lead to each request being locked at least twice? Maybe we could lock only if the remaining time is something very small?
I will run benchmarks with this change to quantify the cost increase. Maybe it is not serious
There was a problem hiding this comment.
Let's wait for them to finish:
Without change: https://github.com/apify/actor-benchmarks/actions/runs/29900513281
With change: https://github.com/apify/actor-benchmarks/actions/runs/29900596790
There was a problem hiding this comment.
Summary for the repeated 10 benchmark runs
Without change:
Overall benchmark of all runs, :aggregated_benchmark=Actor: parsel-crawler-benchmark, Valid results: 2013.6, Runtime: 59.6927 s, Costs: 0.1262127994999134 USD
With change:
Overall benchmark of all runs, :aggregated_benchmark=Actor: parsel-crawler-benchmark, Valid results: 2013.3, Runtime: 61.2107 s, Costs: 0.14725829787564015 USD,
There was a problem hiding this comment.
Around 16 % cost increase seems pretty high to me. Maybe some optimization could be used to mitigate this increase.
How is it done in JS SDK?
The shared request-queue client (
access='shared') locked a batch of up to 25 requests for a fixed 180s inlist_and_lock_headbut never prolonged, released, or tracked those locks. As the batch drained slowly, locks expired mid-flight and requests got processed twice — across clients (another consumer re-locked and processed an expired-lock request) and within one process (an expired request was re-listed and handed to a second worker). This defeated the class's documented "request locking to prevent duplicate processing" guarantee (double charges in PPE, duplicate dataset items).Changes:
lock_expires_atper cached head entry (it was returned by the API but discarded)._requests_in_progressset; skip in-progress IDs both when re-listing the head and when fetching, so a request is never handed to a second consumer in the same process.fetch_next_request, skip queued entries whose lock has already expired (another client may own them now) and prolong the chosen request's lock so the consumer gets a full window starting at hand-off; release the reservation on every early-return path.Out of scope (not part of the reported failure, and not minimally feasible today):
RequestQueueClientexposes no close/finalize hook to attach it to; leftover locks still lapse naturally within the lock TTL.✍️ Drafted by Claude Code