TiKV分裂与添加peer并发场景下的代码问题

对于TiKV add peer出来的新peer在回放之前的分裂日志(BatchSplit)的时候(即分裂的peers里面不包含自己的store id), 这种场景下, 在exec_batch_split的时候的这段代码里面

let mut new_split_regions: HashMap<u64, NewSplitPeer> = HashMap::default();
        for req in split_reqs.get_requests() {
            let mut new_region = Region::default();
            new_region.set_id(req.get_new_region_id());
            new_region.set_region_epoch(derived.get_region_epoch().to_owned());
            new_region.set_start_key(keys.pop_front().unwrap());
            new_region.set_end_key(keys.front().unwrap().to_vec());
            new_region.set_peers(derived.get_peers().to_vec().into());
            for (peer, peer_id) in new_region
                .mut_peers()
                .iter_mut()
                .zip(req.get_new_peer_ids())
            {
                peer.set_id(*peer_id);
            }
            new_split_regions.insert(
                new_region.get_id(),
                NewSplitPeer {
                    peer_id: util::find_peer(&new_region, ctx.store_id).unwrap().get_id(),
                    result: None,
                },
            );
            regions.push(new_region);
        }

里面的find_peer应该找不到对应自己store id的peer, 然后在unwrap处panic.

如果是在添加peer的时候, 修改了peer的conf ver, 从而跳过分裂日志, 又是如何修改region meta中的start key和end key的呢

所以我想问一下, 我之前的想法是哪一步存在问题吗? 正确的流程应该是怎么样的?

首先需要明确的是无论是 add/remove peer or split/merge 都是先把请求 encode 成 raft log 并等到这条 raft log 被 apply 的时候才会在本地 store 上进行具体的 add/remove peer or split/merge. 所以在你的例子中的 add peer & split 都是有明确的先后关系的(raft log 序列的前后关系),下面分别讨论:

这个其实就是先 add peer 后 split 的情况,如果 BatchSplit 请求中的 conf ver 与这个 region add peer 之后的 conf ver 不一样,那么这个 BatchSplit 请求就会被跳过,这时 region 的 key range 是不会修改,另外因为被 commit 的 raft log 在各个 peer 上都是一样的,所以所有的 peer 在执行这条分裂日志的时候都会跳过,所以各个 peer 的 region key range 会保持一致

这个就是先 split 后 add peer 的情况,在 add peer 日志被 apply 之后,leader 才会为新 peer 生成&发生 snapshot , 这时生成的 snapshot 是根据 apply 了 add peer 日志之后的状态机生成的,因此 snapshot index 大于这条 split 日志,所以新 peer 也就不会有这条 split 日志

另外在生成 snapshot 时也会额外检查 conf_ver 在生成 snapshot 前后有没有变化,如果有变化就重新生成 snapshot, 保证 snapshop 中包含了最近的 confchange 日志(自然也就包含了在这条 confchange 日志之前的 split 日志)