zoukankan      html  css  js  c++  java
  • phxpaxos状态机的管理

    一、phxpaxos内置的日志系统

    为了应对系统故障、进程重启之类的工程问题,phxpaxos需要对系统的状态进行持久化存储。从最简单的情况来说,系统必须要保证所有proposer提议的实例ID是连续递增的,而这个信息本身就要求对系统的状态进行持久化存储。
    但是,实例号的连续只是一个基础要求。考虑到paxos算法本身就是为了不稳定系统的强一致性问题,那么允许系统中的节点出现故障然后恢复的场景就不可避免。当一个节点重新启动之后,它如果获取整个系统的当前状态,同样是工程实现的一个重要问题。
    为了满足第二个需求,每个节点可以保存自己所有已经执行过的日志,它的作用感觉是相当于一个“我为人人、人人为我”的保险互助功能:由于每个节点都存储了所有已经完成的协商信息,那么当少数节点故障重启之后,就可以从系统的其它节点中同步历史信息,从而进行追赶。
    同样是在工程实现中,如果节点保留所有的历史信息,那么这个数据量是不可控制的,所以此时引入checkpoint机制。所有的协商结果都是用于改变状态机的状态,如果能够把状态机的状态保存起来,那么就可以把状态机之前的状态删除,这就相当于是一个同步点功能。举一个简单的例子,系统协商的是一个商品的价格,那么不同的提议可以设置不同的值(例如加10,减5等操作),每轮提议都可以修改该值,但是无论如何,最终决定出来的都是一个数值。当系统中有其它节点加入系统之后,它就可以首先学习这个值,之后就可以在这个状态的基础上操作。再用一个更常见的例子来说明,当数据库主备迁移的时候,通常把当前的数据库状态拷贝到新机器,之后两者同时在接收并处理请求即可。

    二、phxpaxos的日志实现

    算法设计的目的是为了保证一个值被“接受”之后不再变化,所以问题的关键在于对acceptor状态的持久化。acceptor的状态包括它promise的值以及接受的值,所以在acceptor执行这些关键操作的时候就需要将这些信息进行持久化,系统中写入日志的场景包括下面三个:

    1、promise时

    int Acceptor :: OnPrepare(const PaxosMsg & oPaxosMsg)
    {
    ……
    int ret = m_oAcceptorState.Persist(GetInstanceID(), GetLastChecksum());
    ……

    2、accept时:

    void Acceptor :: OnAccept(const PaxosMsg & oPaxosMsg)
    {
    ……
    int ret = m_oAcceptorState.Persist(GetInstanceID(), GetLastChecksum());
    ……

    3、learn时

    int LearnerState :: LearnValue(const uint64_t llInstanceID, const BallotNumber & oLearnedBallot,
    const std::string & sValue, const uint32_t iLastChecksum)
    {
    ……
    int ret = m_oPaxosLog.WriteState(oWriteOptions, m_poConfig->GetMyGroupIdx(), llInstanceID, oState);
    ……

    三、系统初始化时对InstanceID的处理

    启动时会从本地存储中读取最大的InstanceID数值。由于leveldb支持这种获取最大键值的形式,所以这个操作没有问题。

    int Acceptor :: Init()
    {
    uint64_t llInstanceID = 0;
    int ret = m_oAcceptorState.Load(llInstanceID);
    if (ret != 0)
    {
    NLErr("Load State fail, ret %d", ret);
    return ret;
    }

    if (llInstanceID == 0)
    {
    PLGImp("Empty database");
    }

    SetInstanceID(llInstanceID);

    PLGImp("OK");

    return 0;
    }

    四、给其它节点发送系统acceptor状态

    const bool LearnerSender :: Comfirm(const uint64_t llBeginInstanceID, const nodeid_t iSendToNodeID)

    {
    m_oLock.Lock();

    bool bComfirmRet = false;

    if (IsIMSending() && (!m_bIsComfirmed))
    {
    if (m_llBeginInstanceID == llBeginInstanceID && m_iSendToNodeID == iSendToNodeID)
    {
    bComfirmRet = true;

    m_bIsComfirmed = true;
    m_oLock.Interupt();
    }
    }

    m_oLock.UnLock();

    return bComfirmRet;
    }

    五、prepare、accept、learn对同一个InstanceID的操作

    一个InstanceID的chosen可能要陆续经过这三次持久化,但是由于这个InstanceID是唯一的,所以之后的更新并不会产生新的实例,只是更新实例的状态。

    六、当其它节点学习的时候,如何避免把prepare和accept状态下的未完成节点同步过去

    发送的上限是learner中保存的已经学习到的实例,prepare和accept状态下的InstanceID大于m_poLearner->GetInstanceID()
    void LearnerSender :: SendLearnedValue(const uint64_t llBeginInstanceID, const nodeid_t iSendToNodeID)
    {
    ……
    int iSendCount = 0;
    while (llSendInstanceID < m_poLearner->GetInstanceID())
    {
    ……
    }

  • 相关阅读:
    css
    js -【 数组】判断一个变量是数组类型的几种方法
    【消灭代办】第2周
    【本周面试题】第2周
    【本周面试题】第1周
    【消灭代办】第1周
    echarts
    css
    js
    JS方法
  • 原文地址:https://www.cnblogs.com/tsecer/p/10637865.html
Copyright © 2011-2022 走看看