// ----------------------------------------------------------------------
// File: RaftState.hh
// Author: Georgios Bitzes - CERN
// ----------------------------------------------------------------------
/************************************************************************
* quarkdb - a redis-like highly available key-value store *
* Copyright (C) 2016 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#ifndef __QUARKDB_RAFT_STATE_H__
#define __QUARKDB_RAFT_STATE_H__
#include "Common.hh"
#include "raft/RaftCommon.hh"
#include
#include
#include
namespace quarkdb {
//------------------------------------------------------------------------------
// Forward declarations
//------------------------------------------------------------------------------
class RaftJournal;
struct RaftStateSnapshot {
RaftTerm term;
RaftStatus status;
RaftServer leader;
RaftServer votedFor;
LogIndex leadershipMarker;
std::chrono::steady_clock::time_point timeCreated;
RaftStateSnapshot() : term(-1), status(RaftStatus::FOLLOWER), leadershipMarker(-1) {
timeCreated = std::chrono::steady_clock::now();
}
RaftStateSnapshot(RaftTerm trm, RaftStatus st, const RaftServer &ld,
const RaftServer &vote, LogIndex marker) : term(trm), status(st), leader(ld),
votedFor(vote), leadershipMarker(marker) {
timeCreated = std::chrono::steady_clock::now();
}
bool equals(const RaftStateSnapshot &rhs) const {
// We don't check timeCreated, this is on purpose.
return term == rhs.term && status == rhs.status && leader == rhs.leader &&
votedFor == rhs.votedFor && leadershipMarker == rhs.leadershipMarker;
}
bool equals(const std::shared_ptr &rhs) const {
return equals(*rhs.get());
}
};
using RaftStateSnapshotPtr = std::shared_ptr;
//------------------------------------------------------------------------------
// This class describes the current, authoritative state of raft, and must be
// bulletproof when accessed concurrently.
//------------------------------------------------------------------------------
class RaftState {
public:
RaftState(RaftJournal &journal, const RaftServer &me);
DISALLOW_COPY_AND_ASSIGN(RaftState);
bool observed(RaftTerm term, const RaftServer &leader);
bool grantVote(RaftTerm term, const RaftServer &vote);
bool ascend(RaftTerm term);
bool becomeCandidate(RaftTerm term);
bool dropOut(RaftTerm term);
void shutdown();
void wait(const std::chrono::milliseconds &t);
void wait_until(const std::chrono::steady_clock::time_point &t);
RaftServer getMyself();
std::vector getNodes();
RaftClusterID getClusterID();
//----------------------------------------------------------------------------
// Get a full, consistent and immutable snapshot of the current raft state.
//
// This is needed because this would not be safe:
// state.getCurrentTerm()
// state.getCurrentStatus()
//
// The state could have changed in-between, leading to horrible bugs.
//----------------------------------------------------------------------------
QDB_ALWAYS_INLINE
RaftStateSnapshotPtr getSnapshot() const {
return std::atomic_load(¤tSnapshot);
}
//------------------------------------------------------------------------------
// Test if the given snapshot is the same as the current one.
//------------------------------------------------------------------------------
QDB_ALWAYS_INLINE
bool isSnapshotCurrent(const RaftStateSnapshot *ptr) const {
return ptr == std::atomic_load(¤tSnapshot).get();
}
static RaftServer BLOCKED_VOTE;
private:
RaftJournal &journal;
std::mutex update;
std::condition_variable notifier;
RaftTerm term;
RaftStatus status;
RaftServer leader;
RaftServer votedFor;
LogIndex leadershipMarker;
const RaftServer myself;
//------------------------------------------------------------------------------
// A shared pointer to the current state snapshot.
//
// Do not even _think_ of modifying the underlying object. :) The pointer
// has to be updated atomically with a new object - from a single point in
// time and onwards, getSnapshot() starts returning a pointer to the new
// object, but old snapshots remain unchanged and valid.
//------------------------------------------------------------------------------
std::shared_ptr currentSnapshot;
void updateSnapshot();
void updateJournal();
void declareEvent(RaftTerm observedTerm, const RaftServer &observedLeader);
void updateStatus(RaftStatus newstatus);
};
}
#endif