予約の道具で最も悪い失敗は、予約が取れないことではない。
二人ともに「ご予約されました」を送ることだ。取れなければやり直せばいいが、どちらも取れたと言われれば、その次は人が電話で解決することになる。
二つの処理、同じ説明
このサンプルにはスロットを取る処理が二つある。言葉で説明すると完全に同じだ — 空いているか見て、空いていれば取る。
一方はこうだ。
// 1. Look.
final free = slot.takenBy == null;
// 2. Anything at all in here — a database round trip, a payment check, a
// log write, a lookup of the customer's name — hands control to
// whatever else was in flight.
await Future<void>.delayed(Duration.zero);
// 3. Write, using what was true in step 1.
if (free) { slot.takenBy = who; ... }
もう一方はこうだ。
if (slot.takenBy != null) {
return '$at is already taken by ${slot.takenBy}';
}
slot.takenBy = who;
slot.confirmations.add(who);
await Future<void>.delayed(Duration.zero); // the slow part, after the claim
同じ await が位置だけ違う。 前者は確認と記録の間にあり、後者は記録の後にある。
そしてその位置が二重予約を作る。
実際に起こす
unsafe -> Suh: "CONFIRMED 10:30 for Suh"
unsafe -> Park: "CONFIRMED 10:30 for Park"
unsafe result: 1 slot(s) confirmed to more than one person

画面が現実の症状を正確に見せている。 スロットの持ち主はParkひとりなのに、「予約された」と言われた人は二人だ。
これが実際の店で起きる形だ。帳簿は一人、扉の前には二人が来る。
だからサーバーは確定を送った相手を全員覚えている。
/// Everybody who has ever been told they got this slot. In a correct
/// booking system this never has more than one name in it, which is exactly
/// why it is worth keeping.
final confirmations = <String>[];
正常なら常に一人だけの一覧だ。常に一人でなければならない一覧こそ、数えてみる値打ちがある。
そして起こらないようにする
同じ二人、同じ時間、隙間だけ閉じて。
--- cleared, same two people, same slot, gap closed ---
safe -> Suh: "CONFIRMED 10:30 for Suh"
safe -> Park: "10:30 is already taken by Suh"
safe result: nobody was told yes twice

拒否が「失敗しました」ではなく「Suhさんが取りました」だ。 二人目が知るべきなのは自分が失敗したという事実ではなく、その枠が出たという事実であり、そうしてはじめて次の時間を選べる。