braft
braft copied to clipboard
multi-raft使用场景中途删除node后的目录清理
multi-raft的使用场景中,一台机器持有多个raft组的node,如果某个raft组发生配置变更后要删除掉某个node,现在发现braft没有对当前机器旧的raft组的日志目录做任何处理,同时测试中也发现这个旧的node其实也还在,退化成了follower,由于不在最新配置中也在一直做无效的pre_vote。不清楚braft原来这部分是怎么考虑的,还望解答下。 我们的存储log会比较大,也可能会涉及某个node删除后重新加回,但我们想删除旧的目录文件占用,想请教下这里对删除销毁旧的node的处理时机和机制有什么建议的处理方式吗?或者是否能够通知用户状态机去做清理?
这里有几个原因:
-
严肃的删除这个语义在没有外围配合的情况下是做不到的。因为remove peer对被删的peer来说,是不能保证能够收到这条日志的。既然不能保证,也就无从保证删数据这个事情。
-
业务系统本身也需要记录peer的信息以做一些管理的操作,业务系统可以保证自己能够接收到删除的请求(譬如无限重试)。在实践过程中,remove peer和gc 数据推荐作为一个整体来完成,change peer 串行化。否则的话,并发的change peer 后不gc数据可能导致脑裂。
-
braft并不总是知道该删什么。业务可以自定义 log storage、raft meta storage、snapshot storage,可以自行决定data存放在哪里,删除raft的数据只是整体删除副本操作的一部分。这里要么全删,不存在保留一部分的问题。
最新版本里增加了一个gc_raft_data接口,可用于删除raft部分的数据,不过对于没有使用leveldb存储vorefor小文件的情况下,也就是仅仅删除了几个目录下的文件而已。
了解了,感谢回复,另外第2条中提到的实践经验中,“并发的change peer 后不gc数据可能导致脑裂”这个场景是怎么理解,每次某个组做change peer操作时候不是都会有状态busy检查吗,本身就不允许做并发变更
脑裂问题的问题,我举三个例子吧。
-
一个类似于线程安全ABA的问题。在PeerId里有一个idx字段,这个类似与一个version号,可用于区分同一机器上的同名副本。严肃的实现里最好保证同一复制组每次创建的副本的idx不一样,但是实际实现中,有时候比较难做到,所以会有直接都设置为0的情况。看下面这个例子: 复制组有3个副本(a, b, c),remove c 后变成 (a, b),c 副本并未看到最新的日志,它的配置停留在(a, b, c)。经过一系列复杂的变更之后,a 副本被销毁(机器维修之类的,数据被销毁),又再次在同一机器上被创建。这时候 a 如果出现异常,且日志比 c 还旧,这个副本也残留了。这时候 a、c 互相通信,c 是可以成为主的。
-
braft 提供了在极端情况下强制reset peer的运维功能。(a, b, c) 副本直接被 reset 成 (a), 它自己形成了一个复制组,残留的 bc 如果还继续工作,也能成为一个复制组。
-
最后一个例子容易出现在系统初始化的阶段。当配置完系统之后,发现有问题,重新来了一遍,重名的复制组被再次创建了,这时候残留的副本可能会干扰新建的复制组。
想请教一下, 对于采用local-merged模式,在一个目录下存放所有braft::Node的meta信息(meta uri)的情况,在删除其中一个node后,这部门meta数据要如何清理。 只能通过gc_raft_data?
另外, 我看gc_raft_data接口的GCOptions参数要求给出VersionedGroupId,但是version group id似乎没有对外暴露, 只能向内部实现那样自己去组装吗? butil::string_printf(&_v_group_id, "%s_%d", _group_id.c_str(), _server_id.idx);
想请教一下, 对于采用local-merged模式,在一个目录下存放所有braft::Node的meta信息(meta uri)的情况,在删除其中一个node后,这部门meta数据要如何清理。 只能通过gc_raft_data?
另外, 我看gc_raft_data接口的GCOptions参数要求给出VersionedGroupId,但是version group id似乎没有对外暴露, 只能向内部实现那样自己去组装吗?
butil::string_printf(&_v_group_id, "%s_%d", _group_id.c_str(), _server_id.idx);
第一个问题,只能通过gc_raft_data删除。 第二个问题,version 本来也是用户自己定的。理论上每次在同一个节点上新建出来同一个复制组的副本,version都应该变化,以避免混淆。
好的,多谢了。
是不是通过braft::Node暴露一个返回VersionGroupId的util的方法会好一点,这样用户不会关心VersionGroupId的内部组织细节。
好的,多谢了。
是不是通过braft::Node暴露一个返回VersionGroupId的util的方法会好一点,这样用户不会关心VersionGroupId的内部组织细节。
嗯,可以考虑暴露一个