Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
2重リクエスト完全攻略HANDBOOK / Double Request Handbook
Search
ShoheiMitani
September 26, 2025
Technology
12k
10
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
2重リクエスト完全攻略HANDBOOK / Double Request Handbook
Kaigi on Rails 2025
ShoheiMitani
September 26, 2025
More Decks by ShoheiMitani
See All by ShoheiMitani
「話せることがない」を乗り越える 〜日常業務から登壇テーマをつくる思考法〜
shoheimitani
4
1.1k
スーパーマンに頼らない"分権型組織"で作る強い開発チーム
shoheimitani
11
12k
データの整合性を保ちたいだけなんだ
shoheimitani
8
3.5k
プロポーザルに込める段取り八分
shoheimitani
4
1.4k
競馬で学ぶ機械学習の基本と実践 / Machine Learning with Horse Racing
shoheimitani
15
19k
AIの全社活用を推進するための安全なレールを敷いた話
shoheimitani
3
1.6k
The Citadel
shoheimitani
0
240
Rails-ishなActiveRecordの操作方法
shoheimitani
0
260
自己実現のためのキャリア選択 / Choosing a Career Path for Self-Realization
shoheimitani
2
590
Other Decks in Technology
See All in Technology
サイバーセキュリティ概論 / Introduction to Cybersecurity
ks91
PRO
0
170
ポケモンの型をTypeScriptの型システムで表現してみた
subroh0508
0
350
「コーディング」しない人のための Claude Code 入門 ChatGPT の次の一歩 — 業務に組み込む 育成・共有・自動化
rfdnxbro
2
1.2k
BigQuery の Cross-cloud Lakehouse への歩み
phaya72
2
600
はじめてのDatadog
kairim0
0
290
会社紹介資料 / Sansan Company Profile
sansan33
PRO
18
420k
Oracle AI Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
4
2.9k
[モダンアプリ勉強会]今更聞けないGit/GitHub入門
tsukuboshi
0
300
ITエンジニアを取り巻く環境とキャリアパス / A career path for Japanese IT engineers
takatama
4
1.8k
AIプラットフォームを運用し続けるための可観測性
tanimuyk
4
1.1k
実装は速くなった、レビューはどうする? ― 自身のレビューをAIで再現させるサーヴァントエンジニアリングのすゝめ / Implementation got faster. So what about reviews? — An invitation to Servant Engineering: Recreating your own code reviews with AI
nrslib
7
4.2k
Oracle AI Database@Azure:サービス概要のご紹介
oracle4engineer
PRO
6
1.9k
Featured
See All Featured
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
210
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
380
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
200
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Context Engineering - Making Every Token Count
addyosmani
9
950
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
Tell your own story through comics
letsgokoyo
1
950
We Have a Design System, Now What?
morganepeng
55
8.2k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
150
The Mindset for Success: Future Career Progression
greggifford
PRO
0
350
From π to Pie charts
rasagy
0
200
Transcript
2ॏϦΫΤετશ߈ུHANDBOOK Shohei Mitani @ εϚʔτόϯΫ
2ॏϦΫΤετ • ޡͬͯෳճಉ͡ϦΫΤετ͕ૹΒΕɺॏෳͯ͠ॲཧ͕ߦΘΕͯ͠·͏͜ͱ • αϒϛοτϘλϯ͕2ճԡ͞Εͨ • ొը໘͕Ϧϩʔυ͞Εͨ • ֎෦αʔϏε͔Βෳճಉ͡σʔλ͕ૹΒΕͨ •
όον͕2ճ࣮ߦ͞Εͨ • 2ॏαϒϛοτͱ͍͏ݴ͍ํ͋Δ͕ɺWebϒϥβҎ֎ʹى͜Γ͏Δ Λѻ͍͍ͨͷͰ2ॏϦΫΤετͱݺͿ • 2ճҎ্ϦΫΤετ͢ΔଟॏϦΫΤετ2ॏϦΫΤετͱݺͿ͜ͱʹ͢Δ
͠2ॏϦΫΤετ͕ൃੜͨ͠Β…?? • ܝࣔ൘ʹಉ͡༰͕2ճߘ͞ΕΔ... • ಉ͕͡2ճߪೖ͞Εͯɺ2ॏͰߴ͕ݮΔ͜ͱʹ… • ܾࡁ / ૹۚ /
༧ / ग़ / ථ / ใु༩ / ਃ / etc… • ѱ༻͞Εͯෆਖ਼߈ܸΛड͚ΔηΩϡϦςΟϗʔϧʹ… • Etc…
2ॏϦΫΤετͷରࡦҙ֎ͱ͍͠ • ΫϥΠΞϯτͰ੍ޚ͢Ε͍͍Μ͡Όͳ͍ͷ͔ʁ • ΫϦοΫ࿈ଧ / ϖʔδ࠶ಡΈࠐΈ / Δˠ࠶ૹ৴ͳͲɺͲ͜·ͰରԠͰ͖Δʁ •
ϒϥβͷσϕϩούʔπʔϧͰૢ࡞͞ΕͨΒʁ • ෳλϒʗෳ͔Βಉ࣌ʹૢ࡞͞Εͨ߹ʹͰ͖Δରࡦʁ • ֎෦αʔϏε͔Βͷࣗಈ࠶ૹͳͲɺΫϥΠΞϯτଆΛ੍ޚͰ͖ͳ͍߹ʹʁ • όοΫΤϯυͰςʔϒϧઃܭΛ্ख͘ΕେৎͰ͠ΐ • ϢχʔΫ੍͕͚ͭΒΕͳ͔ͬͨΓɺϩοΫ͕औΕͳ͍ঢ়گ͋Δ • ҙਤͨ͠ෳճͷૢ࡞ͱɺࣄނతʹൃੜͨ͠2ॏϦΫΤετΛผͰ͖Δʁ
Θ͟Θ͟Kaigi on RailsͰൃද͢Δ΄Ͳʁ • WebͰݕࡧͯ͠ग़ͯ͘Δํ๏WebϒϥβΛલఏͱ ͯ͠ͷ͕ଟ͍ • ʮJavaScritptͰʙʯʮ֬ೝμΠΞϩάग़ͯ͠ʙʯ • ʮઈରʹೋॏαϒϛοτΛڐ͞ͳ͍༑ͷձʯͷϒϩάΑ
͘·ͱ·ͬͯΔ • ݱ࣮ͷΞϓϦέʔγϣϯෳࡶͰ৭ʑͳέʔε͕͋Δ • ຖճɺରࡦΛௐͨΓಠࣗʹฤΈग़͢͜ͱʹർΕͨ • ͜Ε͚ͩݟ͓͚ͯେମΧόʔ͞ΕͯΔͷΛ࡞Γ͍ͨ https://qiita.com/unmelt/items/fb1eff5a2d9bc4da1f7e
ࠓͷൃදςʔϚ • ୭ʹͱͬͯۙͰॏཁͳ2ॏϦΫΤετʹ·ͳ͍͍ͯ͘ੈքΛ࡞Δ • 2ॏϦΫΤετͷͷཧղ • ༷ʑͳ2ॏϦΫΤετରࡦͷཧղ • ϢʔεέʔεʹԠͨ͡ϕετϓϥΫςΟεͷ୳ࡧ •
ྩ7࣌ͰͷใΛ·ͱΊͨHANDBOOKΛఏڙ • ۀͰૺ۰ͨ࣌͠ʹݟฦͤΔࢿྉͱͳΕ͍ • ଞʹ͜͏͍͏ͷ͋ΔΑʂͬͯํ๏͋Εڭ͍͑ͯͩ͘͞🙌 • ݸʑͷରࡦͷৄࡉׂѪ͠·͢
ࣗݾհ ࡾ୩ ণฏ Έͨʹ ͠ΐ͏͍ εϚʔτόϯΫ Engineering Manager / Software
Engineer • SIer → Fablic → ָఱ → εϚʔτόϯΫ • ΤϯδχΞྺ11ɺRailsྺ7 • Welcome Fintech Community ӡӦ
ৗͰ͍ͬͯΔαʔϏεͷཪଆΛެ։Մೳͳܗʹམͱ͠ࠐΜͰൃද͢Δͷ͕͖ builderscon 2024 YAPC::Hiroshima 2024 YAPC::Fukuoka 2025 eKYC Χʔυܾࡁ ڝഅ
x ػցֶश χον͗ͯ͢ڵຯ࣋ͨΕͳ͍? ެ։͢Δͱڝ߹༏Ґੑࣦ͏? ڪΕͣൃද͢Δ͜ͱͰಘΒΕΔ͜ͱଟ͍
ࠓͬͪ͜ͷ Kaigi on Rails 2022 Kaigi on Rails 2021 Kaigi
on Rails 2023 ։ൃݱͰͷ࣮ઓΛநԽɾ൚༻తֶͯ͠ͼʹม͍͑ͯ͘׆ಈ͖ ࢹ ঢ়ଶཧ ςʔϒϧఆٛมߋ
2ॏϦΫΤετͷޚࡦ
લఏ͢Γ߹Θͤʙհ͢Δޚࡦͷείʔϓʙ • POST / PUT / DELETEͷߋ৽ܥૢ࡞Λத৺ʹѻ͏ʢGETͷԠ༻Մʣ • ૹ৴ऀଆʹѱҙͳ͍͕ࣄނతʹ2ॏϦΫΤετ͕ൃੜ͢Δͷޚࡦ •
αϒϛοτϘλϯ͕ԿԡͤΔͷ࣮࿙Ε • όοΫΤϯυͰͷλΠϜΞτʹΑΓɺΫϥΠΞϯτ͔Β࠶ૢ࡞͕Ͱ͖Δέʔε • ૹ৴ऀଆ͕ѱҙΛ࣋ͬͯ2ॏϦΫΤετΛߦ͏Α͏ͳͷޚࡦ • ੬ऑੑΛಥ͍ͨϦϓϨΠ߈ܸ / ෳͰͷಉ࣌ૢ࡞ • ༷ͱͯ͠ಉ͡༰ͷૢ࡞͕ڐ༰͞Ε͍ͯΔέʔεͱͷ۠ผ͢Δํ๏ • ಉҰͷߪೖ / ࿈ߘ
ෳͷޚͰߟ͑Δ 📱 🐟 ΫϥΠΞϯτଆ ޚࡦͷҹ όοΫΤϯυଆ ޚࡦͷҹ
ᶃ αϒϛοτϘλϯͷ੍ޚ ᶄ Post-Redirect-Get (PRG) ύλʔϯ ᶅ ഉଞ੍ޚ • Ϧιʔεͷ൵؍ϩοΫ
• FIFOʹΑΔॱং੍ޚ • ΞυόΠβϦʔϩοΫ ᶆ ςʔϒϧઃܭ • ঢ়ଶભҠͷϧʔϧԽ • ϢχʔΫ੍ ࠓհ͢Δޚࡦͨͪ ᶇ ϨʔτϦϛοτ ᶈ APIΩϟογϡ ᶉ Idempotency-Keyϔομ ᶊ ETag ʴ If-Match ᶋ ϫϯλΠϜτʔΫϯ
ᶃ αϒϛοτϘλϯͷ੍ޚ • ࠷جຊతͰ࠷ॳʹΔͰ͋Δ2ॏϦΫΤετ Λ͙ํ๏ • ϘλϯΛԡͨ͠Βྃ͢Δ·Ͱඇ׆ੑԽ͢Δ • Βͳ͍ͱ͍͏બࢶͳͦ͞͏ʢ?ʣ •
ॲཧঢ়ଶΛอ͓͖࣋ͯ͠ɺॲཧதʹૢ࡞͞Εͨ ߹ʹϝοηʔδΛදࣔ͢Δ͜ͱՄೳ 📱
• ϒϥβͷϦϩʔυʹΑΔ2ॏϦΫΤετΛ͙ํ๏ • POSTϦΫΤετͷྃ࣌ɺHTMLΛฦ٫͢ΔͷͰͳ ͘ɺ݁ՌදࣔϖʔδϦμΠϨΫτ͢ΔϨεϙϯεΛฦ͢ • ॲཧྃޙʹϒϥβͰϦϩʔυΛͯ͠ɺϦμΠϨΫτઌ ͷGETॲཧͱͳΔͨΊɺݩͷPOSTॲཧͷ2ॏϦΫΤετ͕ى ͖ͳ͍ •
ΔϘλϯͰલը໘ʹ͔ͬͯΒɺ͏ҰαϒϛοτϘλ ϯΛԡ͞ΕΔέʔεʹແྗͳͷͰɺผରࡦ͕ඞཁ ᶄ Post-Redirect-Get (PRG) ύλʔϯ ΫϥΠΞϯτ αʔόʔ POST REDIRECT GETʢ݁Ռը໘ʣ HTML ϦϩʔυʢGETʣ 🐟
ᶅ ഉଞ੍ޚ • ͋ΔϓϩηεͷΈ͕ϦιʔεͷΞΫηεݖΛಠ͢Δ͜ͱͰɺ ಉ࣌ʹૢ࡞Ͱ͖ΔϓϩηεΛҰͭʹ੍ݶ͢ΔΈ • 2ॏϦΫΤετ͕ൃੜͯ͠αʔόʔଆͰ҆શʹॲཧͯ͠ޚ ͢Δํ๏ • ಉ࣌ʹಉ͡ϦιʔεΛૢ࡞͢Δͱσʔλͷෆ߹͕ى͖͍͢
• ϩετΞοϓσʔτͷ • ಉ࣮࣌ߦΛ͙͜ͱͰɺॲཧ෦Ͱߟྀ͠ͳ͚ΕͳΒͳ͍͜ͱ͕ ݮΒͤΔ 🐟 Table READ BLOCK READ
ᶅ ഉଞ੍ޚʙϦιʔεͷ൵؍తϩοΫʙ • RDBΛར༻͍ͯ͠Δ߹ʹ࠷؆୯ʹར༻Ͱ͖Δͷ൵؍తϩοΫ • User. fi nd(1).with_lock { …
} • ૢ࡞ରʹରͯ͠ɺదͳϩοΫΛऔΕΔϦιʔε͕ଘࡏ͠ͳ͍߹͋Δ • ΞυόΠβϦʔϩοΫͷར༻ʢMySQL: GET _LOCK / PostgreSQL: pg_advisory_lockʣ • ActiveRecord::Base.connection.get_advisory_lock(“UserID#{user_id}:Payment”) • ϩοΫઐ༻ͷςʔϒϧRedisΛ͏ߟ͑ΒΕΔ • ඇಉظతʹॲཧΛͯ͠ྑ͍߹ʹFIFOΩϡʔΛ͏͜ͱ • FIFOʢFirst-in, First-outʣͷΩϡʔΛ͏͜ͱͰɺΩϡʔ୯ҐͰॱ൪ʹॲཧ͕ߦΘΕΔ ͨΊɺϩοΫΛऔಘͨ࣌͠ͱಉ͡Α͏ʹഉଞ੍ޚ͞ΕΔ 🐟
ᶅ ഉଞ੍ޚʙҙʙ • ഉଞ੍ޚಉ࣌ʹૢ࡞͞ΕΔ͜ͱΛ͍Ͱ͍Δ͚ͩͳͷͰɺϩοΫղ์͞Εͨ ޙͷॲཧͷରԠผ్ߟ͑Δඞཁ͕͋Δ • ଈ࣌Τϥʔʹ͢ΔͳΒ : User. fi
nd(1).with_lock(“FOR UPDATE NOWAIT”) { … } • ଈ࠲Τϥʔɺಉ࣌ʹϩοΫΛऔಘͨ͠߹ʹݶΔͷͰɺλΠϜϥά͕͋ͬͯ2ॏϦΫ Τετ͕དྷͨ࣌ͷ͜ͱΛߟ͑Δඞཁ͕͋Δ • ϩοΫͷऔಘൣғʹҙ͕ඞཁ • 2ॏϦΫΤετΛ͍͗ͨAPIҎ֎ͰϩοΫղ์͕ͪൃੜ͠ɺϨεϙϯελΠϜ͕ѱ Խ͢Δͷ͋Δ͋Δͳ 🐟
ᶆ ςʔϒϧઃܭ • 2ॏϦΫΤετ͕ൃੜͯ͠σʔλͷ߹ੑΛอͯΔΑ͏ςʔϒϧઃܭ͓ͯ͘͠ ͜ͱͰޚ͢Δํ๏ • ഉଞ੍ޚͱҟͳΓɺಉ࣌ʹϦΫΤετ͕དྷͳ͍έʔεͰ͋ͬͯɺ͋Δ͖σʔλͷ ͕࢟อͨΕΔΑ͏ʹ͢Δ • ςʔϒϧઃܭͷํ๏ଟछଟ༷ʹ͋ΓɺશͯΛհ͢Δ͜ͱͰ͖ͳ͍ͷͰɺ
ݸਓతʹΑ͘͏ํ๏Λ2ͭհ 🐟
ᶆ ςʔϒϧઃܭʙঢ়ଶભҠͷϧʔϧԽʙ • ঢ়ଶؒͷભҠϧʔϧΛఆٛ͢Δ͜ͱͰɺ2ॏϦΫΤετ Λड͚औͬͨࡍʹঢ়ଶભҠΤϥʔΛൃੜͤ͞Δ • ӈͷྫͩͱɺจঢ়ଶͱ͍͏ENUMͷΧϥϜʹঢ়ଶભ ҠͷϧʔϧΛఆٛ͢Δ • ʮΩϟϯηϧঢ়ଶจঢ়ଶ͔Β͔͠ભҠͰ͖ͳ͍ʯ
• ʮٻঢ়ଶจঢ়ଶ͔Β͔͠ભҠͰ͖ͳ͍ʯ • ʮൃૹঢ়ଶٻঢ়ଶ͔Β͔͠ભҠͰ͖ͳ͍ʯ • RailsͩͱAASMͷgem͕͋Δ 🐟
ᶆ ςʔϒϧઃܭʙϢχʔΫ੍ʙ • ϢχʔΫ੍Λ͚ͭΔ͜ͱͰɺ2ॏϦΫΤετ Λड͚औͬͨࡍʹঢ়ଶભҠΤϥʔΛൃੜͤ͞Δ • ӈͷྫͩͱɺจͷঢ়ଶຖʹςʔϒϧΛׂ ͠ɺจIDʹϢχʔΫ੍ʢUQʣΛ͚ͭΔ • ΠϛϡʔλϒϧσʔλϞσϧ
• https://scrapbox.io/kawasima/ΠϛϡʔλϒϧσʔλϞσϧ 🐟
ঢ়ଶભҠͷϧʔϧԽ vs ϢχʔΫ੍ • ঢ়ଶભҠͷϧʔϧԽͰɺϩετΞοϓσʔτͷҙ͕ඞཁ • ผτϥϯβΫγϣϯͷ༰͕ಡΈऔΕͳ͍ͨΊɺӈਤͩͱٻ → ٻͷঢ়ଶมߋΛNGͱ͢ΔΑ͏ͳϧʔϧ͢Γൈ͚ΒΕΔ •
τϥϯβΫγϣϯΛషΔ߹ʹഉଞ੍ޚͱηοτͰߟ͑Δ͖ • ޙ͔Βॲཧͨ͠༰Ͱ্ॻ͖ͯ͠ͳ͍༷Ͱ͋Εෆཁ • ϢχʔΫ੍ͷ߹ɺτϥϯβΫγϣϯΛίϛοτͨ͠λΠϛ ϯάͰΤϥʔ͕ൃੜ͢ΔͨΊɺϩοΫෆཁͳར͕͋Δ • σʔλϕʔεͷ੍ศར TRN1 TRN2 *% ঢ়ଶ จ ࣌ ࣌ Table Table Table *% ঢ়ଶ ٻ ࣌ ࣌ *% ঢ়ଶ ٻ ࣌ ࣌ 🐟
ᶇ ϨʔτϦϛοτ • ഉଞ੍ޚ / ঢ়ଶભҠͷϧʔϧԽ / ϢχʔΫ੍͍ͣΕΞϓϦέʔγϣϯͰσʔλ ͷ߹ੑΛอͭͨΊͷΞϓϩʔν •
ΫϨδοτΧʔυʹਃ͠ࠐΉͳͲɺݱ࣮ͱͯ͠ࢦఆظؒʹෳૢ࡞͞Εͳ͍ͱ্ׂ༷ ΓΕΔػೳͰ͋ΕɺΑΓ্ҐϨΠϠʔͰͷޚ͕Մೳ • ϨʔτϦϛοτ : γεςϜͷաͳෛՙΛ͙ͨΊʹɺҰఆظؒʹॲཧͰ͖Δ ϦΫΤεττϥϑΟοΫͷྔΛ੍ݶ͢Δख๏ͷ͜ͱ • ϢʔβʔIDͳͲΛΩʔʹAPIʹϨʔτϦϛοτΛಋೖ͠ɺҰఆظؒ1ճ͔͠ϦΫΤε τͰ͖ͳ͍Α͏ʹ͢Δ 🐟
ϨʔτϦϛοτͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'/PU&YJTU ͷϩοΫ ߪೖঢ়ଶͷมߋ &UDʜ
σʔλετΞ 🎁ߪೖྃ 🐟 1045QBZNFOUT nJUFN@JEJUFN@ 1BZNFOUJUFN $IFDL 63-1045QBZNFOUT *UFN*%JUFN@ 4BWF 63-1045QBZNFOUT *UFN*%JUFN@ 55-
ϨʔτϦϛοτͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ σʔλετΞ 🐟 1045QBZNFOUT nJUFN@JEJUFN@ ❌'PSCJEEFO $IFDL 63-1045QBZNFOUT
*UFN*%JUFN@ 💀ߪೖࣦഊ ⚙*'&YJTU$PVOU Τϥʔॲཧ
• Amazon API GatewayͳͲͷඪ४ػೳʢεϩοτϦϯάʣΛར༻͢Δ͜ͱՄೳ • εϩοτϦϯάͷద༻ൣғʢϦʔδϣϯ / APIΩʔ/ϝιουʣͷઃܭ࣍ୈ • ྫ:
toB͚αʔϏεͷΫϥΠΞϯτຖʹAPI KeyΛൃߦ͠ɺͦͷKeyϨϕϧͰಛఆ ΤϯυϙΠϯτͷϦΫΤετΛ1ճʹߜΔͳͲ • ࡉ͔͍୯ҐͰ੍ޚ͕ඞཁͳ߹ࣗલߏங͢Δඞཁ͋Γ • rack-attack gem / throttling gem ͋ͨΓ͕ࢀߟʹͳΔ͔ • Rails 8͔Βඪ४ͰϨʔτϦϛοτͷػೳ͕ೖͬͨ ϨʔτϦϛοτͷิ 🐟
ᶈ APIΩϟογϡ • Ұड͚͚ͨϦΫΤετͷϨεϙϯεΛอଘ͓͍ͯͯ͠ɺಉҰϦΫΤετ͕ དྷͨ߹ʹΩϟογϡͨ͠ϨεϙϯεΛฦ٫͢Δ͜ͱͰႈੑΛ୲อ͢ΔΞ ϓϩʔν • ΫϥΠΞϯτଆ͔ΒݟΔͱ2ॏͰૹ৴ͨ͠ࡍɺͲͪΒޭѻ͍ͱͯ͠ϋϯυϦϯά Ͱ͖ΔʢϨʔτϦϛοτͱͷҧ͍ʣ •
Amazon API GatewayͳͲͷػೳΛར༻͢Δ͜ͱʹΑΓɺΞϓϦέʔγϣϯͷ ֎ଆͰ2ॏϦΫΤετΛޚ͢Δ͜ͱ͕Մೳ 🐟
APIΩϟογϡͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'/PU&YJTU ͷϩοΫ ߪೖঢ়ଶͷมߋ &UDʜ
σʔλετΞ 🎁ߪೖྃ 🐟 1045QBZNFOUTJUFN@ 1BZNFOUJUFN $IFDL ,FZ1045QBZNFOUTJUFN@ 4BWF ,FZ1045QBZNFOUTJUFN@ 3FTQPOTF#PEZYYYY 55-
APIΩϟογϡͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'&YJTU 3FTQPOTF#PEZͷऔಘ σʔλετΞ 🎁ߪೖྃ 🐟 1045QBZNFOUTJUFN@
1BZNFOUJUFN DBDIFE $IFDL ,FZ1045QBZNFOUTJUFN@
• ΩϟογϡΛ͏͜ͱͷҰൠతͳࠔ͞ʹཱ͔ͪ͏ඞཁ͕͋Δ • ΩϟογϡͷKeyΛͲ͏͢Δ͔ʁ • ΩϟογϡͷTTLΛͲ͏͢Δ͔ʁ • ΩϟογϡͰ͖ΔαΠζͷݶք • GET·ͩ͠ɺPOSTܥͷAPIΛΩϟογϡͯ͠ྑ͍ͷ͔ʁ
• Ωϟογϡ͕อଘ͞ΕΔ·Ͱͷؒʹൃੜ͢Δ2ॏϦΫΤετແྗ APIΩϟογϡͷิ 🐟
• ΫϥΠΞϯτ͕ϦΫΤετΛҰҙʹࣝผͰ͖ΔใʢIdempotency KeyʣΛ HTTPϔομʹ༩͢Δ͜ͱʹΑΓႈͳAPIΛߏங͢ΔΞϓϩʔν ᶉ Idempotency-Keyϔομ 🐟 📱 https://speakerdeck.com/ohbarye/safe-retry-with-idempotency-key-header https://speakerdeck.com/ohbarye/my-favorite-protocol-idempotency-key-header
Idempotency-Keyϔομͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'/PU&YJTU ͷϩοΫ ߪೖঢ়ଶͷมߋ &UDʜ
σʔλετΞ 🎁ߪೖྃ 1045QBZNFOUT nJUFN@JEJUFN@= )t*EFNQPUFODZ,FZYZ[u 1BZNFOUJUFN $IFDL ,FZYZ[ 4BWF ,FZYZ[ 3FTQPOTF#PEZYYYY 55- 🐟 📱 ,FZ࠾൪
Idempotency-Keyϔομͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'&YJTU 3FTQPOTF#PEZͷऔಘ σʔλετΞ 🎁ߪೖྃ 1BZNFOUJUFN DBDIFE
🐟 📱 1045QBZNFOUT nJUFN@JEJUFN@= )t*EFNQPUFODZ,FZYZ[u ,FZ࠾൪ $IFDL ,FZYZ[
APIΩϟογϡ vs Idempotency-Keyϔομ • Idempotency KeyϔομAPIΩϟογϡΑΓෳࡶ͕Ώ͑ʹॊೈ͔ͭݎ࿚ʹႈͳAPIαʔ όʔΛ࡞ΕΔ • ΫϥΠΞϯτ͕Λ࠾൪ͯ͠ઃఆ͢ΔͨΊɺႈͷൣғ͕APIΩϟογϡΑΓݫີ •
ಉҰͰͷૢ࡞Λผ͢Δ͜ͱ͕Ͱ͖Δ • ಉҰͰͷϦτϥΠૢ࡞ͱɺ1͔ΒͷΓ͠ͷૢ࡞ผͰ͖Δ • ະॲཧ / ॲཧத / ॲཧࡁΈͱࡉ͔͘ॲཧεςʔλεΛཧͰ͖Δ • ةݥͳߋ৽ܥͷૢ࡞ʹରͯ҆͠શʹႈੑΛ࣋ͨͤΔͨΊʹɺAPIΩϟογϡΑΓ Idempotency-Keyϔομͷํ͕༏Ε͍ͯΔ • ෭࡞༻͕গͳ͍؆қతͳૢ࡞Ͱ͋ΕAPIΩϟογϡͰेͳέʔε 🐟 📱
ᶊ ETag ʴ If-Match • ETagʢEntity Tagʣͱ͍͏ߋ৽ରͷόʔδϣϯΛλά ཧ͠ɺIf-MatchϔομͰ͖݅ߋ৽ϦΫΤετΛૹΔ͜ ͱͰ2ॏϦΫΤετΛ͙ํ๏ʢָ؍తϩοΫʣ •
αʔόʔଆͰɺΫϥΠΞϯτ͔ΒૹΒΕͨETagͷΛ ൺֱ͠ɺόʔδϣϯ͕Ұகͨ͠ΒॲཧΛߦ͍ɺෆҰகͷ ߹ʹ412 Precondition FailedΛฦ٫͢Δ • ӈͷྫͩͱόʔδϣϯΧϥϜΛETagͱͯ͠ར༻͢Δ͜ͱ Ͱ͖Δ͠ɺ༰ͷtext͔ΒHashΛٻΊͯETagͱ͢Δ͜ ͱՄೳ 🐟 📱
ETag ʴ If-Matchͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'7FSTJPO.BUDI ͷϩοΫ &UDʜ
σʔλετΞ 🎁ߪೖྃ 1045QBZNFOUT nJUFN@JEJUFN@= )t*G.BUDIu 1BZNFOUJUFN (FU*UFN 7FSTJPO 🐟 📱 (&5JUFNTJUFN@ &5BH 6QEBUF*UFN 7FSTJPO $IFDL $VSSFOU7FSTJPO
ETag ʴ If-Matchͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'7FSTJPO/PU.BUDI Τϥʔॲཧ σʔλετΞ 1045QBZNFOUT
nJUFN@JEJUFN@= )t*G.BUDIu ❌1SFDPOEJUJPO'BJMFE (FU*UFN 7FSTJPO 🐟 📱 (&5JUFNTJUFN@ &5BH $IFDL $VSSFOU7FSTJPO 💀ߪೖࣦഊ
• ָ؍తϩοΫͰ͍Ͱ͍Δํ๏ͳͨΊɺมߋ͞Ε͍͢Ϧιʔεͷಋೖʹෆ͖ • ྫ͑ECαΠτͷߪೖͳͲɺෳਓ͕ಉ࣌ߪೖ͢Δέʔεʹ͍͍ͯͳ͍ • ϢʔβʔͷϓϩϑΟʔϧߋ৽ͳͲɺ1ਓ͔͠ฤू͠ͳ͍έʔεʹ͍͍ͯΔ • ΫϥΠΞϯτ͕ϔομΛ༩͢Δ͜ͱͰ2ॏϦΫΤετͷରࡦΛ͍ͯ͠ΔͰ Idempotency-Keyϔομͱಉ͕ͩ͡ɺతʹҧ͍͕͋Δ •
Idempotency-Key : ϔομͷKeyใΛͬͯɺႈͳAPIΛఏڙ͢Δ • ETag ʴ If-Match : ָ؍తϩοΫͰҟͳΔΫϥΠΞϯτؒͰͷಉ࣌ߋ৽Λ͙ ETag ʴ If-Matchͷิ 🐟 📱
• 1ͷΈ༻ՄೳͳτʔΫϯΛൃߦ͢Δ͜ͱͰ2ॏϦΫΤετΛ͙Ξϓϩʔν • 2ॏϦΫΤετͷతҎ֎ʹɺਖ਼ͳϦΫΤετͰ͋Δ͜ͱΛอূͯ͠վ͟ΜΛ͍ ͩΓɺ༗ޮظݶΛઃ͚Δ͜ͱͰΑΓηΩϡΞʹ͢ΔͳͲෆਖ਼ରࡦతͰΘΕΔ • τϥϯβΫγϣϯτʔΫϯνΣοΫͱ͍͏ݴ༿Ͱհ͞ΕΔ͜ͱ • CSRFτʔΫϯτʔΫϯΛͬͯϦΫΤετΛνΣοΫ͍ͯ͠ΔͷͰࣅ͍ͯΔ͕ɺ CSRFରࡦͷจ຺ͰτʔΫϯ͕ϫϯλΠϜͰ͋Δ͜ͱඞਢͰͳ͍
ᶋ ϫϯλΠϜτʔΫϯ 🐟 📱
ϫϯλΠϜτʔΫϯͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'5PLFOJT7BMJE ͷϩοΫ &UDʜ σʔλετΞ 🎁ߪೖྃ
1045QBZNFOUT nJUFN@JEJUFN@= nUPLFOYZ[ 1BZNFOUJUFN *TTVF UPLFOYZ[ 🐟 📱 1045UPLFOT 5PLFOYZ[ %FMFUF5PLFO $IFDL UPLFOYZ[
ϫϯλΠϜτʔΫϯͷϑϩʔ 📱ΫϥΠΞϯτ 🐟αʔόʔ ⚙*'5PLFOJT*OWBMJE Τϥʔॲཧ σʔλετΞ 1045QBZNFOUT nJUFN@JEJUFN@= nUPLFOYZ[
❌'PSCJEEFO *TTVF UPLFOYZ[ 🐟 📱 1045UPLFOT 5PLFOYZ[ %FMFUF5PLFO $IFDL UPLFOYZ[ 💀ߪೖࣦഊ
ޚࡦͷ·ͱΊ
ᶃ αϒϛοτϘλϯͷ੍ޚ ᶄ Post-Redirect-Get (PRG) ύλʔϯ ᶅ ഉଞ੍ޚ • Ϧιʔεͷ൵؍ϩοΫ
• FIFOʹΑΔॱং੍ޚ • ΞυόΠβϦʔϩοΫ ᶆ ςʔϒϧઃܭ • ঢ়ଶભҠͷϧʔϧԽ • ϢχʔΫ੍ հͨ͠ޚࡦͨͪ ᶇ ϨʔτϦϛοτ ᶈ APIΩϟογϡ ᶉ Idempotency-Keyϔομ ᶊ ETag ʴ If-Match ᶋ ϫϯλΠϜτʔΫϯ
αϒϛοτϘλϯͷ੍ޚ ޚࡦͷάϧʔϐϯά Idempotency-Keyϔομ ETag ʴ If-Match ϫϯλΠϜτʔΫϯ ΫϥΠΞϯτͰͷޚࡦ ΫϥΠΞϯτͱόοΫΤϯυͰͷޚࡦ όοΫΤϯυͰͷޚࡦ
Post-Redirect-Get (PRG) ύλʔϯ ഉଞ੍ޚ ςʔϒϧઃܭ ϨʔτϦϛοτ APIΩϟογϡ 🐟 📱 🐟 📱
ൃදऀͷߟ͑ • جຊαϒϛοτϘλϯͷ੍ޚ / PRGύλʔϯ / ഉଞ੍ޚ + ςʔϒϧઃܭΛߦ ͳ͍ͬͯΕेͳέʔε͕ଟ͍
• σʔλͷ߹ੑ͕֬อ͞ΕΔΑ͏ςʔϒϧઃܭΛ͍ͯ͠Εக໋తͳى͖ͳ͍ • 2ॏϦΫΤετࣗମɺશମͷϦΫΤετ͔ΒݟΔͱΤοδέʔεʹͨΔͷͰɺ2ॏϦ ΫΤετ͕ൃੜͨ͠ࡍͷඃಘΒΕΔϝϦοτΛఆͯ͠ɺదͳΞϓϩʔνΛબ ͖͢ • ϫϯόϯΫͰ࣮ࡍʹར༻͍ͯ͠ΔޚࡦΛཧ༝ͱͱʹհ
ϓϦϖΠυΧʔυ / ೖۚ / ग़ۚ Citadel / ֎෦αʔϏεґଘ
Idempotency-Keyϔομ • ΫϥΠΞϯτଆͰ࠾൪ͨ͠KeyΛͬͯႈͳAPIΛߏங ͢Δํ๏ • ֎෦αʔϏεͷॏཁͳૢ࡞Λߦ͏ॲཧͰଟ͘ར༻ • ίϯϏχ / ϖΠδʔͳͲͷೖۚใΛൃߦ͢Δॲཧ
• ΫϨδοτΧʔυೖۚΛ͢Δࡍͷ3DSೝূॲཧ • ۜߦޱ࠲͔ΒҾ͖མͱ͠ॲཧ
Idempotency-Keyϔομ • ֎෦αʔϏεͱͷ࿈ܞॲཧෳࡶʹͳΓ͕ͪ • ෳͷDB τϥϯβΫγϣϯ͕ඞཁͰഉଞ੍ޚ͕ෳࡶʹͳΔ • ࣗαʔϏεଆͱ֎෦αʔϏεଆͷ྆ํͰσʔλෆ߹͕ൃ ੜ͢ΔՄೳੑ͕͋ΓɺϦΧόϦॲཧ͕େม •
Idempotency-KeyϔομΛར༻ͯ͠ຊॲཧͷલஈͰ2ॏ ϦΫΤετͷରࡦΛߦ͏͜ͱͰɺσʔλෆ߹͕ൃ ੜ͢ΔϦεΫΛݮΒ͢͜ͱ͕Ͱ͖Δ ֎෦αʔϏε ݺͼग़͠ લॲཧ τϥϯβΫγϣϯ ޙॲཧ τϥϯβΫγϣϯ ֎෦αʔϏεݺͼग़͠ॲཧ Idempotency-Key ϔομʹΑΔޚ
Idempotency-Keyϔομ • ࣄނతͳ2ॏϦΫΤετͱҙਤతͳผϦΫΤετΛ۠ผ͢Δ͜ͱ͕Ͱ͖Δͷັྗత • ྫ͑ϓϦϖΠυΧʔυͷೖۚॲཧ࣌ؒͰෳճߦΘΕ͓͔ͯ͘͠ͳ͍ • Key͕ಉ͡ = ࣄނతͳϦΫΤετɺKey͕ҟͳΔ =
ҙਤతͳϦΫΤετͱ۠ผͰ͖Δ • ҰํɺKeyͷνΣοΫॲཧొॲཧͳͲͷΦʔόʔϔου͕૿͑ͨΓɺΫϥΠΞϯτଆ ͰͷKey࠾൪ॲཧͷ࣮ͳͲ࣮ίετ͕͔͔ΔͨΊɺಋೖՕॴݶఆత
ϫϯλΠϜτʔΫϯ • ΞϓϦͷதͰಛʹॏཁͳॲཧͰ༻ • ύείʔυͷొ / ࠶ઃఆ • Χʔυ൪߸ͷදࣔ •
͋ͱΒ͍νϟʔδۜߦޱ࠲Ҿ͖མͱ͠ͳͲͷೖۚ ॲཧ
ϫϯλΠϜτʔΫϯ • Ϣʔβʔͷࣄނతͳ2ॏϦΫΤετΛࢭ͢ΔతͰͳ͘ɺϦΫΤετ༰ͷ౪ௌͳͲ ʹΑΔϦϓϨΠ߈ܸΛ͙తͷͨΊʹ༻ • ϦϓϨΠ߈ܸ : αΠόʔ߈ܸͷҰछͰɺ௨৴ͰҰૹ৴͞Εͨਖ਼نͷσʔλΛड͠ɺͦ ͷ··࠶ૹ৴͢Δ͜ͱͰɺෆਖ਼ͳಈ࡞Λ༠ൃ͢Δ߈ܸ •
ೖۚॲཧ͕౪ௌ͞ΕͯԿෆਖ਼ೖۚ͞ΕΔͳͲͷ߈ܸΛ͙ • ࣄނతͳ2ॏϦΫΤετΛ͙ͨΊɺIdempotency-KeyϔομͱΈ߹Θ࣮͍ͤͯͯ͠ ΔAPIଟ͍ • τʔΫϯऔಘϑϩʔͳͲ࣮ίετɺϢʔβʔͷख͕ؒൃੜ͢Δํ๏ͳͷͰɺ࣮Օॴ ηΩϡϦςΟڧΛ্͍͛ͨՕॴͷΈ
ϨʔτϦϛοτ • όʔνϟϧΧʔυͱ͍͏ཧతͳΧʔυൃߦ͞ΕͣΧʔυ ൪߸͚ͩೖखͰ͖ΔػೳͷൃߦॲཧͰར༻ • όʔνϟϧΧʔυԿͰ࠶ൃߦͰ͖Δ͕ɺ࣌ؒʹԿ ࠶ൃߦ͢Δ͜ͱਖ਼ৗͳঢ়ଶͰͳ͍ • ؒҧ͑ͯΧʔυൃߦ͕ى͖ͨࡍ֎෦αʔϏεଆͷमਖ਼ඞ ཁͰӡ༻ίετ͕ߴ͍
• Χʔυൃߦ2ͭͷαʔϏεʹލͬͯॲཧ͞ΕΔ͕ɺΧʔυൃ ߦଆͰϩοΫΛऔಘͰ͖ΔదͳϦιʔε͕ଘࡏ͠ͳ͔ͬͨ • ϢʔβʔຖͷൃߦճΛอଘ͠ɺࢦఆճΛ͑ͨΒΤϥʔ ൃੜ͢Δ༷ʹ ϞόΠϧΞϓϦ #'' *TTVJOH"1* $PSF"1* ֎෦αʔϏε
ϨʔτϦϛοτ • ഉଞ੍ޚͰ͖ΔΑ͏ʹطଘͷΈΛमਖ਼ͨ͠ΓɺΞυ όΠβϦʔϩοΫݕ౼͕ͨ͠ίεύ؍Ͱෆ࠾༻ • ϨʔτϦϛοτͷ੍ޚςʔϒϧΛҰͭՃ͢ΔఔͰ࣮ݱ Մೳ • Idempotency-Keyϔομͷಋೖݕ౼͕ͨ͠ɺΧʔυൃߦ αʔϏεଆʹͦͷΈ͕͍ͬͯͳ͔ͬͨͨΊෆ࠾༻
• ໓ଟʹൃੜ͠ͳ͍͕ɺ͠ൃੜͨ͠Βσʔλमਖ਼ͷӡ ༻ίετ͕ߴ͍ΛίετͰղܾ https://blog.smartbank.co.jp/entry/2025/03/19/123000
ςʔϒϧઃܭ + Ωϟογϡ • ίϯϏχ / ϖΠδʔͳͲͷೖۚํ๏ͰɺϢʔβʔ͕ϨδͰ͓ۚ Λࢧͬͨޙʹྃใ͕WebhookͰૹΒΕΔ • WebhookʹϦτϥΠػೳ͕ఏڙ͞Ε͍ͯΔ͜ͱ͕ଟ͍
• λΠϜΞτ͕ൃੜ͢Δͱ֎෦αʔϏεଆʹΤϥʔผ͞Εͨ ͚Ͳ࣮ࡍʹޭঢ়ଶͱ͍͏σʔλෆ߹͕ൃੜ͢Δ • ϦτϥΠ࣌ʹɺςʔϒϧઃܭͰ߹ੑΛอ͍ͬͯΔ͔ΒͱΤϥʔ ൃੜͤ͞ΔͱΤϥʔϧʔϓ͕ൃੜ͢Δ • ֎෦αʔϏεΛར༻͍ͯ͠Δͱ͖ɺIdempotency-Keyϔομͳ ͲͷΫϥΠΞϯτଆͷมߋ͕Ͱ͖ͳ͍ ֎෦ αʔϏε αʔόʔ POST TIMEOUT RETRY ? DB Table SAVE SUCCESS
ςʔϒϧઃܭ + Ωϟογϡ • ςʔϒϧઃܭ্Ұҙ੍ΛೖΕΔͳͲͰ߹ੑΛ֬อ͓ͯ͠ ͖ɺWebhookΛड͚औͬͨࡍʹରϦιʔεͷঢ়ଶΛ֬ೝ ͠ɺॲཧ͍ྃͯͨ͠ΒޭΛฦ͢͜ͱͰႈʹ͢Δ • ͨͩ͠ႈʹ͢ΔϦΫΤετΛಛఆͰ͖ΔIDͳͲؚ͕·Ε͍ͯͳ ͍ͱ࣮ݱͰ͖ͳ͍ͷͰɺ֎෦αʔϏεͷ༷࣍ୈ
֎෦ αʔϏε αʔόʔ POST TIMEOUT RETRY SUCCESS DB Table SAVE FETCH SUCCESS
·ͱΊ
·ͱΊ • ୭ʹͱͬͯۙͰॏཁͳ2ॏϦΫΤετʹ·ͳ͍͍ͯ͘ੈքΛ࡞Δ • 2ॏϦΫΤετ͕ͲͷΑ͏ͳͳͷ͔આ໌ • 2ॏϦΫΤετͷޚ๏Λ9ݸհ • ϫϯόϯΫͰͷϢʔεέʔεͱ࠾༻͍ͯ͠Δޚ๏Λհ •
ۀͰʮͲ͏͠Α͏ʯͱͳͬͨ࣌ʹͥͻݟฦ͍ͯͩ͘͠͞ʂ • ࠓൃදͨ͠ύλʔϯΛ୯ಠ or Έ߹ΘͤΔ͜ͱͰͳΜͱ͔ͳΔέʔε͕ଟ͍ͣ
None