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

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

男爵
April 16, 2023

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

ScalaMatsuri2023の登壇資料です。

男爵

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 ご清聴ありがとうございました!