スケールするシステムにおけるエンティティの扱いと 分散id生成

16
スススススススススススススススススススス スススス スス ID スス 安安安安 ChatWork 2016/03/26

Upload: tanukkii

Post on 16-Feb-2017

1.216 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成安田裕介

ChatWork2016/03/26

Page 2: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

アジェンダ•エンティティとは何か•エンティティの何が難しいのか•エンティティの難しさの解決策: Event Sourcing•アクターとエンティティ•エンティティの IDの分散発行方法•エンティティの一意性の保証:シャーディング

Page 3: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティ•一意に識別できるオブジェクト•ユーザー、メッセージなど• IDを持つ•同一エンティティの状態は変わり続ける• e.g. メッセージは編集されたり消されたりする•ドメインイベントやコマンドもエンティティ (不変 )

Page 4: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティは変化するから難しい

Page 5: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティと素直に付き合っているとシステムはスケールしない記憶デバイス、ファイルシステム、データベース、アーキテクチャはスケールするためにイミュータブルになっていく

ミュータブルなエンティティの状態をインフラストラクチャに永続化しない方法を考えないといけないImmutability Changes Everything Designing Data-Intensive Applications

Page 6: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

ドメインイベントによる Event Sourcing

•不変のイベントだけで状態を管理する手法に Event Sourcingがある

•エンティティの状態が変更された時ドメインイベントが起きる•不変のドメインイベントの歴史があればエンティティの状態が復元できる•ドメインイベントだけ永続化すれば追記のみにできる

未読 +1 未読 +1 未読 -1 未読 +1 未読 -1 未読 +1

未読件数2

ドメインイベントの歴史

エンティティの状態

Page 7: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティをアクターで表現してEvent Sourcingする

• エンティティの状態をアクターの内部に隔離する• コマンドメッセージを受け取ったらドメインイベントを永続化してエンティティの状態を変更• アクターは状態や位置、障害を隠蔽するので、外からは無限の寿命を持つオブジェクトのように見える

Akka Persistence and Eventuate

Akka PersistenceはアクターでEvent Sourcingを行う実装の1つ

Page 8: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティアクターの実装class MessageEntity(messageId: MessageId, roomId: ChatRoomId) extends PersistentActor with ActorLogging { import MessageProtocol._

override def persistenceId: String = Message.persistenceId(messageId, roomId)

var messageState: Message = _

def receiveCommand: Receive = { case Commands.PostMessage(id, roomId, message, createdAt) => { val p = Message.postMessage(id, roomId, message, createdAt) persist(MessagePosted(p)) { event => updateState(MessagePosted(p)) context.system.eventStream.publish(MessagePosted(p)) } } case Commands.DeleteMessage(id, roomId, deletedAt) => { val e = messageState.deleteMessage(deletedAt) persist(MessageDeleted(e)) { event => updateState(MessageDeleted(e)) saveSnapshot(messageState) context.system.eventStream.publish(MessageDeleted(e)) } } }

def receiveRecover: Receive = { case event: MessagePosted => updateState(event) case event: MessageDeleted => updateState(event) }

def updateState(event: MessageProtocol.Event): Unit = event match { case MessagePosted(newMessage) => messageState = newMessage case MessageDeleted(deletedMessage) => messageState = deletedMessage }}

Page 9: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティの IDは誰が決める?• Event Sourcingではエンティティは DBに存在しない•エンティティはシステムに存在する• IDの発行を DBに任せられない•そもそも分散 DBは連番 IDを発行できない

Page 10: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

分散 ID生成器 : Twitter Snowflake•システムをスケールさせるには、従来の中央集権的なDBによる ID生成ではなく、分散独立した ID生成が必要になる

• Snowflakeは分散 ID生成サービス• Twitter社がMySQLから Cassandraに移行した時に作られた• https://github.com/twitter/snowflake

Page 11: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

Chatworkの ID生成器

Scalaを用いて分散IDワーカを実装する PHPでID生成器を実装してみました

Page 12: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

ID生成アクターを作ってみるclass IdWorker(dcId: DatacenterId, wId: WorkerId) extends Actorwith IdWorkerImpl with ActorLogging { import IdWorkerProtocol._

val datacenterId: Long = dcId.value val workerId: Long = wId.value

var sequenceId: Long = 0L

var lastTimestamp = -1L

def receive: Receive = { case msg@GenerateId(replyTo) => { val NextId(idOpt, timestamp, nextSequence) = nextId(timeGen(), lastTimestamp, sequenceId) sequenceId = nextSequence lastTimestamp = timestamp idOpt match { case Some(id) => replyTo ! IdGenerated(id) case None => { log.debug("retrying to avoid id duplication") self ! msg } } } }}

ID生成のアルゴリズムは同じ状態をアクター内に閉じ込めて同期コードを排除時間分解能を超えた時にブロックしない

1つの IdWorkerでおよそ 40k/secの IDを生成可能ソースコード

Page 13: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティ(分散 ID生成器)の一意性をどう保証するか•エンティティはシステムに複数存在してはならない•複数存在すると同時に異なるドメインイベントが発行され矛盾した状態になる• ID生成器もエンティティで、同じ IDのものが複数存在してはならない(重複した IDの発行につながる)• Snowflakeは Zookeeperを使って ID生成器を管理している

Page 14: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

エンティティの一意性を保証する手法シャーディングShard Coordinator

Shard Region Shard Region Shard Region

IdW

orke

r-1

IdW

orke

r-2

IdW

orke

r-3

IdW

orke

r-30

IdW

orke

r-31

IdW

orke

r-32

…client

GenerateId(workerId = 30)

Node 1 Node N

ID生成器とエンティティの一意性は同じ手法で実現可能

Page 15: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

シャーディングに対応した分散 ID生成器のソースコードhttps://github.com/TanUkkii007/reactive-snowflake

Page 16: スケールするシステムにおけるエンティティの扱いと 分散ID生成

スケールするシステムにおけるエンティティの扱いと分散 ID生成

2016/03/26 © ChatWork All rights reserved.

まとめ•エンティティは一意に識別できるオブジェクト•エンティティは状態が変わり続けるから難しい•システムをスケールさせるためには不変なデータを扱う必要がある•Event Sourcingをつかえば可変なエンティティではなく不変なドメインイベントを永続化できる•DBに頼らずにエンティティの IDを発行する手法がある•システムレベルでエンティティの一意性を保証する技術にシャーディングがある