Slide 1

Slide 1 text

protoactor-goで Pregelを作った話 rerorero mercari.go #9 2019/7/17

Slide 2

Slide 2 text

グラフプロセッシング

Slide 3

Slide 3 text

大規模グラフの計算 - 高級なマシンを使うか、MapReduceが主流 - MapReduceはステートレス。グラフプロセッシングでは計算のステップごとの結果 を次のステップに渡す必要があり、これが膨大になるためI/Oが多く発生し効率が 悪い

Slide 4

Slide 4 text

Pregel - Googleが内製した、大規模な有向グラフに対してアルゴリズムを実行するための スケーラブルなフレームワーク - 2010年に同名の論文を発表 - コモディティハードウェアで大規模グラフの計算が効率的に行えるようになった

Slide 5

Slide 5 text

Pregelの仕組み - グラフのノード間でメッセージを送信しあう - ノードはアクティブ・非アクティブの状態を持つ - メッセージを受け取らないなど、特定の条件でノードは非アクティブになる - すべてのノードが非アクティブになったら計算終了 - 特定の計算アルゴリズムに依存しない - Pregelに向かないアルゴリズムもある

Slide 6

Slide 6 text

3 6 1 2 例: 最大値 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 7

Slide 7 text

3 6 1 2 例: 最大値 3 6 6 2 2 1 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 8

Slide 8 text

6 6 6 2 例: 最大値 3 6 6 2 2 1 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 9

Slide 9 text

6 6 6 2 例: 最大値 3 6 6 2 2 1 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ 自分より大きい値 を受け取ったらそ の値で更新 値を更新しなかっ たら非アクティブ

Slide 10

Slide 10 text

6 6 6 2 例: 最大値 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 11

Slide 11 text

6 6 6 2 例: 最大値 6 6 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 12

Slide 12 text

6 6 6 6 例: 最大値 6 6 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 13

Slide 13 text

6 6 6 6 例: 最大値 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 14

Slide 14 text

6 6 6 6 例: 最大値 6 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 15

Slide 15 text

6 6 6 6 例: 最大値 6 値 アクティブノード 値 非アクティブノード 値 値が更新されたノード メッセージ

Slide 16

Slide 16 text

Pregelの仕組み - 1つのマスターと複数のワーカーでクラスタを構成する - 複数ノードをパーティションと言う単位でまとめて扱う - パーティションの分割ルールに決まりはない - パーティションは任意の1つのワーカーに配置される

Slide 17

Slide 17 text

Pregel構成例 ノード ノード ノード ノード ノード パーティション パーティション ワーカー ノード ノード パーティション ワーカー マスター Graph Database

Slide 18

Slide 18 text

どうやって作る? - 分散システム - ノードの計算結果をメモリに持つ&局所性が必要 - ワーカー、パーティション、ノードという複数のステートマシンが存在し、木構造を構 成して通信しあう

Slide 19

Slide 19 text

アクターモデル

Slide 20

Slide 20 text

アクターモデル - 並行計算モデル - 競合を回避したい - メッセージパッシング

Slide 21

Slide 21 text

アクターモデル

Slide 22

Slide 22 text

アクターモデル Goにはchannelがあるのにこ れ必要?

Slide 23

Slide 23 text

アクターモデル vs channel アクターモデル (protoactor-go) channel (CSP) 宛先を明示的に指定する 宛先を指定しない 送信時ブロッキングしない (メッセージボックスがあふれる可能性) ブロッキングする (デッドロックする可能性) アクターツリー(親子関係)を構築できる ツリー構築は提供していない goroutineの同期ポイント リモートプロセスとも通信できる シングルプロセス内で利用 耐障害性 親アクターが子アクターのリカバリーをする 自動復旧はしない ライブラリによって提供 Go言語によって提供

Slide 24

Slide 24 text

アクターモデルを使いたいケース - リモート = 分散システム - ステートフル = 状態の競合を避けたい、局所化したい - 耐障害性

Slide 25

Slide 25 text

アクターモデルが使えそうなケース - チャットサーバーやゲームサーバー - ユーザーアクティビティを一箇所で管理する - TIS社ではペイメントサービスで利用 - イベントソーシングと組み合わせ、ユーザーの残高更新を一箇所で行うことで 整合性を保つ - 「Using Akka Cluster for a payment service」 - https://speakerdeck.com/negokaz/using-akka-cluster-for-a-payment-service?slide=33

Slide 26

Slide 26 text

protoactor-go - アクターモデルを実装したGoのライブラリ - https://github.com/AsynkronIT/protoactor-go - Asynktronという会社がホストしている - プロダクションでも使っているらしい

Slide 27

Slide 27 text

Hello world (アクター側) import "github.com/AsynkronIT/protoactor-go/actor" type Hello struct{ Who string } type HelloActor struct{} func (state *HelloActor) Receive(actorContext actor.Context) { switch msg := actorContext.Message().(type) { case Hello: fmt.Printf("Hello %v\n", msg.Who) } }

Slide 28

Slide 28 text

Hello world (メッセージ送信) import "github.com/AsynkronIT/protoactor-go/actor" func main() { props := actor.PropsFromProducer(func() actor.Actor { return &HelloActor{} }) pid, err := actor.EmptyRootContext.Spawn(props) if err != nil { // エラー処理 } actor.EmptyRootContext.Send(pid, Hello{Who: "Roger"}) }

Slide 29

Slide 29 text

耐障害性 - Let it crash - ハンドリングできないエラーが起きたらとにかくクラッシュ - 親のアクターが子のクラッシュを検知してリカバリーを試みる - クラッシュさせるには panicを使う - Supervisor Strategy - 子アクターがクラッシュした時にどうリカバリーするかをカスタマイズできる

Slide 30

Slide 30 text

Supervisor Strategy decider := func(reason interface{}) actor.Directive { // リカバリーの方法にリスタートを選択 return actor.RestartDirective } // 1000msec間隔で最大10回までリカバリーを試みる supervisor := actor.NewOneForOneStrategy(10, 1000, decider) props := actor. FromProducer(func() actor.Actor { return &HelloActor{} }). WithSupervisor(supervisor) pid, err := actor.EmptyRootContext.Spawn(props) if err != nil { // エラー処理 }

Slide 31

Slide 31 text

Supervisor Strategy - One-For-One Strategy - クラッシュしたアクターのみリカバリーを試みる - All-For-One Strategy - すべての兄弟アクターに対しリカバリーを試みる - Exponential Backoff Strategy - One-For-Oneのリトライ間隔がExponentialに増える

Slide 32

Slide 32 text

protoactor-go その他の機能 - .NETでも使える - リモートはgRPCで通信していて .NET ↔ Go でもクラスタを構築可能 - 他にもPython, Kotlin, JavaScriptのリポジトリも存在(動くかは不明・・) - plugin - メッセージをフックする処理を定義し、差し込むことができる - router - メッセージの送信先を複数アクターから 1つ選択する際の選択条件を指定できる - Virtual Actor - アクターモデルをさらに抽象化して Location TransparencyやAutomatic Scaleなどを提供する - MicrosoftのOrelansという論文がベース - Consulクラスタも別途必要になる

Slide 33

Slide 33 text

作ったもの Prerogel https://github.com/rerorero/prerogel - 最大値(maximum)と単一視点最短経路(sssp)のサンプルコード - たくさんマシンを借りて動かしてみたい(まだやってない) - k8sでスケールしたい - ※Spark on k8s があるらしいので、まともなやつが欲しい人は試してみるとい いかもしれません - protoactor-goのおかげで1ヶ月くらいでそこそこ動くものができた

Slide 34

Slide 34 text

使ってみた感想 - アクターモデルは要件に合っていれば便利 - Goでアクターを使いたい場合はprotoactor-go一択(個人の見解です) - アクターモデルありきなら、成熟度という意味ではGo以外の選択肢も考えてもよさ そう(Erlang, Akka)

Slide 35

Slide 35 text

ご清聴ありがとうございました 参考文献 - A universal modular ACTOR formalism for artificial intelligence: https://dl.acm.org/citation.cfm?id=1624804 - Microsoft Orleans: https://dotnet.github.io/orleans/ - Pregel A system for Large-Scale Graph Processing: https://www.cs.cmu.edu/~pavlo/courses/fall2013/static/papers/p135-malewicz.pdf - Apache Giraph: https://giraph.apache.org/ - A Bridging Model for Parallel Computation: http://web.mit.edu/6.976/www/handout/valiant2.pdf