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
updated_at に依存したら大変なことになった / Don't depend on up...
Search
megane42
November 28, 2019
Programming
650
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
updated_at に依存したら大変なことになった / Don't depend on updated_at
Presented at :
https://gotanda-rb.connpass.com/event/155193/
megane42
November 28, 2019
More Decks by megane42
See All by megane42
Immutable ActiveRecord
megane42
0
370
Rails deprecation warning に立ち向かう技術 / v.s. rails deprecation warnings
megane42
0
840
OSS コミットゴルフのすすめ / Let's play OSS-contribute-golf
megane42
0
140
ゆる計算理論ラジオ / P vs NP for beginner
megane42
1
290
How to Make "DJ giftee"
megane42
1
1k
Rails 6 Upgrade "Practical" Guide
megane42
6
1.4k
本当は怖い Rails の `build_xxx` / The Hard Facts of `build_xxx` of Rails
megane42
0
290
Other Decks in Programming
See All in Programming
FreeBSDでZabbixを動かす
kenkino
0
310
JRuby: Past, Present, and Future
headius
0
170
setup-vp GitLab対応の裏側
naokihaba
0
120
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
2
290
巨大モノリシックアプリ モダン化大作戦
ktcryomm
1
1.1k
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
4
3.2k
そのリトライ、死んだコネクションを使い回していませんか ── GoのHTTPクライアントとHTTP/2を実プロダクト障害から学び直す
myus4a
0
200
GKE で Pod の見方を変えたら、スケールアウト時の挙動を真に捉えられた話
stkk
0
130
Agents on Rails - Rails at Scale 2026
irinanazarova
0
120
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
290
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
120
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
250
Featured
See All Featured
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
700
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
330
Documentation Writing (for coders)
carmenintech
77
5.5k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
280
Paper Plane
katiecoart
PRO
4
53k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
760
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
500
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
200
The Language of Interfaces
destraynor
162
27k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Transcript
updated_at に依存したら ⼤変なことになった megane42 / Hikaru Kazama @ giftee 2019/11/28
gotanda.rb
免責 このスライドに載せたコードや スキーマは⼀部簡略化しています このしくじりによるトラブルは、今はすべて解消しています
第 I 部 : 背景
弊社 giftee デジタルギフトを作って売っています
プロダクト giftee campaign platform フォロー / RT すると抽選でギフトをプレゼントします
機能紹介 実績集計機能 管理画⾯から ⽇ごとの抽選者数 がわかる
create_table "entries" do |t| # ... t.bigint "campaign_id", null: false
t.string "lottery_status", default: "fresh" t.datetime "created_at", null: false t.datetime "updated_at", null: false # ... end
class Entry # ... enum lottery_status: [ :fresh, :winner, :loser
] def draw! 抽選処理 ? winner! : loser! end def self.daily_drawers group("DATE(updated_at)").count end # ... end
当時の実装 抽選を回すと entry の updated_at が更新される 逆に、それ以外に更新される機会はない じゃあ updated_at を使って
⽇ごとの抽選者数 を集計しよう 後述しますが、 これ⾃体はしくじりじゃない と思ってます
第 II 部 : いよいよしくじります
悲劇はこの⽇起きた 2018-09-26 とある⼤型メンテの⽇ entries テーブルにカラムを追加 entries テーブルのレコード全体にデータ遡及が必要
Entry.winners.each do |e| e.update(new_column: "foo") end
+---------+----------------+---------------------+---------------------+ | id | lottery_status | created_at | updated_at |
+---------+----------------+---------------------+---------------------+ | 5 | winner | 2017-08-04 02:37:51 | 2018-09-26 01:53:54 | | 20 | winner | 2017-08-04 12:55:04 | 2018-09-26 01:53:54 | | 42 | winner | 2017-08-04 13:57:21 | 2018-09-26 01:53:54 | | 50 | winner | 2017-08-04 14:07:08 | 2018-09-26 01:53:54 | | 93 | winner | 2017-08-04 14:55:32 | 2018-09-26 01:53:54 | | 109 | winner | 2017-08-04 15:08:01 | 2018-09-26 01:53:54 | | 121 | winner | 2017-08-04 15:18:09 | 2018-09-26 01:53:54 | | 137 | winner | 2017-08-04 15:28:05 | 2018-09-26 01:53:54 | | 147 | winner | 2017-08-04 15:36:19 | 2018-09-26 01:53:54 | | 177 | winner | 2017-08-04 15:56:18 | 2018-09-26 01:53:54 |
しくじり データ遡及⽤スクリプトで updated_at を考慮していなかった その結果、ほとんどのレコードの updated_at がメンテ時刻に更新 された 実績が壊れた 2018-09-26
の当選者が⼤量に発⽣
第 III 部 : 対応
⼀次対応 created_at で代⽤ ほとんどの場合 created_at と updated_at は数秒の差しかない entry レコード作成直後に抽選を実⾏しているから
恒久対応 drawed_at カラムを新設 抽選実⾏時に時刻を埋める 既存のレコードに関しては created_at の値をコピー
class Entry # ... def draw! + transaction do 抽選処理
? winner! : loser! + update!(drawed_at: Time.zone.now) + end end # ... end
と簡単に⾔うけれど entries テーブルにカラムを増やすのはかなり⼤変 レコード数が多い n000 万のオーダー 常にレコードが増え続けている マイグレーション中にロックがかかると困る ⼆次被害を避けるために、念⼊りなリハーサルを実施
第 IV 部 : 教訓
何がしくじりだったのか? はじめから drawed_at のような専⽤カラムを作らなかったこと? メンテ時に updated_at を考慮し忘れたこと?
トレードオフ もし drawed_at を⽤意するなら: 抽選処理の実装時に、 drawed_at を埋める処理を忘れずに書く必 要がある データ遡及メンテのときは、何も考えなくてよい else
( updated_at に依存するなら): 抽選処理の実装時には、何も考えなくてよい データ遡及メンテのときに、 updated_at を更新してしまわない か気にする必要がある
トレードオフ(抽象化) もし専⽤カラムを作るなら: 定常的な開発時にひと⼿間かかる 突発的なメンテ時に何も考えなくてよい else ( updated_at に依存するなら): 定常的な開発時に何も考えなくてよい 突発的なメンテ時にひと⼿間かかる
個⼈的な意⾒ 突発的なメンテ時の⽅が、慌てていることが多い 突発的なメンテ時に何も考えなくてよい⽅がうれしい 基本的には専⽤カラムを⽤意した⽅がよさそう
別の観点からの教訓 知識に経験が伴うと⼈は強くなる created_at updated_at とは別に専⽤カラムを⽤意する流派があ ること⾃体は知っていたが、「なぜそうするのか」まではわかっ ていなかった 今は、 ⾔葉ではなく⼼で理解できた わけもわからず従っているベストプラクティスがまだまだある
きっとそれらにも理由がある
まとめ updated_at に依存したコードを書いている⼈は、突発メンテ時に ⼗分気をつけましょう 不安な場合は専⽤カラムを作りましょう たくさん経験を積んで or 共有しあって強くなっていきましょう
updated_at に依存したら ⼤変なことになった megane42 / Hikaru Kazama @ giftee 2019/11/28
gotanda.rb
おまけ : DB メンテのリハーサル中に得た知⾒
nullable なカラムを追加している最中でも INSERT できる (Aurora (MySQL)) MySQL にはオンライン DDL という機能があり、ALTER
TABLE 中 に更新系のクエリが実⾏できる https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl- operations.html MySQL 互換をうたっている Aurora は、その辺も互換性あるの? やってみたら追加できた !!! 必ずご⾃⾝の環境でも確認してください !!!
テーブル全体を UPDATE したら徐々にロック された (Aurora (MySQL)) UPDATE entries SET drawed_at
= created_at WHERE entries.drawed_at IS NULL 上記の SQL を実⾏すると、 entries テーブルが ID : 1 から徐々にロ ックされていった 更新ができないだけでレコード新規追加はできる 対象レコードを「ID 1 から 100 万まで」のように絞りながら⼩刻み に実⾏していくことで、影響を最⼩化できる !!! 必ずご⾃⾝の環境でも確認してください !!!