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
ZIOでサクッとFunctionalにETL
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
wakye5815
September 06, 2024
2.5k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ZIOでサクッとFunctionalにETL
wakye5815
September 06, 2024
More Decks by wakye5815
See All by wakye5815
LintのみでAIに開発スタイルを叩き込めるのか?
wakye5815
4
4.6k
sbt-assemblyにハマってDB接続できず時間が溶けた話
wakye5815
1
1.9k
Featured
See All Featured
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
Game over? The fight for quality and originality in the time of robots
wayneb77
1
280
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.1k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
850
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
540
The Limits of Empathy - UXLibs8
cassininazir
1
670
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
GitHub's CSS Performance
jonrohan
1033
470k
Transcript
ZIOでサクッとFunctionalにETL 2024/09/06 Scalaわいわい勉強会 #3 wakye5815 1
自己紹介 Scalaでバックエンド書いたりEM見習いを始めたり色々やってます 名前:脇田悠介 働いてるところ:株式会社Flinters X :@wakye5815 悩み:ZIOの読み方がZIOかZIOなのかわからない 2
とある日 3
実装依頼はいつも突然に 大量の広告配信データを〜 ちょろっと圧縮して〜 ストレージに出力する〜 使い捨ての〜 スクリプトお願いします! 4
欲しいのは ストリーミング操作が優秀でサクッと書ける 軽量に動かせて 並行処理もいい感じ あわよくばFunctionalな そんなScalaライブラリ 5
そんなScalaライブラリあるわけ.... 6
7
と言うことで今日は軽量ETL by ZIOに ついて話します 8
他ライブラリ候補 9
Akka/Pekko Stream メリット 様々なデータソースを接続できるAlpakka優秀 ストリーミング操作のオペレーターもかなり揃っている 腐っているので情報が多い デメリット ActorSystemが付いてくる チームでは脱Akkaしていたので改めて採用するのも... 10
Spark メリット 分散処理できてスケールする Alpakkaと同じくデータソースサポートが手厚い デメリット クラスタ管理はちょっとしたくない 11
そしてZIO ZIOはEffectライブラリ 誤解を恐れずに表現するとZIO文脈のEffectは R => Either[E,A] であり ZIO[R,E,A] として扱われる ScalaらしくFunctionalに設計されているがモナモナしていないので
とっつきやすい、またドキュメントも十分 並行処理はJVMのvirtual threadをベースとしたFiberを提供してい る 12
Streamingはどうなの? ZIOを元にしたZIO Streamsが提供されている Akka/Pekkoに負けない抽象化され且つ豊富なストリーミング操作 ZIO Streamsはプッシュベースストリームではなくプルベースストリ ームのためバッファリング要らず、バックプレッシャーしなくてOK ただしAlpakkaほどの豊富なデータソースサポートはないので要件 とご相談 13
顧客が本当に求めていたもの 14
結果としてZIOを採用 Httpな広告媒体APIからデータをとるだけなのでデータソースサポ ートが豊富でなくてもOK 使い捨てなのでSparkではオーバーキル チーム背景も含めてAkka/Pekkoはごめん 15
実装していく 16
まずは今回のETLについて Extract 広告媒体APIから取得できる配信データをExtract Transform 取得したデータを一定粒度でgzipに圧縮 Load 外部のシステムが読みにくるGCSにLoad 17
ZIO Streamsで表現するには ワークフロー定義に3つの抽象化されたモジュールを利用する ZStream[Env,Err,Out] ストリームワークフローのデータソースとして機能 ZPipeline[Env, Err, In, Out] 実態は
ZStream[Env,Err,In] => ZStream[Env,Err,Out] ZSink[Env,Err,In,Left,Consumed] ZSink[Env,Err,In,Left,Consumed] => ZIO[Env,Err,Consumed] な ZStream#run に渡して消費する 18
組み合わせると def extractionStream(apiEndpoint: URL): ZStream[Any, Throwable, ApiResponse] def transformationPipeline: ZPipeline[Any,
Throwable, APIResponse, (String, Array[Byte])] def loadingSink: ZSink[Any, IOException, (String, Array[Byte]), Nothing, Unit] object Main extends ZIOAppDefault override def run = val effects = apiEndpoints.map { apiEndpoint => extractionStream(apiEndpoint) >>> transformationPipeline >>> loadingSink } ZIO.collectAllPar(effects).unit 19
Extractの実装 java.util.concurrent.FutureあるいはBlockingなAPI実行をZIO Effectに変換する APIはpagination形式 paginateしつつ変換したZIO EffectをさらにStreamへ変換 20
Extractの実装 def extractionStream(apiEndpoint: URL): : ZStream[Any, Throwable, ApiResponse] = ZStream.paginateZIO(apiEndpoint)
{ apiEndpoint => for response: Task[ApiResponse] <- if(isBlocking(apiEndpoint)) then ZIO.attemptBlocking(callBlockingApi(apiEndpoint)) else ZIO.fromFutureJava(callApi(apiEndpoint)) nextEndpoint = Option(response.nextEndpoint) yield (response, nextEndpoint) } 21
Transformの実装 ZStreamで流れてくるApiResponseをArray[Byte]に変換 ArrayButeをgzip圧縮する ついでにuploadする粒度毎にファイル名をつけておく 22
Transformの実装 def transformationPipeline: ZPipeline[Any, Throwable, APIResponse, (String, Array[Byte])] = val
gzipPipeline = ZPipeline.mapZIO { (response: APIResponse) => ZIO.scoped { for byteArrayOs <- ZIO.fromAutoCloseable(ZIO.attempt(new ByteArrayOutputStream())) gzipOs <- ZIO.fromAutoCloseable(ZIO.attempt(new GZIPOutputStream(byteArrayOs))) _ <- ZIO.attemptBlockingIO(gzipOs.write(response.getRawResponse.getBytes)) yield byteArrayOs }.map(_.toByteArray) } val fileNamePipeline = ZPipeline.mapAccum[Array[Byte], Int, (String, Array[Byte])](1)( (index, data) => (index + 1, (s"data_$index.json.gz", data)) ) gzipPipeline >>> fileNamePipeline 23
Loadの実装 あとはGCSに放り込むだけ 24
Loadの実装 def loadingSink: ZSink[Any, IOException, (String, Array[Byte]), Nothing, Unit] =
ZSink.foreach { (pathWithData: (String, Array[Byte])) => val blobId = BlobId.of(BUCKET_NAME, pathWithData._1) val blobInfo = BlobInfo.newBuilder(blobId).build() ZIO.attemptBlockingIO(storage.create(blobInfo, pathWithData._2)) } 25
ZIO Streams万歳 26
他にも Streamに対するretry stream.retry(Schedule.exponential(1.second)) timeoutしたら他のStreamに切り替える firstStream.timeoutTo(10.seconds)(secondStream) 27
まとめ モナモナせずにFunctionalにETLを組めた ZStream(むしろZIO)の豊富なオペレータでデータ処理のロジックが スマートに Akka/Pekko Streamを使っていた人はZStreamの操作自体は似てい るので移行しやすそう 28
We're hiring 絶賛採用中なのでZIOやりたい人、大規模広告データと戯れたい人い らっしゃいましたらお話ししましょう〜 https://www.wantedly.com/companies/flinters 29