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
Monixと常駐プログラムの勘どころ
Search
Ishikawa Ryuto
December 13, 2024
Programming
940
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Monixと常駐プログラムの勘どころ
【オフライン】Scalaわいわい勉強会 #4【東京】
https://scala-tokyo.connpass.com/event/335477/
Ishikawa Ryuto
December 13, 2024
More Decks by Ishikawa Ryuto
See All by Ishikawa Ryuto
Scalaが支える4RAPの認証認可基盤
stoneream
0
85
Other Decks in Programming
See All in Programming
1B+ /day規模のログを管理する技術
broadleaf
0
140
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
220
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
330
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
640
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
0
210
AIエージェントで 変わるAndroid開発環境
takahirom
2
660
SREの積み重ねがAI駆動開発のガードレールになった ― 7つの実践/SRE Guardrails The 7
tomoyakitaura
8
4.3k
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
2
490
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
380
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
270
自作OSでスライド発表する
uyuki234
1
3.8k
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
200
Featured
See All Featured
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
150
Designing for Performance
lara
611
70k
Un-Boring Meetings
codingconduct
0
350
30 Presentation Tips
portentint
PRO
1
350
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
250
New Earth Scene 8
popppiees
3
2.4k
Context Engineering - Making Every Token Count
addyosmani
9
1k
Building the Perfect Custom Keyboard
takai
2
810
It's Worth the Effort
3n
188
29k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.6k
Transcript
Monixと 常駐プログラムの 勘どころ 2024/12/13 Ishikawa Ryuto
自己紹介 石川 龍斗 (Ishikawa Ryuto) @stoneream 株式会社FOLIO (2023~) バックエンドエンジニアとしてScalaを書いている Scala歴は5年目くらい
「常駐プログラム」って何? 文字通り「一度起動したらそのまま立ち上げておくプログラム 」 背景として... 「Spotify アーティストの新曲を巡回・通知くん」を作った 主な機能 • フォロー中アーティストの取り込み (バッチ)
• アーティストのリリースを巡回 (毎日) • Discordへの通知 (随時) 以降、「巡回くん」
今日のお話 Monixの書き心地が良かった • エラーハンドリング • リトライ処理 • 処理の並列実行 常駐プログラムが常駐してもらうためのの勘どころ •
設計として考慮しておくと良さそうなこと • 実装としてやっておくと良かったこと 巡回くんでの実例を交えて紹介! 何度か作り直している ... 「こうすれば良かった ...」の話
簡単にMonixについて
val t1 = Task[Int] { 10 } val t2 =
Task[Int] { 20 } // Task同士の合成が可能 val combinedTask = for { t1Result <- t1 t2Result <- t2 } yield t1Result + t2Result // 30 combinedTask.runToFuture.foreach(println) // 並列で実行が可能 val tasks = Task.parSequence(Seq(t1, t2)) // 10 // 20 tasks.runToFuture.foreach(println) 処理のかたまりを “Task” という単位で扱い抽象化
val t1 = Task[Int] { 10 } val t2 =
Task[Int] { 20 } // Task同士の合成が可能 val combinedTask = for { t1Result <- t1 t2Result <- t2 } yield t1Result + t2Result // 30 combinedTask.runToFuture.foreach(println) // 並列で実行が可能 val tasks = Task.parSequence(Seq(t1, t2)) // 10 // 20 tasks.runToFuture.foreach(println) Task は遅延評価
val t1 = Task[Int] { 10 } val t2 =
Task[Int] { 20 } // Task同士の合成が可能 val combinedTask = for { t1Result <- t1 t2Result <- t2 } yield t1Result + t2Result // 30 combinedTask.runToFuture.foreach(println) // 並列で実行が可能 val tasks = Task.parSequence(Seq(t1, t2)) // 10 // 20 tasks.runToFuture.foreach(println) 非同期に実行することも
簡単に Monix について 処理のかたまりを Task という単位で扱い抽象化 非同期処理をシンプルに書くための機能を提供してくれる リトライやエラーハンドリングのための機能が充実している点も魅力 (後述) Cats
Effect の一部機能は Monix の影響を受けている、らしい >> cats.effect.IOとmonix.eval.Taskはとてもよく似ていて、しかも私は両方の開発に携わることになった ので、Taskで決めた設計は結局IOでも採用することになった。 とはいえ、IOはシンプルで信頼性の高い、 純粋なリファレンス実装になるように設計されている。 (Monix vs Cats-Effect より deepL翻訳)
勘所 1
エラーは基本的には自動回復 当たり前ですが...。 考慮する観点は以下の2つ? エラーの種類 • リトライで問題ない • 自動復旧できない ◦ 想定内のエラー?
◦ 想定外のエラー? リトライの種類 • 即座 • n秒後 • しない リトライ回数の制限 処理全体のタイムアウト なども プロダクション環境下では 復旧のための参考情報になる
Monixにおけるエラーハンドリングとリトライ Taskで発生した例外は自動的に捕捉してくれる! 捕まえたエラーを良い感じに扱うメソッドがいくつか生えている ・Task#onErrorHandleWith[B >: A](f: (Throwable) => Task[B]): Task[B]
例外の型や内容から別のTaskを作成する ・Task#onErrorRestartIf(p: (Throwable) => Boolean): Task[A] 例外の型や内容が特定の条件にマッチした場合にリトライする・しない ・Task#onErrorRestart(maxRetries: Long): Task[A] エラー発生時に任意の回数まではリトライする
巡回くんの例
def retryWhenTooManyRequests[A](task: Task[A], maxRetries: Int): Task[A] = { task.onErrorHandleWith {
case e: TooManyRequestsException => if (maxRetries > 0) { val retryAfter = e.getRetryAfter retryWhenTooManyRequests(task, maxRetries - 1) .delayExecution(retryAfter.seconds) } else { Task.raiseError(e) } case e => Task.raiseError(e) } } レートリミットに引っかかった
def retryWhenTooManyRequests[A](task: Task[A], maxRetries: Int): Task[A] = { task.onErrorHandleWith {
case e: TooManyRequestsException => if (maxRetries > 0) { val retryAfter = e.getRetryAfter retryWhenTooManyRequests(task, maxRetries - 1) .delayExecution(retryAfter.seconds) } else { Task.raiseError(e) } case e => Task.raiseError(e) } } 例外情報に含まれる情報を 参考にリトライを試みる
def retryWhenTooManyRequests[A](task: Task[A], maxRetries: Int): Task[A] = { task.onErrorHandleWith {
case e: TooManyRequestsException => if (maxRetries > 0) { val retryAfter = e.getRetryAfter retryWhenTooManyRequests(task, maxRetries - 1) .delayExecution(retryAfter.seconds) } else { Task.raiseError(e) } case e => Task.raiseError(e) } } それ以外の例外は諦める 何回やっても駄目なときは諦める
勘所 2
処理は途中で落ちる、絶対に落ちる イチからやり直しはしたくない • メモリが足りなくなった • APIのレートリミットに引っかかった • 間違えてKillした ほしい機能 •
途中から処理を再開できるようにする • 不必要な処理の再実行の抑制する 全部 実際に起きた
巡回くんの例 「新着リリース巡回」機能の場合... 1. 巡回スケジュールを作成し、データベースに処理待ちとして積む 2. 処理待ちから数件ずつ取って処理する 設計する際は... 各タイミングでの時刻も保持することもポイント! • 処理を積んだタイミング
• 処理の開始 • 処理の正常終了・異常終了 処理が始まっていない! 終わっていない!など エラー検知に役立ちます
ありがとうございました
リンク集 Monix https://github.com/monix/monix Monix vs Cats-Effect https://monix.io/blog/2018/03/20/monix-vs-cats-effect.html softwaremill/retry https://github.com/softwaremill/retry Scala
Advent Calendar 2024 https://qiita.com/advent-calendar/2024/scala FOLIO Advent Calendar 2024 https://adventar.org/calendars/10315 こちらもぜひご覧ください m(_ _)m これもおすすめ