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
850
OSS コミットゴルフのすすめ / Let's play OSS-contribute-golf
megane42
0
150
ゆる計算理論ラジオ / P vs NP for beginner
megane42
1
300
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
300
Other Decks in Programming
See All in Programming
速習iPhone Duo対応
yuukiw00w
2
980
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
170
SREの越境 / SRE Collaboration
y0hgi
2
300
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
270
CodeRabbitの効果検証と過ごしてみた3ヶ月
armondando
0
230
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
150
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
140
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.6k
コードで差別化できない時代の ソフトウェアエンジニアリング / Software Engineering in an Era Where Code Doesn’t Differentiate
kishida
2
160
JRuby: Past, Present, and Future
headius
0
230
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
34
16k
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
970
Featured
See All Featured
Ethics towards AI in product and experience design
skipperchong
2
420
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.3k
Done Done
chrislema
187
17k
Statistics for Hackers
jakevdp
799
230k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
350
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Navigating Team Friction
lara
192
16k
Accessibility Awareness
sabderemane
1
240
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
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 万まで」のように絞りながら⼩刻み に実⾏していくことで、影響を最⼩化できる !!! 必ずご⾃⾝の環境でも確認してください !!!