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
バッチ処理をパイプラインパターンで上手くやる
Search
yusuke misawa
September 17, 2019
Programming
2
1.3k
バッチ処理をパイプラインパターンで上手くやる
yusuke misawa
September 17, 2019
Tweet
Share
More Decks by yusuke misawa
See All by yusuke misawa
KYCとは何なのか
yusukemisa
0
430
Other Decks in Programming
See All in Programming
技術的負債と向き合うカイゼン活動を1年続けて分かった "持続可能" なプロダクト開発
yuichiro_serita
0
290
traP の部内 ISUCON とそれを支えるポータル / PISCON Portal
ikura_hamu
0
180
AWSのLambdaで PHPを動かす選択肢
rinchoku
2
390
非ブラウザランタイムとWeb標準 / Non-Browser Runtimes and Web Standards
petamoriken
0
430
はてなにおけるfujiwara-wareの活用やecspressoのCI/CD構成 / Fujiwara Tech Conference 2025
cohalz
1
2.3k
KubeCon NA 2024の全DB関連セッションを紹介
nnaka2992
0
120
ecspresso, ecschedule, lambroll を PipeCDプラグインとして動かしてみた (プロトタイプ) / Running ecspresso, ecschedule, and lambroll as PipeCD Plugins (prototype)
tkikuc
2
1.5k
선언형 UI에서의 상태관리
l2hyunwoo
0
270
VisionProで部屋の明るさを反映させるシェーダーを作った話
segur
0
100
Итераторы в Go 1.23: зачем они нужны, как использовать, и насколько они быстрые?
lamodatech
0
1.3k
20年もののレガシープロダクトに 0からPHPStanを入れるまで / phpcon2024
hirobe1999
0
1k
PHPで学ぶプログラミングの教訓 / Lessons in Programming Learned through PHP
nrslib
4
1.1k
Featured
See All Featured
A Tale of Four Properties
chriscoyier
157
23k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
26
1.9k
Music & Morning Musume
bryan
46
6.3k
Navigating Team Friction
lara
183
15k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
GraphQLとの向き合い方2022年版
quramy
44
13k
VelocityConf: Rendering Performance Case Studies
addyosmani
327
24k
Building an army of robots
kneath
302
45k
Learning to Love Humans: Emotional Interface Design
aarron
274
40k
Transcript
バッチ処理をパイプライン パターンで上手くやる Yusuke Misawa MediaDo.go@2019/09/17
Yusuke Misawa(三澤 悠介) • 決済系の自社サービスの会社 バックエンドエンジニア • 仕事でGoを使うようになって1年 • Goで個人情報周りの
マイクロサービス開発 • 趣味:ボルダリング♂ • Twitter: https://twitter.com/FpmpAmpm
話の流れ • なにかとバッチ処理を作る機会があるという話 • バッチ処理作成時の課題がいろいろあるよねという話 • パイプラインパターンでうまくやろうという話 ◦ パイプラインとはステージと呼ばれるデータ操作の組み合わせのパターン ◦
ステージをうまくやるためのパターン(ファンアウト・ファンイン・キャンセル) • パイプラインを使えばうまく作れそうだが残る課題として ◦ ゴルーチンがリークしないようにする話 ◦ チャネルのブロックと panicを防ぐ話 • まとめ
なにかとバッチ処理を作る機会がある • ユーザーデータの不整合を解消したい • 非同期で動くイベント送信が失敗したので再送したい (なお自動でリカバリーする仕組みはない) • ユーザーが個人情報の削除を要請してきた • 追加機能開発で増えたDBカラムの既存レコード分のデータ作成
• とにかくいろいろある
なにかとバッチ処理を作る機会がある • 組織・チーム体制にもよるけど前職だと関わっていたサービスの規模が大きく専業 のバッチチームやインフラチームがあった。 • なので個人的には最近まであまりバッチ処理を作る機会はあまりなかった • マイクロサービス構成だと各サービスの開発者がなんとかするしかない。
バッチ処理作成時の課題 • 処理量が多いので並行処理しようとするが複雑化 • 動きそうだがオレオレ実装で可読性が悪くレビューもし辛い • できれば再利用・拡張性あるものにしたい • ゴルーチンがうまく扱えているか不安 •
チャネルがうまく扱えているか不安 →コードの見通しが良く拡張性があり 安全でいい感じの実装方式があれば真似したい
「Goで並行処理するときにはパ ターンがあったな・・・」
並行処理のパターンに関する情報源 • The Go Blog:Go Concurrency Patterns: Pipelines and cancellation
◦ https://blog.golang.org/pipelines • 書籍「Go言語による並行処理」 ◦ オライリージャパン ◦ Katherine Cox-Buday 著、山口 能迪 訳 ◦ 原書: Concurrency in Go ◦ https://www.oreilly.co.jp/books/9784873118468/
• 入力を受け取り何らかの一連の操作(ステージと呼ぶ)を行い出力する この組み合わせをパイプラインと呼ぶ(Go特有の概念ではない) パイプラインと呼ばれる考え方 ステージ1 ステージ2 ステージ3 入力 出力
• Goで実装する場合ステージ間はチャネルでデータを共有する パイプラインと呼ばれる考え方 ステージ1 ステージ2 ステージ3 入力 出力 データを渡すチャネルを作りステージ 2に
渡し、入力をチャネルに流す データ受信チャネルを引数に持ち、そこ から入力を受信後データ操作を行う
• 各ステージは独立して修正や入れ替えが可能 ステージをファンアウト・ファンインにすることで 並行処理とその結果の集約もできる。 コードの見通しはよくなりそう。 パイプラインと呼ばれる考え方 ステージ1 ステージ2 ステージ3 入力
出力
• ステージ1で出力されたデータを複数のステージ2で処理 複数のゴルーチンでステージ2を行う ファンアウト ステージ1 ステージ2 ステージ3 入力 出力
• ステージ2で別々に出力されたデータをステージ3で集約 ファンインで結果の集約 ステージ1 ステージ2 ステージ3 入力 出力
パイプライン全体の擬似コード // ステージ1(ctx以下は入力) in := gen(ctx, 1,2,3,4,5) //genは受信専用チャネルinを返す // ステージ2
(ファンアウト) c1 := sq(ctx,in) // 何らかの操作を並行実行 c2 := sq(ctx,in) // c1,c2は実行結果の受信専用チャネル // ステージ3(ファンイン) for n := range merge(ctx,c1, c2) { // mergeもc1,c2をまとめた受信専用チャネルを返す fmt.Println(n) }
ファンインで複数のチャネルをまとめる 引数のチャネル個数分それぞれデータを取り出し返却済のチャネル に送信する データ集約先のチャネルは先に返却
バッチ処理作成時の課題(再掲) • 処理量が多いので並行処理しようとするが複雑化 ◦ ◎パイプラインで見通し良さそう • 動きそうだがオレオレ実装で可読性が悪くレビューもし辛い ◦ ◎パイプラインの考え方が共有されていれば話が早そうだしコードの見通し良さそう。 •
できれば再利用・拡張性あるものにしたい ◦ ◎パイプラインで拡張性良さそう • ゴルーチンがうまく扱えているか不安 ◦ パイプラインのステージ内の実装による • チャネルがうまく扱えているか不安 ◦ パイプラインのステージ内の実装による
ゴルーチンのリークが起きないようにしたい • リーク(=漏れ) ◦ 情報のリーク:関係者がマスコミに秘密を漏らしちゃう • ゴルーチンのリーク ◦ 非メインのゴルーチンが終了せず残り続けること。 ◦
ガベージコレクションで回収されずその分メモリを圧迫する。 ◦ バッチ処理の場合どこかでプロセスが終わるので永遠に増え続けることはな いが意図しないリークは避けたい。
ゴルーチンのリーク例 https://play.golang.org/p/_YJcrlApJRq
コンテキストキャンセルで解決 https://play.golang.org/p/U18DkwkN0SO メインゴルーチンのdefer cancel()が伝播してctx.Doneがcloseされこ ちらのゴルーチンも終了する。
パイプラインでチャネルをうまく扱う • panicが起きる条件を知る ◦ closedチャネルへの送信(https://play.golang.org/p/puTTlBWgSTh) ◦ closedチャネルのclose ◦ nilチャネルのclose •
意図せずブロックしないようにする ◦ nilチャネルの読み書きをしない(https://play.golang.org/p/jCnURn3qaiU ) ◦ 所有権(誰が初期化するか、送受信の制限)を決める
チャネルのpanicを回避するパターン defer でcloseして二重にcloseを防ぎ、初期化 スコープでやることで nilチャネルのcloseを防ぐ 初期化したスコープでのみ送信する チャネルは初期化して受信専用で返却する(外 部で勝手に書き込ませない)
チャネルのブロックを回避するパターン • 作成者(ジェネレーター) ◦ パイプラインに流すデータのチャネルの作成、初期化し返却 ◦ 並行してデータ送信、チャネルのclose • 消費者(コンシューマー) ◦
ジェネレーターから受け取ったチャネルからデータ受信 ◦ もちろん必要な業務上の処理をやる
バッチ処理作成時の課題(再々掲) • 処理量が多いので並行処理しようとするが複雑化 ◦ ◎パイプラインで見通し良さそう • 動きそうだがオレオレ実装で可読性が悪くレビューもし辛い ◦ ◎パイプラインの考え方が共有されていれば話が早そうだしコードの見通し良さそう。 •
できれば再利用・拡張性あるものにしたい ◦ ◎パイプラインで拡張性良さそう • ゴルーチンがうまく扱えているか不安 ◦ ◎コンテキストで処理終了を伝えゴルーチンのリークを回避 • チャネルがうまく扱えているか不安 ◦ ◎チャネルの作成者と消費者で役割分担し不要なブロックや panicを回避
パイプラインまとめ ステージ1 ステージ2 ステージ3 入力 出力 • パイプラインは入力を受け取りステージと呼ばれる独立したデータ操作の組み合わせるパターン • ステージの組み合わせで柔軟に処理の分割、並行実行が可能になる。
◦ ステージ同士はチャネルでデータを受け渡しする • ステージ(個々のデータ操作を上手くやる)のためのパターン ◦ ファンアウト・ファンイン(ステージ同士の連携のためのパターン) ◦ コンテキストキャンセル(ステージを安全に終了するためのパターン) ◦ ジェネレーターとコンシューマー(ステージを安全に終了するための役割分担のパターン)
最後に • バッチ処理作成時に課題と思ったいくつかのトピックは解決できた。 しかし並行処理の沼はまだまだ深い。 ◦ 競合状態 ◦ ロック ◦ エラー処理
◦ ゴルーチンのスケジューリング ◦ 並行処理の速度・律速問題 etc… • 今ここで話すために並行処理の勉強をしたけどまだ氷山の一角に過ぎないと感じ た。これからも備えていきたい。 • 懇親会では是非声をかけてください。