#3615440: Move the hold clock from the booking line to the order put the hold window on the order, and a realm that renews on change therefore writes the order row every time the basket grows. Nothing else on that path writes it, so that is a write per click that did not exist before. Measured with Database::startLog() against 18435fc6, for a basket built with one opening hold and N further ones: before, 14 + 9N; after, 10 + 11N.
Opening a basket got cheaper, because the window and the pending state are written in the save that was opening the order anyway. Appending got two queries dearer, because the deadline used to ride along inside the line's own INSERT and now belongs to a row nothing else on that path writes. The two cross at N = 2, so a seating map where a visitor picks ten places one at a time costs about 15% more than it did. HoldCostTest measures all four paths.
The obvious cheaper version does not work, and this is worth recording so nobody spends the afternoon on it twice. Moving the window with a targeted UPDATE of the order's own table is one query instead of two and skips the cache invalidation. It was built, measured at 10 + 10N, and reverted: the row changes, but every later entity read still answers with the old deadline, so the sweep would act on a window it cannot see move. Verified directly, raw row against loadUnchanged(). The write cannot be made cheaper in place. It has to stop happening.
The anchor needed for that already exists and is already written. Appending inserts a line, and a line carries created, so MAX(created) across an order's held lines is exactly when the basket last changed. The deadline could be derived from it rather than stored: LEAST(hold_started + hold_max_lifetime, MAX(line.created) + hold_ttl), with hold_started the only thing still written, once, as the order opens. That is 10 + 9N, cheaper than before this issue at every N.
What it would remove, beyond the write: yoyaku_transaction.hold_expires and its composite index, and hold_suspended_expires with the whole suspend-record-restore pair. A locked order is already excluded from the hold sweep by its state rather than by a nulled deadline, so nothing has to remember a deadline in order to give it back. The derived value is an absolute moment anchored in the past, so the clock still runs while the booker is at the provider, which is the behavior #3614424: The checkout amount is computed before the order is locked, so the basket can still change between the two settled and which this must not change.
What it would cost: the sweep stops being an indexed range scan on one column and becomes a join with an aggregate over the order's lines. It is bounded by the orders still open for editing rather than by every order the site has ever taken, so the set is small, but it is no longer a single indexed number. Every reader that displays a deadline computes it too, though the ones that matter are already reading the lines: HeldCookie builds the overlay from the session's held lines, so it gets the aggregate for nothing.
Open, and the reason this is not a small change: whether the sweep stays cheap enough at the sizes that matter, and whether a derived deadline is still easy enough to reason about. The invariant #3615440: Move the hold clock from the booking line to the order bought was that there is exactly one clock and it is a number you can read. Deriving keeps the one clock but stops it being a number.
Issue fork yoyaku-3615509
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
Comment #2
mably commentedComment #3
mably commented