FlowG – 分布式系统无需Raft(第二部分)
FlowG – Distributed Systems without raft (part 2)

原始链接: https://david-delassus.medium.com/distributed-systems-without-raft-part-2-81ca31eae4db

该系统使用SWIM协议进行节点发现,并利用BadgerDB的增量备份实现数据复制。每个节点都维护一个状态,跟踪来自其他节点数据的最新已知版本。 在SWIM的“TCP Push/Pull”过程中,每个节点都会与其他节点共享其状态。接收到状态后,节点会将其与自身已知信息进行比较,并向源节点的管理接口发起并行HTTP请求,以获取任何较新的数据分区(身份验证、配置、日志)。 这些请求利用BadgerDB的`Backup`函数,通过请求体发送自上次已知版本以来的数据。新的备份版本号作为HTTP尾部信息发送(`X-FlowG-Since`)。 接收节点的HTTP处理器使用BadgerDB的`Load`函数从请求体加载数据,并用接收到的数据版本更新其本地状态,完成复制过程。

一个Hacker News帖子讨论了FlowG,一个避免使用Raft共识算法的分布式系统。作者linkdd最初表示放弃Raft的原因是Raft只允许一个领导者。 kikimora澄清说Raft可以有多个领导者,每个状态空间分区一个。在一个Raft集群中,节点可以同时是某些分区的领导者,而其他分区的副本。 linkdd承认了这个澄清,并承认他对Raft的理解很肤浅。他进一步解释说,Go的hashicorp/raft库的复杂性以及领导者选举失败的实例(可能是由于用户错误,PEBKAC)是他决定在FlowG中放弃Raft的促成因素。

原文

To implement replication, we will rely on the SWIM protocol, and on BadgerDB’s incremental backup feature.

The hashicorp/memberlist package provides what they call a “TCP Push/Pull”. Periodically, each node computes their local state, send them to other nodes, which then merge the remote state with their local state.

We will trigger the replication during this “TCP Push/Pull” process.

We defined a node state as: the set of last known version of other nodes.

For example:

{
"node_id": "flowg-0",
"last_sync": {
"flowg-1": {
"auth": 1,
"config": 2,
"log": 324
},
"flowg-2": {
"auth": 0,
"config": 0,
"log": 0
}
}
}

When flowg-1 receives this state from flowg-0, it will start an HTTP request to flowg-0’s management interface and run (in parallel):

// POST http://<remote>/cluster/sync/auth
newAuthSince, err := authDb.Backup(
syncAuthRequestBodyWriter,
remoteState.LastSync["flowg-1"].Auth,
)
// send `X-FlowG-Since: <newAuthSince>` as HTTP trailer

// POST http://<remote>/cluster/sync/config
newConfigSince, err := configDb.Backup(
syncConfigRequestBodyWriter,
remoteState.LastSync["flowg-1"].Config,
)
// send `X-FlowG-Since: <newConfigSince>` as HTTP trailer

// POST http://<remote>/cluster/sync/log
newLogSince, err := logDb.Backup(
syncLogRequestBodyWriter,
remoteState.LastSync["flowg-1"].Log,
)
// send `X-FlowG-Since: <newLogSince>` as HTTP trailer

On the node flowg-0, the HTTP handler for /cluster/sync/... will read the request body:

nodeId := request.Header.Get("X-FlowG-NodeID")
err := db.Load(syncLogrequestBodyReader, 1)
updateLocalState("log", nodeId, request.Trailer.Get("X-FlowG-Since"))

And voilà! We have actual replication.

联系我们 contact @ memedata.com