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
新卒一年目でサーバーサイド開発あるあるを踏み抜いてきた話 / Developers Boost...
Search
Kohei Yamato
November 30, 2019
Programming
4
1.9k
新卒一年目でサーバーサイド開発あるあるを踏み抜いてきた話 / Developers Boost 2019 A-7
Developers Boost 2019で登壇したスライドです。
https://event.shoeisha.jp/devboost/20191130/session/2230/
Kohei Yamato
November 30, 2019
Tweet
Share
More Decks by Kohei Yamato
See All by Kohei Yamato
コードで見るKotlin 1.3
daiwahome
1
2.4k
Other Decks in Programming
See All in Programming
Zendeskのチケットを Amazon Bedrockで 解析した
ryokosuge
3
310
@Environment(\.keyPath)那么好我不允许你们不知道! / atEnvironment keyPath is so good and you should know it!
lovee
0
120
Android 16 × Jetpack Composeで縦書きテキストエディタを作ろう / Vertical Text Editor with Compose on Android 16
cc4966
2
260
Azure SRE Agentで運用は楽になるのか?
kkamegawa
0
2.4k
Reading Rails 1.0 Source Code
okuramasafumi
0
250
Navigation 2 を 3 に移行する(予定)ためにやったこと
yokomii
0
330
今だからこそ入門する Server-Sent Events (SSE)
nearme_tech
PRO
3
240
テストカバレッジ100%を10年続けて得られた学びと品質
mottyzzz
2
610
はじめてのMaterial3 Expressive
ym223
2
880
「待たせ上手」なスケルトンスクリーン、 そのUXの裏側
teamlab
PRO
0
560
Design Foundational Data Engineering Observability
sucitw
3
200
スケールする組織の実現に向けた インナーソース育成術 - ISGT2025
teamlab
PRO
1
130
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
188
55k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
580
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
252
21k
Optimizing for Happiness
mojombo
379
70k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
50k
Build your cross-platform service in a week with App Engine
jlugia
231
18k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.9k
Making Projects Easy
brettharned
117
6.4k
A Tale of Four Properties
chriscoyier
160
23k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
13k
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