A hold waits on an anchor row per thing that actually bounds it, and the anchors are written by bookkeeping so that no hold ever has to write one. That matters more than it sounds: locking a row that does not exist takes a GAP lock over the range where it would be rather than a row lock, and two bookers meeting in that gap deadlock, so one of them loses their click to an error page.

Only two of the four scopes are actually bookkept. A session writes its own anchor when it is created, and yoyaku_placement writes one per part of the house. Nothing anywhere writes the TARIFF anchor, and nothing writes the ALLOTMENT one: the only code that creates them is the repair inside the hold itself. On a session with no capacity of its own the number that bounds a booker is the tariff's quota, so the tariff anchor is exactly the row that booker waits at.

So it is not the rare anomaly the repair is written for. Every session reaches its first booker with that anchor missing, the first booker writes it, and the moment a session first sells is the moment a rush arrives. The comment beside the repair calls it "something that happens once in a slot's life and never again", which is true and is the problem: that once is the worst moment there is.

Measured over HTTP against MySQL, forty bookers arriving together, each asking for a party of three at a session whose quota leaves two: with the tariff anchor missing, THIRTEEN of four hundred claims came back as an error, spread over eight rounds of ten, each one "SQLSTATE[40001] Serialization failure: 1213 Deadlock found" on the anchor read. With the anchor present, ZERO of eight hundred. Nothing was ever overbooked and nothing leaked: what the bookers were told they held matched what the database held in every round, so this costs a click rather than a seat.

The fix is to write the anchors where the class already says they are written. A session writes one for every tariff and allotment its resource already carries, and a tariff or an allotment given to a session afterwards writes its own as it arrives. Anchoring a tariff no quota bounds costs one row nothing ever locks, which is the right way round: the row is written once in a quiet week, and the alternative is writing it in a rush.

The repair stays, because an anchor can still arrive late by design: a part of the house added to a venue after its sessions exist is repaired on demand. It now writes in a fixed order, for the same reason the read is ordered, so two repairs cannot take the same rows the opposite way round. That is a correctness fix and not a cure: measured on its own it moved thirteen failures to nine, which is noise. What ordering cannot rule out is two repairs meeting over the gap itself, and retrying there is not valid either, since the rollback a deadlock causes is of the whole transaction. The answer to that residual is to write missing anchors before the hold opens its transaction, which is a change to where the repair is called from rather than to what it does, and it is left for its own issue.

AI-Generated: Yes (Claude Code was used to find this, to draft this summary, and to write the code on its merge request. I reviewed it before posting.)

Issue fork yoyaku-3618198

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Status: Active » Needs review

  • mably committed a2d6e8c1 on 1.x
    fix: #3618198 Write a session's tariff and allotment anchors when it is...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.