#include "TransactionManager.h"
#include
#include
namespace
{
log4cxx::LoggerPtr spLogger(log4cxx::Logger::getLogger("TransactionManager"));
}
AllCommitHistories::AllCommitHistories(const std::vector& commits)
: mCommits(Sort(commits))
, mMaxCommitId(FindMaxCommitId(commits))
{
}
void AllCommitHistories::StoreCommit(const CommitHistory& commit)
{
mCommits.emplace_back(commit);
mMaxCommitId = std::max(mMaxCommitId, commit.mId);
mMaxCommitId = std::max(mMaxCommitId, commit.mParent);
}
boost::optional AllCommitHistories::GetCommit(CommitHistory::CommitId commitId)
{
boost::optional maybeCommit;
for (const auto& commit : mCommits)
{
if (commit.mId == commitId)
{
maybeCommit = commit;
}
}
return maybeCommit;
}
boost::optional AllCommitHistories::GetChild(CommitHistory::CommitId parentId)
{
boost::optional maybeCommit;
for (const auto& commit : mCommits)
{
if (commit.mParent == parentId)
{
maybeCommit = commit;
}
}
return maybeCommit;
}
boost::optional AllCommitHistories::DropOldestCommit()
{
boost::optional maybeCommit;
if (mCommits.size() > 0)
{
maybeCommit = mCommits.front();
mCommits.erase(mCommits.begin());
}
return maybeCommit;
}
boost::optional AllCommitHistories::GetMostRecentCommit()
{
boost::optional maybeCommit;
if (mCommits.size() > 0)
{
maybeCommit = mCommits.back();
}
return maybeCommit;
}
//Returns the highest commit id in use. This is not necessarily the commit id of the most recent commit
CommitHistory::CommitId AllCommitHistories::GetHighestCommitId()
{
return mMaxCommitId;
}
size_t AllCommitHistories::GetCommitCount()
{
return mCommits.size();
}
std::vector AllCommitHistories::Blame(const ConfigPair::Key& key)
{
auto getDelta = [key] (const CommitHistory& commit)
{
boost::optional maybeDelta;
for (const auto& delta : commit.mItems)
{
if (delta.first.GetKey() == key)
{
maybeDelta = delta;
}
}
return maybeDelta;
};
std::vector vec;
for (const auto& commit : mCommits)
{
auto configDelta = getDelta(commit);
if (configDelta)
{
BlameDelta blameDelta {commit.mId, commit.mTime, configDelta.get()};
vec.emplace_back(blameDelta);
}
}
//Reverse the vec so deltas are listed youngest -> oldest
std::reverse(vec.begin(), vec.end());
return vec;
}
CommitHistory::CommitId
AllCommitHistories::FindMaxCommitId(const std::vector& unsortedCommits)
{
CommitHistory::CommitId id = 0;
for (auto& commit : unsortedCommits)
{
id = std::max(id, commit.mId);
id = std::max(id, commit.mParent);
}
return id;
}
boost::optional
AllCommitHistories::FindMostRecentCommit(const std::vector& unsortedCommits)
{
boost::optional maybeCommit;
for (const auto& commit : unsortedCommits)
{
auto maybeChild = FindChild(commit.mId, unsortedCommits);
if (!maybeChild) //The youngest commit won't have a child
{
maybeCommit = commit;
}
}
return maybeCommit;
}
boost::optional
AllCommitHistories::FindChild(CommitHistory::CommitId parentId,
const std::vector& unsortedCommits)
{
boost::optional maybeCommit;
for (const auto& commit : unsortedCommits)
{
if (commit.mParent == parentId)
{
maybeCommit = commit;
}
}
return maybeCommit;
}
std::list
AllCommitHistories::Sort(const std::vector& unsortedCommits)
{
std::list sortedCommits;
auto maybeCurrent = FindMostRecentCommit(unsortedCommits);
while (maybeCurrent)
{
CommitHistory commit = maybeCurrent.get();
sortedCommits.insert(sortedCommits.begin(), commit);
maybeCurrent = FindCommit(commit.mParent, unsortedCommits);
}
return sortedCommits;
}
boost::optional
AllCommitHistories::FindCommit(CommitHistory::CommitId commitId,
const std::vector& unsortedCommits)
{
boost::optional maybeCommit;
for (const auto& commit : unsortedCommits)
{
if (commit.mId == commitId)
{
maybeCommit = commit;
}
}
return maybeCommit;
}
TransactionManager::TransactionManager(IConfigurator* pDb,
IHistoryStorage* pStorage,
size_t maxStorage)
: mpDb(pDb)
, mStorage(pStorage)
, mMaxStorage(maxStorage)
, mCommitHistory(mStorage->LoadAllCommits())
{
DropExcessiveCommits();
}
//TODO: In the future AllCommitHistory could be responsible for the local storage as well
size_t TransactionManager::LogCommit(const ConfigPairList& toBeCommitted)
{
CommitHistory commit;
CommitHistory::ConfigDeltaList items = GenerateDeltas(toBeCommitted);
commit.mItems.insert(commit.mItems.begin(), items.begin(), items.end());
commit.mId = mCommitHistory.GetHighestCommitId() + 1;
//Assign parent id (defaults to 0 if this is the first commit)
CommitHistory::CommitId parentId = 0;
boost::optional parent = mCommitHistory.GetMostRecentCommit();
if (parent)
{
parentId = parent.get().mId;
}
commit.mParent = parentId;
commit.mTime = boost::posix_time::second_clock::local_time();
mCommitHistory.StoreCommit(commit);
mStorage->StoreCommit(commit);
DropExcessiveCommits();
return commit.mId;
}
folly::Future
TransactionManager::Rollback(CommitHistory::CommitId commitId)
{
auto maybeCommit = mCommitHistory.GetCommit(commitId);
if (maybeCommit)
{
auto commit = maybeCommit.get();
if (CanRollback(commit))
{
for (const auto& item : commit.mItems)
{
if (item.first.GetValue().Empty())
{
mpDb->Erase(item.first.GetKey(), TRANSACTION_HISTORY_ID);
}
else
{
mpDb->Set(item.first, TRANSACTION_HISTORY_ID);
}
}
return mpDb->Commit(TRANSACTION_HISTORY_ID).thenValue([commitId](CommitHistory::CommitId)
{
LOG4CXX_INFO(spLogger, "Completed rollback of commit "