Issue #3620039: Consolidate a party inside the hold that split it, so a busy house does not lose it
A hold now consolidates before it releases its locks, so the placement and the consolidation land in one transaction under one set of locks.
Previously one press of a stepper took the slot locks twice: the hold placed the seats, committed and released, and a consolidation was then asked for as a separate call that reached the exchange through hold() and locked again. Nothing was left incorrect by that, but the consolidation was the first thing lost under contention, and its refusal was silent.
What is here
hold()carries$consolidate, on by default.consolidate()passes it off, since an exchange reaches the engine throughhold()and a hold that offered a consolidation would have that one offer another.- A hold consolidates only what its own locks already cover, and never takes a second lock. An exchange reaching further is not made, and
BookingResult::$needsConsolidationsays so, so the caller knows to ask the wider call. Taking a second lock while holding the first is the deadlock #3616975 was about. - An exchange is whole or nothing: it runs in a savepoint and marks the deferred batch, and a refusal puts back both. A savepoint alone cannot reach the batch, whose seats would stay marked gone at the flush.
DeferredWritesgainsregisterGatheredState(),mark()androllBackTo().mark()answers NULL where a registrant cannot hand its work back, since undoing half a batch is worse than undoing none.
Fixed alongside, all older than this branch
- The lock scopes covered only the offers a hold takes, not the ones it gives up, so a hold could rewrite rows it had not locked.
- A slot's spent units were read once for the whole hold, so an exchange was refused for want of room it had just handed back itself. On a quota whose last unit the party holds, that was every exchange.
- A party holding one place the booker chose could not be improved at all: the veto was put to the whole party rather than to the place under consideration, so one chosen seat in the balcony froze four places in the stalls.
- A party carried into another part of the venue was told it had changed row. It now says which happened, in English and in French.
- The whole-party move added in #3620010 had no test that reached it.
Reading this branch
The commits are separable and are meant to be read one at a time: the vocabulary sweep, the deferred-batch undo, the lock scopes, the change itself, the placement fixes, the missing coverage, the next-major lane, the docs, and then three passes over the comments and docs for plain English.
Cost is one statement on a hold that has a party on the slot, and none on a hold that does not, however large the party. The halving of lock traffic is reasoned from the code path and has not been measured on the load rig.