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
新卒一年目でサーバーサイド開発あるあるを踏み抜いてきた話 / Developers Boost...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Kohei Yamato
November 30, 2019
Programming
2k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
新卒一年目でサーバーサイド開発あるあるを踏み抜いてきた話 / Developers Boost 2019 A-7
Developers Boost 2019で登壇したスライドです。
https://event.shoeisha.jp/devboost/20191130/session/2230/
Kohei Yamato
November 30, 2019
More Decks by Kohei Yamato
See All by Kohei Yamato
コードで見るKotlin 1.3
daiwahome
1
2.6k
Other Decks in Programming
See All in Programming
Next.js: The final boss of Web Performance
guaca
0
380
Spec-Driven Development - Why Vaadin Is the Perfect Fit
simas
PRO
0
240
Spec-driven Development with AI
simas
PRO
0
2.3k
Web3Dライブラリを作ってVue Custom Rendererの仕組みを理解しよう / lets learn vue custom renderer by making 3d library
drumath2237
1
130
GraphQL の onError と Nullability のこれから
quramy
0
210
AI時代のシニアエンジニアとは
yosuke_furukawa
PRO
15
11k
Python Lambdaにおけるテストしやすい設計とAI支援の活用 / Designing Testable Python Lambda Functions with AI
kitano_yuichi
1
300
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
360
Будущее локальных LLM – Алексей Голобурдин, PythoNN
sobolevn
0
110
Lambda durable functionsでHuman in the Loopを楽に…楽に……
har1101
2
290
社内AIエージェント「Askワンバン」における コストチューニングの取り組み
t0yohei
0
160
AmebaブログはKMPから卒業します
kmars0605
0
330
Featured
See All Featured
The Cult of Friendly URLs
andyhume
79
7.1k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.7k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Writing Fast Ruby
sferik
630
63k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.4k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
12k
Designing Experiences People Love
moore
143
24k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
Music & Morning Musume
bryan
48
7.5k
Transcript
新卒⼀年⽬でサーバーサイド開発あるあるを 踏み抜いてきた話 Developers Boost 2019 #devboostA (A-7) エムスリー株式会社 ⼤和康平 #devboostA
1
おことわり サーバサイドあるあるの感じ⽅には個⼈差があります 現在は開発フローも⾒直されており、技術的負債とならないように改善を続けて います #devboostA 2
⾃⼰紹介 ⼤和 康平 Twitter: daiwahome0 GitHub: daiwahome マルチデバイスチーム エンジニア アプリ開発チームのサーバーサイドをメインに開発
Java, Kotlin, Terraform, CloudFormation (SAM) 等 AWS, オンプレでの開発がメイン #devboostA 3
はなすこと 昨年1年間で遭遇したサーバーサイド開発中に起こった問題と、 どのように解決してきたかについて事例ごとに紹介します。 (⼀緒に笑い⾶ばしていきましょう) #devboostA 4
今回紹介する事例 1. ライブラリを追加したら◯◯が2回呼ばれるようになった 2. 単体テストのCoverageが5%台のシステム 3. スケールしない並列処理システム #devboostA 5
ちなみに 今回お話する事例は、全て同じシステムの開発中に遭遇した内容です。 #devboostA 6
事例1. ライブラリを追加したら◯◯が2回呼ばれるように なった #devboostA 7
あらまし Spring Boot (Java + Kotlin) のWebアプリケーションに、Gradleでライブラリの依 存関係を追加しようとした。 (build.gradle) implementation
'io.opencensus:opencensus-api:0.16.0' implementation 'io.opencensus:opencensus-exporter-trace-stackdriver:0.16.0' runtimeOnly 'io.opencensus:opencensus-impl:0.16.0' → 起動時に例外を吐いて落ちるようになってしまった... 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 8
追加しようとしていたライブラリ 並列処理を可視化するために、トレーシングツールのStackdriver Traceを導⼊しよ うとしていた。 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 9
スローされている例外 Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) Caused by: java.lang.IllegalStateException: Stackdriver exporter is already registered. Stackdriver exporter is already registered. → すでに初期化されている??? 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 10
Breakpointを置いて動作を追ってみる 1. main関数が実⾏される 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 11
Breakpointを置いて動作を追ってみる 2. 例外が発⽣していた⾏で停⽌する 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 12
Breakpointを置いて動作を追ってみる 3. main関数が実⾏される (!?) 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 13
Breakpointを置いて動作を追ってみる 4. 例外がスローされる 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 14
起こっていたこと main関数が2回実⾏されていた 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 15
原因 ⾃動リロードなどを⾏う開発⽤ツールである spring-boot-devtools がmain関数 を2回実⾏していた。 (RestartLauncher.java:47-49) Class<?> mainClass = getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); mainMethod.invoke(null, new Object[] { this.args }); その結果、Stackdriver Trace⽤のライブラリが初期化エラーとなった。 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 16
対応 初回のみ初期化処理が実⾏されるように修正した。 val isStarted = AtomicBoolean(false) // すでに実⾏されている場合はスキップ if (!isStarted.compareAndSet(false,
true)) { return } // 初期化処理 init() 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA 17
事例1のまとめ spring-boot-devtools によりmain関数が2回実⾏されていたが、 初回のみ初期化処理を⾏うようにした。 教訓: 起動の仕組みに⼿が⼊るようなツールを使⽤する場合には注意しましょう ログを⼿がかりにして、Breakpointを使⽤して原因を特定するとよいです 事例1. ライブラリを追加したら◯◯が2回呼ばれるようになった #devboostA
18
事例2. 単体テストのCoverageが5%台のシステム #devboostA 19
あらまし 歴史的経緯で、単体テストのCoverageが5%程度しかないシステムが本番稼働して いた。動作の正しさは、⼿作業のQA (Quality Assurance, 品質保証) で担保してい た。 システムについてのドキュメントもほとんどなく、何が正しい動作なのか判断できな い状況だった
(加えて開発者は退職済み)。 事例2. 単体テストのCoverageが5%台のシステム #devboostA 20
Coverageが5%台 事例2. 単体テストのCoverageが5%台のシステム #devboostA 21
Coverageが5%台 事例2. 単体テストのCoverageが5%台のシステム #devboostA 22
つらい点 単体テストのCoverageが低いことで、次のような問題が存在した。 機能開発のコストが⾼い 変更した部分以外へ影響がないことを担保できない ⼿動でテストしてレビューに出す必要がある 事例2. 単体テストのCoverageが5%台のシステム #devboostA 23
直⾯した問題 DBアクセスが遅い 運⽤期間が延びたことでDBに保存されているデータが増え、DBへのアクセスがボト ルネックになってきた。原因の⼀つは、必要以上の正規化をしていて、JOINのコス トが無視できない程になっていたことだった。 そのためチームのメンバと相談し、DBの⾮正規化を⾏うことにした。 事例2. 単体テストのCoverageが5%台のシステム #devboostA 24
直⾯した問題 DBの⾮正規化では、動作が変わらないことを保証しなければならない。 単体テストがない中、次の状況を踏まえて対応する必要があった。 Coverageをあげようとすると相応のコストがかかる Javaのソースコード298ファイル (約1.4万⾏) 開発者としてアサインできるのは1⽇あたり1⼈ 既に本番環境に影響が出ている 事例2. 単体テストのCoverageが5%台のシステム
#devboostA 25
対応 範囲を絞って単体テストを追加した 単体テストなしに開発することは避けたいため、コード上で影響が出そうな範囲を絞 り込み単体テストを追加した (OR Mapper部分 + ⼀部のロジック)。 単体テストを先に作成したことにより修正漏れに気づくことができ、開発スピードを 上げることができた。最終的には、⾮正規化したシステムをリリースすることがで
き、動作速度を改善することができた。 事例2. 単体テストのCoverageが5%台のシステム #devboostA 26
事例2のまとめ 動作が変わらない部分の単体テストを先に埋めることで、⾮正規化後の動作を保証し ながら問題に対応できた。 教訓: 単体テストは書くようにしましょう 初めは⼯数がかかるかもしれませんが、あとで運⽤が楽になります 単体テストだけでなく、ドキュメントも整備しましょう 事例2. 単体テストのCoverageが5%台のシステム #devboostA
27
事例3. スケールしない並列処理システム #devboostA 28
あらまし Android, iOSやWebブラウザ向けのプッシュ通知を管理するシステムが本番環境で 稼働しているが、問題を抱えていた。 システムの要件 (⼀部): 決まった時刻に送信可能な全ユーザにプッシュ通知を送る ⼀度の配信で最⼤で40万ユーザ宛てに10分で送信完了する 事例3. スケールしない並列処理システム
#devboostA 29
システム構成図 事例3. スケールしない並列処理システム #devboostA 30
抱えていた問題 要件を満たせていない プッシュ通知配信サーバにアクセスするworkerを複数台構成にしているが改 善しない 事例3. スケールしない並列処理システム #devboostA 31
抱えていた問題 要件を満たせていない 「⼀度の配信で最⼤で40万ユーザ宛てに10分で送信完了」すべき。 → 実際には30分以上かかっている → 配信が多い時間帯だと、最初の配信が遅延して、その影響で次の配信も遅延し て... (以下繰り返し) 事例3.
スケールしない並列処理システム #devboostA 32
抱えていた問題 プッシュ通知配信サーバにアクセスするworkerを複数台構成にしているが改 善しない SQSを介して並列処理しているworkerを増やしても、スループットを改善できなか った。 事例3. スケールしない並列処理システム #devboostA 33
Stackdriver Traceの導⼊ AWS CloudWatch Logsにworkerのログを収集して原因を調査していたが、明確に 遅い処理は発⾒できず。 並列処理の状況について調査しようとするも、複数workerによる並列処理に加えて ⼀つのworker内でも並列処理している関係上、全体を追うのはつらい状況だった。 → Stackdriver
Traceの導⼊を⾏った 事例3. スケールしない並列処理システム #devboostA 34
導⼊した結果...... 並列動作していないように⾒える 事例3. スケールしない並列処理システム #devboostA 35
原因調査 SQSからメッセージを並列で受信できていない可能性があるため、 workerの処理の開始と終了時刻をログに追加して動作を検証した。 その結果、ログからもworkerの数を増やしても直列でメッセージを処理しているこ とを確認した。 SQSから並列で受信できていないことが判明したため、SQSの設定とAWS公式ドキ ュメントを⼀から⾒直した。 事例3. スケールしない並列処理システム #devboostA
36
改善しない原因 SQSのパラメータの設定ミスにより、複数台のworkerが排他的に動作していたのが 改善しない原因だった。 事例3. スケールしない並列処理システム #devboostA 37
改善しない原因 すべてのmessageで共通のグループIDを使⽤していたため、処理が終わるまで次の messageを処理できないようになっていた。 https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloper Guide/using-messagegroupid-property.html 同じメッセージグループに属するメッセージは、常にメッセージグループに対 して厳密な順序で1つずつ処理されます 事例3. スケールしない並列処理システム #devboostA
38
設定の修正 設定ミスが判明したため、並列でmessageを受信できるように修正する。 並列に受信するためには、messageごとに別のグループIDを付与すればよいため、 重複の可能性がほとんどないUUIDを設定した。 事例3. スケールしない並列処理システム #devboostA 39
修正前後のTrace結果 修正後 (右図) は並列処理されていることを⽰している。 修正前
修正後 事例3. スケールしない並列処理システム #devboostA 40
事例3のまとめ 複数のworkerが休まずに仕事をするようになったのもあり、「⼀度の配信で最⼤で 40万ユーザ宛てに10分で送信完了」の要件を満たすことができた。 教訓: ドキュメントをしっかり読もう 動作確認をきちんと⾏おう 並列処理の確認は難しいため必要に応じて可視化ツールを導⼊しましょう 事例3. スケールしない並列処理システム #devboostA
41
おわりに サーバサイド開発を通してハマってしまった問題について、事例を元に紹介しま した Breakpointで調査した例 単体テストを追加してDBを⾮正規化した例 Stackdriver Traceを導⼊した例 発⽣した問題の対応と、そこからの教訓をまとめました このスライドがみなさんの役に⽴つと幸いです #devboostA
42
ご静聴ありがとうございました #devboostA 43