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
updated_at に依存したら大変なことになった / Don't depend on up...
Search
megane42
November 28, 2019
Programming
640
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
ソフトウェアラスタライザ
fadis
1
790
Swift愛好会と私(ウホーイ) / Swift Fan Club and Uhooi
uhooi
0
140
kubernetes コンポーネント開発入門 / 新卒N年目の勉強会&交流会!〜〇〇への誘い〜 #n_study
mazrean
0
170
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
210
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
0
240
まずはプロンプトガイドを読もう、話はそれからだ
kiakiraki
1
260
スマート反転とウェブアクセシビリティ
camiha
0
130
『寄り添うラジオ』をAIで作る 体験価値から逆算した、会話しないUXと品質設計
theoriatec2024
3
120
思考垂れ流し開発 ~音声入力 × AIエージェント × 開発ハーネスによる試行錯誤~
npostring
0
880
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
0
530
初めての模倣学習とVLA
natsutan
0
490
高専キャリア LT 発表内容
crysta1221
6
5.6k
Featured
See All Featured
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Bash Introduction
62gerente
615
220k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
370
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.5k
WENDY [Excerpt]
tessaabrams
12
39k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
What's in a price? How to price your products and services
michaelherold
247
13k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.2k
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 万まで」のように絞りながら⼩刻み に実⾏していくことで、影響を最⼩化できる !!! 必ずご⾃⾝の環境でも確認してください !!!