Featured image of post 通知を取りこぼした画面だけ、古い状態のまま戻らない。push と定期取得を併用して必ず追いつかせる【Eventual Consistency】

通知を取りこぼした画面だけ、古い状態のまま戻らない。push と定期取得を併用して必ず追いつかせる【Eventual Consistency】

push 通知を取りこぼした側が、古い状態のまま戻らない。push に定期取得を足し、バージョンで束ねて、遅れても必ず追いつき、古い通知では状態を戻さないようにする。TypeScript で実装します。

Featured image of post 同時に更新した片方が、気づかないうちに消えている。バージョンを持たせて衝突した方にやり直させる【Optimistic Locking】

同時に更新した片方が、気づかないうちに消えている。バージョンを持たせて衝突した方にやり直させる【Optimistic Locking】

在庫を読んでから書き戻すまでの隙に別の更新が入り、一方の更新が黙って上書きされる(Lost Update)。値にバージョンを持たせ、衝突した側だけ読み直してやり直させる。TypeScript で実装します。

Featured image of post boolean フラグを足すたびに、ありえない状態が増えていく。状態を1つの型にまとめて書けなくする【State Machine】

boolean フラグを足すたびに、ありえない状態が増えていく。状態を1つの型にまとめて書けなくする【State Machine】

フラグを1つ足すたびに、「待機中かつ実行中」のようなありえない組み合わせが倍に増える。状態を判別可能ユニオンにまとめ、遷移を型で縛る。TypeScript で実装し、足し忘れも型で止めます。

Featured image of post 上限は守っているのに、切り替わりの一瞬だけ2倍通ってしまう。窓で区切らず溜まる速さで制限する【Token Bucket】

上限は守っているのに、切り替わりの一瞬だけ2倍通ってしまう。窓で区切らず溜まる速さで制限する【Token Bucket】

1分あたりの上限を決めても、固定窓の境目をまたぐと上限の2倍が通り抜ける。窓の区切りをやめ、トークンが一定の速さで溜まる Token Bucket に替えて流量を抑える。TypeScript で実装します。

Featured image of post 2つの処理が互いのロックを待ち合って、永久に止まる。ロックを取る順番を全体で1つに揃える【Deadlock】

2つの処理が互いのロックを待ち合って、永久に止まる。ロックを取る順番を全体で1つに揃える【Deadlock】

JavaScript はシングルスレッドでも、ロックを取り合う2つの処理が互いを待って永久に止まる。ロックを取る順を全体で揃えて循環を断ち、タイムアウトの限界にも触れる。TypeScript で実装します。

Featured image of post 並列数を上げたら、かえって全体が落ちた。同時に動く数を上限で絞って待たせる【Semaphore】

並列数を上げたら、かえって全体が落ちた。同時に動く数を上限で絞って待たせる【Semaphore】

非同期処理を一斉に走らせると、相手の接続を取り合って共倒れになる。同時に通す数を N に絞り、溢れた分は行列で待たせる(Semaphore)。TypeScript で実装し、同時実行数の最大値をテストで確かめます。

Featured image of post 障害のたびにリトライを連発し、かえって復旧を遅らせる。待ち時間を倍々に伸ばして散らす【Exponential Backoff】

障害のたびにリトライを連発し、かえって復旧を遅らせる。待ち時間を倍々に伸ばして散らす【Exponential Backoff】

相手が落ちるたびに即リトライすると、回復する前にリクエストを使い果たし、再開時に殺到する。待ちを倍に伸ばす指数バックオフと、ばらつきを足す Full Jitter で避ける。TypeScript で実装します。

Featured image of post 消したはずの検索が裏で動き続け、リソースを握ったまま離さない。キャンセルを最後まで伝える【AbortController】

消したはずの検索が裏で動き続け、リソースを握ったまま離さない。キャンセルを最後まで伝える【AbortController】

キャンセルした非同期処理が裏で動き続け、接続やカーソルを握りっぱなしになる。AbortSignal を処理の前・中・待機中の3か所で見るようにして止める。TypeScript で実装します。

Featured image of post ボタンを連打すると、同じ処理が二重に走って二重送信になる。await をまたぐ前に実行中フラグを立てて締め出す【Race Condition】

ボタンを連打すると、同じ処理が二重に走って二重送信になる。await をまたぐ前に実行中フラグを立てて締め出す【Race Condition】

非同期処理の途中(await の間)に同じ操作がもう一度入り、二重送信になる。実行中フラグを置く位置を直し、二度目の呼び出しを締め出す。TypeScript で実装し、テストで確かめます。

連載 コード調教師 (第1話 / 全12話)
Featured image of post コードテイマー シリーズ目次

コードテイマー シリーズ目次

非同期処理や並行処理で起きる「たまにだけ壊れる」バグを、TypeScript のコードで直していく全12話の目次。困りごと別の一覧から、読みたい話を選べます。

Featured image of post キャンセル機能を足したら、確認メールが2通届いた。部品同士の直結をやめ仲介役に集める【Mediator】

キャンセル機能を足したら、確認メールが2通届いた。部品同士の直結をやめ仲介役に集める【Mediator】

在庫・課金・通知・配送は分けたのに、繋ぎ方だけが直結のN対Nで、通知を出す係が配線に散っていた。連絡を仲介役に集約して、部品同士が互いを知らなくて済むようにする(Mediator)。Go で実装します。

連載 コードメカニック (第12話 / 全12話)
Featured image of post リクエストID の形式を変えるのに、30ファイルを直す羽目になった。横断処理を1か所に集める【Middleware】

リクエストID の形式を変えるのに、30ファイルを直す羽目になった。横断処理を1か所に集める【Middleware】

ログ・認証・recover・計測といった同じ前置きが30個のハンドラーにコピペされていた。先頭で呼ぶ関数では後処理が括れない理由を押さえ、ハンドラーを包む層に横断処理を集める(Middleware)。Go で実装します。

Featured image of post 共通関数にしても、HTTPの外から呼べない。ハンドラーに作り付けた処理を切り出す【Command】

共通関数にしても、HTTPの外から呼べない。ハンドラーに作り付けた処理を切り出す【Command】

共通関数に抽出しても、引数に http.ResponseWriter を握っている限り HTTP の外からは呼べない。処理を Execute(ctx) error だけで話す部品に切り出し、ハンドラーからもワーカーからも同じ形で実行する(Command)。Go で実装します。

Featured image of post 同じ直しが、3本目のバッチにだけ当たらなかった。丸写しの骨格を1本にまとめる【Template Method】

同じ直しが、3本目のバッチにだけ当たらなかった。丸写しの骨格を1本にまとめる【Template Method】

夜間バッチ3本が骨格ごとコピペで、共通のエラー処理の改修が1本だけ漏れ、接続断の朝にそのバッチだけ黙って正常終了した。骨格を1本に固定して工程だけ差し込む(Template Method)。Go で実装します。

Featured image of post 1年前に保管した証跡が、監査の朝に開かなかった。暗号化や圧縮を1枚ずつ重ねる【Decorator】

1年前に保管した証跡が、監査の朝に開かなかった。暗号化や圧縮を1枚ずつ重ねる【Decorator】

圧縮・暗号化・バッファを組み合わせごとの型で抱え、最も複雑な型の閉じ処理を落としたせいで、復元した証跡の末尾が欠けた。同じ io.Writer を1枚ずつ被せて、掛け算で増える型を積み重ねに変える(Decorator)。Go で実装します。