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.8k
新卒一年目でサーバーサイド開発あるあるを踏み抜いてきた話 / 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.3k
Other Decks in Programming
See All in Programming
GitHub Actions × RAGでコードレビューの検証の結果
sho_000
0
260
仕様変更に耐えるための"今の"DRY原則を考える / Rethinking the "Don't repeat yourself" for resilience to specification changes
mkmk884
0
190
負債になりにくいCSSをデザイナとつくるには?
fsubal
10
2.4k
Spring gRPC について / About Spring gRPC
mackey0225
0
220
CI改善もDatadogとともに
taumu
0
120
GoとPHPのインターフェイスの違い
shimabox
2
190
パスキーのすべて ── 導入・UX設計・実装の紹介 / 20250213 パスキー開発者の集い
kuralab
3
790
メンテが命: PHPフレームワークのコンテナ化とアップグレード戦略
shunta27
0
120
PHPカンファレンス名古屋2025 タスク分解の試行錯誤〜レビュー負荷を下げるために〜
soichi
1
200
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
160
Honoのおもしろいミドルウェアをみてみよう
yusukebe
1
210
2,500万ユーザーを支えるSREチームの6年間のスクラムのカイゼン
honmarkhunt
6
5.3k
Featured
See All Featured
Being A Developer After 40
akosma
89
590k
The Power of CSS Pseudo Elements
geoffreycrofte
75
5.5k
GraphQLの誤解/rethinking-graphql
sonatard
68
10k
Bash Introduction
62gerente
611
210k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
Done Done
chrislema
182
16k
Large-scale JavaScript Application Architecture
addyosmani
511
110k
A better future with KSS
kneath
238
17k
A designer walks into a library…
pauljervisheath
205
24k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
27
1.9k
For a Future-Friendly Web
brad_frost
176
9.5k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.7k
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