Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
2重リクエスト完全攻略HANDBOOK / Double Request Handbook
Search
ShoheiMitani
September 26, 2025
Technology
13k
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.5k
スーパーマンに頼らない"分権型組織"で作る強い開発チーム
shoheimitani
11
14k
データの整合性を保ちたいだけなんだ
shoheimitani
8
3.6k
プロポーザルに込める段取り八分
shoheimitani
4
1.7k
競馬で学ぶ機械学習の基本と実践 / Machine Learning with Horse Racing
shoheimitani
18
21k
AIの全社活用を推進するための安全なレールを敷いた話
shoheimitani
3
1.7k
The Citadel
shoheimitani
0
280
Rails-ishなActiveRecordの操作方法
shoheimitani
0
290
自己実現のためのキャリア選択 / Choosing a Career Path for Self-Realization
shoheimitani
2
700
Other Decks in Technology
See All in Technology
[JAWS-UG彩の国埼玉#12]そろそろ準備する?re:Invent2026をやさしく解説 / reinvent2026-sainokuni
sh_fk2
1
350
パスキーの自動テストはどこまでいけるのか
teamlab
PRO
0
280
脆弱性診断って、何をしているの? 〜弱点を見つけて、会社の改善につなげるまで〜
keeg
0
930
M5Stackで作る BLEマウス GPSログの世界
tomokusaba
PRO
0
440
フィンテック養成勉強会#68
finengine
0
120
DWH の限界突破! AI エージェント向け爆速リアルタイムデータ基盤 ClickHouse
jozono
1
490
QA4AI 2026-Oct 生成AI時代の品質保証
tokuty
1
890
Active Record Connection Adaptersにおける長年の課題に再び取り組む
yahonda
0
200
Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
1
740
1つのリクエストを追って理解する - llm-dのP/D分離
akhino
1
160
Persisting Massive Data with Rails ~Designing for 60 billion rows, and fighting the bottlenecks~ Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
0
730
ブラウザとサーバーから考えるAI時代のフロントエンド監視
shogo4131
3
1k
Featured
See All Featured
sira's awesome portfolio website redesign presentation
elsirapls
0
460
エンジニアに許された特別な時間の終わり
watany
109
250k
Product Roadmaps are Hard
iamctodd
55
13k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
74
43k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1.1k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
710
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Making the Leap to Tech Lead
cromwellryan
135
10k
HDC tutorial
michielstock
2
960
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