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
Monixと常駐プログラムの勘どころ
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Ishikawa Ryuto
December 13, 2024
Programming
990
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
150
Other Decks in Programming
See All in Programming
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
140
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
Domain-Driven Transformation
hschwentner
2
2.3k
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
280
コードで差別化できない時代の ソフトウェアエンジニアリング / Software Engineering in an Era Where Code Doesn’t Differentiate
kishida
2
160
phperだってパンチカードでコーディングしたい
o0h
PRO
0
270
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
5
5.1k
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
330
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
960
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.8k
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
3k
Featured
See All Featured
Side Projects
sachag
456
43k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
860
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
300
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
1
620
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.6k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
The Spectacular Lies of Maps
axbom
PRO
1
1.1k
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 これもおすすめ