Give back a run of clicks without recomputing the order once per place

Adds TransactionManagerInterface::recomputeOnce(), a bracket a caller can open around a batch of line changes, and opens it for the two picker paths that give places back.

What was happening

Every released line announces itself, and OrderLifecycleSubscriber::onChange answers each announcement by locking the order's row and reading every line on it. It already steps aside during the manager's own whole-transaction transitions, with a comment saying this avoids an O(n^2) storm; a run of clicks was simply never able to say it was one.

Measured

PlaceReleaseCostTest, 24-place hall, one run of clicks:

before after
giving back 1 place 16 queries, 2 recomputes unchanged
giving back 6 places 58 queries, 6 recomputes 53 queries, 1 recompute

The five queries are the smaller half. The five locks on the order's row are the half that matters under load, and the shape no longer grows with the basket.

The test

Compares one place against six and asserts the recompute does not grow with them, rather than asserting a ceiling, which a number written from today's code would pass by construction. It counts the read of the order's row rather than the lock taken around it: a locking read is written FOR UPDATE on MySQL and nothing at all on SQLite, so counting the lock would measure the driver. Seen to fail against the unfixed path at 6 against 2, and the figures are in the register.

Care taken

The bracket cannot be left open, recomputes whether the work returned or threw, and nests safely, an inner one leaving the recompute to the outer that owns it. The order is read before the releases, because giving back the last place empties the basket.

Found while measuring the release path for [#3615593].

Merge request reports

Loading
Loading