Upgrade to Pro — share decks privately, control downloads, hide ads and more …

デッドロックを回避するリポジトリ実装の勘所

Avatar for 男爵 男爵
April 16, 2023

 デッドロックを回避するリポジトリ実装の勘所

ScalaMatsuri2023の登壇資料です。

Avatar for 男爵

男爵

April 16, 2023
Tweet

More Decks by 男爵

Other Decks in Programming

Transcript

  1. Table of Contents ✘ Sample of DDD like domain model

    ✘ Implementation policy of Repository ✘ Making persistence of Value Object ✘ Deadlock examples and corrections ✘ Points of implementation of Repository ✘ More fundamental solutions of deadlock 3 目次
  2. Preconditions of System ✘ RDB is Amazon Aurora (compatible with

    MySQL 5.7). ✘ Default storage engine is InnoDB ✘ Default transaction separation level is Repeatable Read. ✘ Primary keys in tables are UUID. 4 MySQL 5.7、InnoDB、リピータブルリード、UUID
  3. 15 依存オブジェクトは唯一つの親を持つ 親以外からの参照は存在しない - A dependent must have exactly one

    owner. - There must be no references from any object other than the owner to the dependent Dependent Mapping
  4. Update logic for custom fields is very simple 16 DELETE

    FROM custom_fields where invoice_id = X; INSERT INTO custom_fields (id, invoice_id, …) VALUES (uuid(), X, …); INSERT INTO custom_fields (id, invoice_id, …) VALUES (uuid(), X, …); 値オブジェクトの更新は非常にシンプル
  5. Why that sql is effectively update value objects? ✘ Value

    Object don't have an identity. ✘ A permanent Value Object necessarily depends on one parent entity. ✘ Value Object is immutable. 17 なぜこのSQLが効果的に働くのか?
  6. 20 ロックの競合が発生しやすいクエリ TX1 > SELECT * FROM invoices iv JOIN

    custom_field cf ON cf.invoice_id = iv.id JOIN custom_field_master cfm ON cf.master_id = cfm.id WHERE iv.id = X FOR UPDATE; TX2 > SELECT * FROM invoices iv JOIN custom_field cf ON cf.invoice_id = iv.id JOIN custom_field_master cfm ON cf.master_id = cfm.id WHERE iv.id = Y FOR UPDATE; TX2 > (Waiting for lock granted …)
  7. 24 ロックの競合が発生しづらいクエリ TX1 > select * from invoices iv join

    custom_field cf on cf.invoice_id = iv.id where iv.id = X fo update; TX2 > select * from invoices iv join custom_field cf on cf.invoice_id = iv.id where iv.id = Y fo update; TX2 > (Query returns immediately)
  8. Correction Don`t select tables belonging to aggregate to recreate an

    aggregate. Select tables belonging to an aggregate itself only. 25 集約の再構築には、自身に所属するテーブルだけを使う
  9. 27 デッドロックの発生しやすいクエリ TX1 > select * from custom_fields where invoice_id

    = X for update; TX1 > delete from custom_fields where invoice_id = X; TX1 > insert into custom_fields values (uuid(), X, …); TX1 > (Waiting for lock granted …) TX2 > select * from custom_fields where invoice_id = Y for update; TX2 > delete from custom_fields where invoice_id = Y; TX2 > insert into custom_fields values (uuid(), Y, …); TX2 > Deadlock Detected!
  10. Correction Mix in many objects into aggregate in spite of

    difference of its life cycle Extract value object having its own life cycle from aggregate as a new aggregate. 30 独自のライフサイクルを持つ値オブジェクトを、新たな集 約として抽出する
  11. Correction Issue a query only one time and raise a

    deadlock alert to engineers. Retry queries having deadlock lisk automatically. 32 ロックが競合する可能性のあるクエリは、自動的にリトラ イする
  12. Points of implementation of Repository ✘ Don’t share a database

    row with another aggregate. ✘ If another lifecycle is detected extract a new aggregate. ✘ Retry failed transaction for deadlock. 34 リポジトリ実装の勘所
  13. More fundamental solutions of deadlock ✘ Consider to change transaction

    separation level. Do you really need that level of integrity? Think about boundary of aggregate. 36 トランザクション分離レベルの変更を検討する
  14. THANKS! Any questions? You can find me at ✘  @dnxkimox

    ✘ https:/ /dnskimox.hateblo.jp ✘ https:/ /note.com/dnskimo 37 ご清聴ありがとうございました!