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
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
We Are The Robots
honzajavorek
0
380
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Designing for Timeless Needs
cassininazir
1
480
HDC tutorial
michielstock
2
870
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
5
690
KATA
mclloyd
PRO
35
15k
Facilitating Awesome Meetings
lara
57
7.1k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
430
Designing for Performance
lara
611
70k
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