More Related Content
Similar to スケールするシステムにおけるエンティティの扱いと 分散ID生成
Similar to スケールするシステムにおけるエンティティの扱いと 分散ID生成 (20)
スケールするシステムにおけるエンティティの扱いと 分散ID生成
- 5. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 5
エンティティと素直に付き合っている
とシステムはスケールしない
記憶デバイス、ファイルシステム、データベース、アーキテクチャは
スケールするためにイミュータブルになっていく
ミュータブルなエンティティの状態をインフラストラク
チャに永続化しない方法を考えないといけない
Immutability Changes Everything Designing Data-Intensive Applications
- 6. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved.
ドメインイベントによるEvent Sourcing
• 不変のイベントだけで状態を管理する手法にEvent
Sourcingがある
• エンティティの状態が変更された時ドメインイベントが
起きる
• 不変のドメインイベントの歴史があればエンティティの
状態が復元できる
• ドメインイベントだけ永続化すれば追記のみにできる
6
未読+1 未読+1 未読-1 未読+1 未読-1 未読+1
未読件数2
ドメインイベントの歴史
エンティティ
の状態
- 7. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 7
エンティティをアクターで表現して
Event Sourcingする
• エンティティの状態をアクターの内部に隔離する
• コマンドメッセージを受け取ったらドメインイベントを永続化してエン
ティティの状態を変更
• アクターは状態や位置、障害を隠蔽するので、外からは無限の寿命を持
つオブジェクトのように見える
Akka Persistence and Eventuate
Akka Persistenceはアクターで
Event Sourcingを行う実装の1つ
- 8. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 8
エンティティアクターの実装
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
}
}
- 10. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved.
分散ID生成器: Twitter Snowflake
• システムをスケールさせるには、従来の中央集権的なDB
によるID生成ではなく、分散独立したID生成が必要にな
る
• Snowflakeは分散ID生成サービス
• Twitter社がMySQLからCassandraに移行した時に作られ
た
• https://github.com/twitter/snowflake
10
- 12. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 12
ID生成アクターを作ってみる
class IdWorker(dcId: DatacenterId, wId: WorkerId) extends Actor
with 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を生成可能
ソースコード
- 13. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved.
エンティティ(分散ID生成器)の一意
性をどう保証するか
• エンティティはシステムに複数存在してはならない
• 複数存在すると同時に異なるドメインイベントが発行さ
れ矛盾した状態になる
• ID生成器もエンティティで、同じIDのものが複数存在し
てはならない(重複したIDの発行につながる)
• SnowflakeはZookeeperを使ってID生成器を管理している
13
- 14. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 14
エンティティの一意性を保証する手法
シャーディング
Shard Coordinator
Shard Region Shard Region Shard Region
IdWorker-1
IdWorker-2
IdWorker-3
…
IdWorker-30
IdWorker-31
IdWorker-32
…
client
GenerateId(workerId = 30)
Node 1 Node N
ID生成器とエンティティの一意性は同じ手法で実現可能
- 16. スケールするシステムにおけるエンティティの扱いと分散ID生成
2016/03/26 © ChatWork All rights reserved. 16
まとめ
•エンティティは一意に識別できるオブジェクト
•エンティティは状態が変わり続けるから難しい
•システムをスケールさせるためには不変なデータを扱う
必要がある
•Event Sourcingをつかえば可変なエンティティではなく
不変なドメインイベントを永続化できる
•DBに頼らずにエンティティのIDを発行する手法がある
•システムレベルでエンティティの一意性を保証する技術
にシャーディングがある