phxpaxos状态机的管理

1、phxpaxos内置的日志系统

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

2、phxpaxos的日志实现

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

一、promise时

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

二、accept时:

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

三、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);
……
学习

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

启动时会从本地存储中读取最大的InstanceID数值。因为leveldb支持这种获取最大键值的形式,因此这个操做没有问题。spa

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;
}

4、给其它节点发送系统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;
}

5、prepare、accept、learn对同一个InstanceID的操做

一个InstanceID的chosen可能要陆续通过这三次持久化,可是因为这个InstanceID是惟一的,因此以后的更新并不会产生新的实例,只是更新实例的状态。

6、当其它节点学习的时候,如何避免把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()) { ……}

相关文章
相关标签/搜索