Fix catalog pricing saves failing on the queue mutex - #4360
Open
sjcallender wants to merge 1 commit into
Open
Conversation
Contributor
Author
|
I should add that we really only see the error on staging/production. Locally, it finishes too fast, so our test forces the issue. |
Member
|
Thanks for the PR, we will review and give feedback soon. Thanks! |
Contributor
Author
|
Thanks @lukeholder. While we have hopes for you all restructuring how all this works, this PR stays closer to the current setup. It is AI-assisted output for sure, but human-guided in shape and thought. Hope there's something valuable in here to help alleviate our builds' issues. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This one has been nagging us for a long time. And it's about to bite us again on a new build, so we dove in deeper to offer what we see is a solid fix.
Specifically, the build pulls its products in from an ERP. Running the sync piles up so many catalog pricing jobs that the sync itself starts failing on the mutex. Pausing the queue is the only way to get through it. We see the same thing on another build whenever a lot of products get created at once.
Pausing the queue works for the console command, but we're importing off of webhooks, so the same saves will be running in a web request where we can't pause anything.
[edited]
#4123 was closed asking for a new ticket if it persisted. On 5.7.3 we still get 600 jobs for 600 saves.
This doesn't fix the database deadlocks in #4003 and #4141, which are in
generateCatalogPrices()rather than the queue table. Fewer jobs means fewer of them running at once, so it should reduce how often those happen.[/end edit]
Problem
N saves push N jobs against the one row they all merged into. All but a few reserve nothing, each taking the
catalogpricingqueuemutex on the way.The two sides of that mutex disagree:
reserveCatalogPricingQueueRow()0null_queueCatalogPricingIds()5throw new RuntimeExceptionSo a background job can fail a foreground save.
Changes
UPDATE ... SET reserved = true WHERE id = ? AND reserved = false, checking affected rowsttrWe first tried dropping the merge and inserting a row per save. That made the write path much faster, 0.26s against 7.0s for 1200 saves, but it put
generateCatalogPrices()on every row: 60 IDs in one call measured 0.025s against 0.081s as 60 single-ID calls. We kept the merge.We also tried claiming every pending row up front. Killing a worker mid-drain left 200 rows reserved with nothing to free them, so we kept claiming one row at a time.
Our changes don't include any schema change, or
SKIP LOCKEDorRETURNING.Verification
Postgres,
runQueueAutomaticallyoff, no workers running.Six processes, 100
createCatalogPricingJob()calls each with a distinct purchasable ID.Mutex contention. Holding
catalogpricingqueueand then saving throwsRuntimeException: Unable to acquire the catalog pricing queue mutex.on 5.7.2, succeeds patched.Lost updates. Compare-and-swap against an unguarded read-modify-write of the same row, six processes merging 30 IDs each:
Reservation. Eight processes against 400 seeded rows: 400 claims, 400 distinct, 0 double-claimed, 0 left unreserved.
Crash. A worker that is killed after claiming leaves just its 1 row reserved; others drain the rest, and the expired-reservation release recovers it.