<commit_before>#include "ReplicatedLog.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "System/Events/EventLoop.h"

//#define RLOG_DEBUG_MESSAGES 1

static Buffer dummy;

void ReplicatedLog::Init(QuorumContext* context_)
{
    Log_Trace();
    
    context = context_;

    paxosID = 0;
    waitingOnAppend = false;
    
    proposer.Init(context);
    acceptor.Init(context);

    lastRequestChosenTime = 0;
    commitChaining = false;
    alwaysUseDatabaseCatchup = false;
    
    dummy.Write("dummy");
}

void ReplicatedLog::Shutdown()
{
    proposer.Shutdown();
}

void ReplicatedLog::SetUseProposeTimeouts(bool useProposeTimeouts_)
{
    useProposeTimeouts = useProposeTimeouts_;
}

void ReplicatedLog::SetCommitChaining(bool commitChaining_)
{
    commitChaining = commitChaining_;
}

bool ReplicatedLog::GetCommitChaining()
{
    return commitChaining;
}

void ReplicatedLog::SetAsyncCommit(bool asyncCommit)
{
    acceptor.SetAsyncCommit(asyncCommit);
}

bool ReplicatedLog::GetAsyncCommit()
{
    return acceptor.GetAsyncCommit();
}

void ReplicatedLog::SetAlwaysUseDatabaseCatchup(bool alwaysUseDatabaseCatchup_)
{
    alwaysUseDatabaseCatchup = alwaysUseDatabaseCatchup_;
}

void ReplicatedLog::Stop()
{
    proposer.Stop();
}

void ReplicatedLog::Continue()
{
    if (context->IsLeaseKnown() && context->GetHighestPaxosID() > GetPaxosID())
        RequestChosen(context->GetLeaseOwner());
}

bool ReplicatedLog::IsMultiPaxosEnabled()
{
    return proposer.state.multi;
}

bool ReplicatedLog::IsAppending()
{
    return context->IsLeaseOwner() && proposer.state.numProposals > 0;
}

void ReplicatedLog::TryAppendDummy()
{
    Log_Trace();
    
    if (proposer.IsActive())
        return;

    proposer.SetUseTimeouts(true);
    Append(dummy);
#ifdef RLOG_DEBUG_MESSAGES
    Log_Debug("Appending DUMMY!");
#endif
}

void ReplicatedLog::TryAppendNextValue()
{
    Log_Trace();
    
    if (!context->IsLeaseOwner() || proposer.IsActive() || !proposer.state.multi)
        return;
    
    Buffer& value = context->GetNextValue();
    if (value.GetLength() == 0)
        return;
    
    proposer.SetUseTimeouts(useProposeTimeouts);
    Append(value);
}

void ReplicatedLog::TryCatchup()
{
    if (context->IsLeaseKnown() && context->GetHighestPaxosID() > GetPaxosID())
        RequestChosen(context->GetLeaseOwner());
}

void ReplicatedLog::Restart()
{
    context->OnStartProposing();

    if (proposer.IsActive())
        proposer.Restart();
}

void ReplicatedLog::SetPaxosID(uint64_t paxosID_)
{
    paxosID = paxosID_;
}

uint64_t ReplicatedLog::GetPaxosID()
{
    return paxosID;
}

void ReplicatedLog::NewPaxosRound()
{
    EventLoop::Remove(&(proposer.prepareTimeout));
    EventLoop::Remove(&(proposer.proposeTimeout));
<commit_msg>Fix possible bug in Paxos code where, if PaxosProposer::restartTimeout is active when ReplicatedLog::NewPaxosRound() executes, and then the timer starts a new round of replication, then eventually it will find that the proposed value is empty and it will assert.<commit_after>#include "ReplicatedLog.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "System/Events/EventLoop.h"

//#define RLOG_DEBUG_MESSAGES 1

static Buffer dummy;

void ReplicatedLog::Init(QuorumContext* context_)
{
    Log_Trace();
    
    context = context_;

    paxosID = 0;
    waitingOnAppend = false;
    
    proposer.Init(context);
    acceptor.Init(context);

    lastRequestChosenTime = 0;
    commitChaining = false;
    alwaysUseDatabaseCatchup = false;
    
    dummy.Write("dummy");
}

void ReplicatedLog::Shutdown()
{
    proposer.Shutdown();
}

void ReplicatedLog::SetUseProposeTimeouts(bool useProposeTimeouts_)
{
    useProposeTimeouts = useProposeTimeouts_;
}

void ReplicatedLog::SetCommitChaining(bool commitChaining_)
{
    commitChaining = commitChaining_;
}

bool ReplicatedLog::GetCommitChaining()
{
    return commitChaining;
}

void ReplicatedLog::SetAsyncCommit(bool asyncCommit)
{
    acceptor.SetAsyncCommit(asyncCommit);
}

bool ReplicatedLog::GetAsyncCommit()
{
    return acceptor.GetAsyncCommit();
}

void ReplicatedLog::SetAlwaysUseDatabaseCatchup(bool alwaysUseDatabaseCatchup_)
{
    alwaysUseDatabaseCatchup = alwaysUseDatabaseCatchup_;
}

void ReplicatedLog::Stop()
{
    proposer.Stop();
}

void ReplicatedLog::Continue()
{
    if (context->IsLeaseKnown() && context->GetHighestPaxosID() > GetPaxosID())
        RequestChosen(context->GetLeaseOwner());
}

bool ReplicatedLog::IsMultiPaxosEnabled()
{
    return proposer.state.multi;
}

bool ReplicatedLog::IsAppending()
{
    return context->IsLeaseOwner() && proposer.state.numProposals > 0;
}

void ReplicatedLog::TryAppendDummy()
{
    Log_Trace();
    
    if (proposer.IsActive())
        return;

    proposer.SetUseTimeouts(true);
    Append(dummy);
#ifdef RLOG_DEBUG_MESSAGES
    Log_Debug("Appending DUMMY!");
#endif
}

void ReplicatedLog::TryAppendNextValue()
{
    Log_Trace();
    
    if (!context->IsLeaseOwner() || proposer.IsActive() || !proposer.state.multi)
        return;
    
    Buffer& value = context->GetNextValue();
    if (value.GetLength() == 0)
        return;
    
    proposer.SetUseTimeouts(useProposeTimeouts);
    Append(value);
}

void ReplicatedLog::TryCatchup()
{
    if (context->IsLeaseKnown() && context->GetHighestPaxosID() > GetPaxosID())
        RequestChosen(context->GetLeaseOwner());
}

void ReplicatedLog::Restart()
{
    context->OnStartProposing();

    if (proposer.IsActive())
        proposer.Restart();
}

void ReplicatedLog::SetPaxosID(uint64_t paxosID_)
{
    paxosID = paxosID_;
}

uint64_t ReplicatedLog::GetPaxosID()
{
    return paxosID;
}

void ReplicatedLog::NewPaxosRound()
{
    EventLoop::Remove(&(proposer.prepareTimeout));
    EventLoop::Remove(&(proposer.proposeTimeout));
    EventLoop::Remove(&(proposer.restartTimeout));

{'fraction_non_alphanumeric': 0.12251542152159013, 'fraction_numerical': 0.003084304318026045, 'mean_word_length': 4.590996168582375, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5009243', 'n_tokens_mistral': 1972, 'n_tokens_neox': 1947, 'n_words': 313}