Skip to content

Commit

Permalink
Snapshot on each block (#2723)
Browse files Browse the repository at this point in the history
* Add snapshot support

* Generate snapshot on ConnectTip

* Get snapshot on demand

* Get changed map under lock

* Update snapshot based on height

* Revert "Update snapshot based on height"

This reverts commit adc22c6.

* Do not Discard or Flush snapshot

* Use snapshot in getblockcount

* Snapshot on each block

* Resolve issues post-merge

* lint: circular deps

* Only snapshot on each block near tip

* Use IBD

* Snapshot when current block near the current time

* lint: circular deps

* Add history snapshots

* lint: circular deps

* Add vault snapshots

* Add GetVaultSnapshot wrapper

* lint: circular deps

* Pass vaultDB

* Get all snapshots at once

---------

Co-authored-by: Prasanna Loganathar <pvl@prasannavl.com>
  • Loading branch information
Bushstar and prasannavl committed Jul 15, 2024
1 parent 5370c8c commit 2352c26
Show file tree
Hide file tree
Showing 17 changed files with 496 additions and 62 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ DEFI_CORE_H = \
dfi/oracles.h \
dfi/poolpairs.h \
dfi/proposals.h \
dfi/snapshotmanager.h \
dfi/tokens.h \
dfi/threadpool.h \
dfi/coinselect.h \
Expand Down Expand Up @@ -460,6 +461,7 @@ libdefi_server_a_SOURCES = \
dfi/rpc_tokens.cpp \
dfi/rpc_vault.cpp \
dfi/skipped_txs.cpp \
dfi/snapshotmanager.cpp \
dfi/tokens.cpp \
dfi/threadpool.cpp \
dfi/undos.cpp \
Expand Down
8 changes: 6 additions & 2 deletions src/dbwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,12 @@ class CDBWrapper
return new CDBIterator(*this, pdb->NewIterator(readOptions));
}

[[nodiscard]] std::shared_ptr<CStorageSnapshot> GetStorageSnapshot() const {
return std::make_shared<CStorageSnapshot>(pdb);
[[nodiscard]] const leveldb::Snapshot* CreateLevelDBSnapshot() const {
return pdb->GetSnapshot();
}

void ReleaseSnapshot(const leveldb::Snapshot* snapshot) const {
pdb->ReleaseSnapshot(snapshot);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/dfi/accountshistory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ Res CAccountsHistoryView::EraseAccountHistoryHeight(uint32_t height) {
CAccountHistoryStorage::CAccountHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory, bool fWipe)
: CStorageView(new CStorageLevelDB(dbName, cacheSize, fMemory, fWipe)) {}

CAccountHistoryStorage::CAccountHistoryStorage(std::shared_ptr<CDBWrapper> &db,
std::unique_ptr<CCheckedOutSnapshot> &otherSnapshot)
: CStorageView(new CStorageLevelDB(db, otherSnapshot)) {}

CBurnHistoryStorage::CBurnHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory, bool fWipe)
: CStorageView(new CStorageLevelDB(dbName, cacheSize, fMemory, fWipe)) {}

Expand Down
8 changes: 6 additions & 2 deletions src/dfi/accountshistory.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ class CAccountsHistoryView : public virtual CStorageView {

class CAccountHistoryStorage : public CAccountsHistoryView, public CAuctionHistoryView {
public:
CAccountHistoryStorage(CAccountHistoryStorage &accountHistory)
: CStorageView(new CFlushableStorageKV(accountHistory.DB())) {}
CAccountHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory = false, bool fWipe = false);

explicit CAccountHistoryStorage(std::shared_ptr<CDBWrapper> &db,
std::unique_ptr<CCheckedOutSnapshot> &otherSnapshot);

CStorageLevelDB &GetStorage() { return static_cast<CStorageLevelDB &>(DB()); }
};

class CBurnHistoryStorage : public CAccountsHistoryView {
Expand Down Expand Up @@ -75,5 +78,6 @@ extern std::unique_ptr<CAccountHistoryStorage> paccountHistoryDB;
extern std::unique_ptr<CBurnHistoryStorage> pburnHistoryDB;

static constexpr bool DEFAULT_ACINDEX = true;
static constexpr bool DEFAULT_SNAPSHOT = false;

#endif // DEFI_DFI_ACCOUNTSHISTORY_H
10 changes: 1 addition & 9 deletions src/dfi/masternodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ CCustomCSView::CCustomCSView(CStorageKV &st)
CheckPrefixes();
}

CCustomCSView::CCustomCSView(std::unique_ptr<CStorageLevelDB> &st, const MapKV &changed)
CCustomCSView::CCustomCSView(std::unique_ptr<CStorageLevelDB> &st, MapKV &changed)
: CStorageView(new CFlushableStorageKV(st, changed)) {
CheckPrefixes();
}
Expand Down Expand Up @@ -1413,11 +1413,3 @@ void CalcMissingRewardTempFix(CCustomCSView &mnview, const uint32_t targetHeight
}
}
}

std::unique_ptr<CCustomCSView> GetViewSnapshot() {
// Get database snapshot and flushable storage changed map
auto [changed, snapshotDB] = pcustomcsview->GetStorage().GetSnapshotPair();

// Create new view using snapshot and change map
return std::make_unique<CCustomCSView>(snapshotDB, changed);
}
4 changes: 1 addition & 3 deletions src/dfi/masternodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ class CCustomCSView : public CMasternodesView,
explicit CCustomCSView(CStorageKV &st);

// Snapshot constructor
explicit CCustomCSView(std::unique_ptr<CStorageLevelDB> &st, const MapKV &changed);
explicit CCustomCSView(std::unique_ptr<CStorageLevelDB> &st, MapKV &changed);

// Cache-upon-a-cache constructors
CCustomCSView(CCustomCSView &other);
Expand Down Expand Up @@ -628,8 +628,6 @@ class CCustomCSView : public CMasternodesView,

std::map<CKeyID, CKey> AmISignerNow(int height, const CAnchorData::CTeam &team);

std::unique_ptr<CCustomCSView> GetViewSnapshot();

/** Global DB and view that holds enhanced chainstate data (should be protected by cs_main) */
extern std::unique_ptr<CStorageLevelDB> pcustomcsDB;
extern std::unique_ptr<CCustomCSView> pcustomcsview;
Expand Down
264 changes: 264 additions & 0 deletions src/dfi/snapshotmanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
#include <dfi/snapshotmanager.h>

#include <dfi/accountshistory.h>
#include <dfi/masternodes.h>
#include <dfi/vaulthistory.h>

template <typename T>
static void CheckoutSnapshot(T &checkedOutMap, const CBlockSnapshot &snapshot) {
const auto checkOutKey = snapshot.GetKey();
const auto dbSnapshot = snapshot.GetLevelDBSnapshot();
if (checkedOutMap.count(checkOutKey)) {
++checkedOutMap.at(checkOutKey).count;
} else {
checkedOutMap[checkOutKey] = {dbSnapshot, 1};
}
}

SnapshotCollection GetSnapshots() {
return psnapshotManager->GetSnapshots();
}

SnapshotCollection CSnapshotManager::GetSnapshots() {
if (auto currentSnapshots = GetCurrentSnapshots()) {
return std::move(*currentSnapshots);
}
return GetGlobalSnapshots();
}

std::optional<SnapshotCollection> CSnapshotManager::GetCurrentSnapshots() {
std::unique_lock lock(mtx);

if (!currentViewSnapshot || (historyDB && !currentHistorySnapshot) || (vaultDB && !currentVaultSnapshot)) {
return {};
}

auto [changed, snapshotDB] = CheckoutViewSnapshot();
auto viewSnapshot = std::make_unique<CCustomCSView>(snapshotDB, changed);

std::unique_ptr<CAccountHistoryStorage> historySnapshot{};
if (historyDB) {
auto snapshot = CheckoutHistorySnapshot();
historySnapshot = std::make_unique<CAccountHistoryStorage>(historyDB, snapshot);
}

std::unique_ptr<CVaultHistoryStorage> vaultSnapshot{};
if (vaultDB) {
auto snapshot = CheckoutVaultSnapshot();
vaultSnapshot = std::make_unique<CVaultHistoryStorage>(vaultDB, snapshot);
}

return std::make_tuple(std::move(viewSnapshot), std::move(historySnapshot), std::move(vaultSnapshot));
}

SnapshotCollection CSnapshotManager::GetGlobalSnapshots() {
// Same lock order as ConnectBlock
LOCK(cs_main);
std::unique_lock lock(mtx);

auto [changed, snapshotDB] = GetGlobalViewSnapshot();
auto viewSnapshot = std::make_unique<CCustomCSView>(snapshotDB, changed);

std::unique_ptr<CAccountHistoryStorage> historySnapshot{};
if (historyDB) {
auto snapshot = GetGlobalHistorySnapshot();
historySnapshot = std::make_unique<CAccountHistoryStorage>(historyDB, snapshot);
}

std::unique_ptr<CVaultHistoryStorage> vaultSnapshot{};
if (vaultDB) {
auto snapshot = GetGlobalVaultSnapshot();
vaultSnapshot = std::make_unique<CVaultHistoryStorage>(vaultDB, snapshot);
}

return std::make_tuple(std::move(viewSnapshot), std::move(historySnapshot), std::move(vaultSnapshot));
}

CCheckedOutSnapshot::~CCheckedOutSnapshot() {
// Check snapshot back in
psnapshotManager->ReturnSnapshot(key);
}

CSnapshotManager::CSnapshotManager(std::unique_ptr<CCustomCSView> &otherViewDB,
std::unique_ptr<CAccountHistoryStorage> &otherHistoryDB,
std::unique_ptr<CVaultHistoryStorage> &otherVaultDB) {
viewDB = otherViewDB->GetStorage().GetStorageLevelDB()->GetDB();

// acindex index might be disabled
if (otherHistoryDB) {
historyDB = otherHistoryDB->GetStorage().GetDB();
}

// vault index index might be disabled
if (otherVaultDB) {
vaultDB = otherVaultDB->GetStorage().GetDB();
}
}

template <typename T, typename U, typename V>
static void ReturnSnapshot(T &db, U &snapshot, V &checkedMap) {
if (db && snapshot) {
if (snapshot->GetLevelDBSnapshot() && (!checkedMap.count(snapshot->GetKey()) || // Not in map
(checkedMap.count(snapshot->GetKey()) && // Is in map...
!checkedMap.at(snapshot->GetKey()).count))) { // ..but not in use
checkedMap.erase(snapshot->GetKey());
db->ReleaseSnapshot(snapshot->GetLevelDBSnapshot());
}
snapshot.reset();
}
}

template <typename T, typename U>
static void SetCurrentSnapshot(T &db, U &currentSnapshot, SnapshotType type, const CBlockIndex *block) {
if (db) {
// Get database snapshot
const auto snapshot = db->GetStorage().CreateLevelDBSnapshot();

// Set current snapshot
currentSnapshot = std::make_unique<CBlockSnapshot>(
snapshot, MapKV{}, CBlockSnapshotKey{type, block->nHeight, block->GetBlockHash()});
}
}

void CSnapshotManager::SetBlockSnapshots(CFlushableStorageKV &viewStorge,
CAccountHistoryStorage *historyView,
CVaultHistoryStorage *vaultView,
const CBlockIndex *block,
const bool nearTip) {
std::unique_lock lock(mtx);

// Return current snapshots
::ReturnSnapshot(viewDB, currentViewSnapshot, checkedOutViewMap);
::ReturnSnapshot(historyDB, currentHistorySnapshot, checkedOutHistoryMap);
::ReturnSnapshot(vaultDB, currentVaultSnapshot, checkedOutVaultMap);

// Do not create current snapshots if snapshots are disabled or not near tip
if (!gArgs.GetBoolArg("-enablesnapshots", DEFAULT_SNAPSHOT) || !nearTip) {
return;
}

// Get view database snapshot and flushable storage changed map
auto [changedView, snapshotView] = viewStorge.CreateSnapshotData();

// Set current view snapshot
currentViewSnapshot = std::make_unique<CBlockSnapshot>(
snapshotView, changedView, CBlockSnapshotKey{SnapshotType::VIEW, block->nHeight, block->GetBlockHash()});

// Set current snapshots
::SetCurrentSnapshot(historyView, currentHistorySnapshot, SnapshotType::HISTORY, block);
::SetCurrentSnapshot(vaultView, currentVaultSnapshot, SnapshotType::VAULT, block);
}

std::pair<MapKV, std::unique_ptr<CStorageLevelDB>> CSnapshotManager::GetGlobalViewSnapshot() {
// Get database snapshot and flushable storage changed map
auto [changedMap, snapshot] = pcustomcsview->GetStorage().CreateSnapshotData();

// Create checked out snapshot
const auto blockIndex = ::ChainActive().Tip();
CBlockSnapshotKey key{SnapshotType::VIEW, blockIndex->nHeight, blockIndex->GetBlockHash()};
auto globalSnapshot = std::make_unique<CCheckedOutSnapshot>(snapshot, key);

// Set global as current snapshot
currentViewSnapshot = std::make_unique<CBlockSnapshot>(globalSnapshot->GetLevelDBSnapshot(), changedMap, key);

// Track checked out snapshot
::CheckoutSnapshot(checkedOutViewMap, *currentViewSnapshot);

return {changedMap, std::make_unique<CStorageLevelDB>(viewDB, globalSnapshot)};
}

std::unique_ptr<CCheckedOutSnapshot> CSnapshotManager::GetGlobalHistorySnapshot() {
// Get database snapshot and flushable storage changed map
auto snapshot = paccountHistoryDB->GetStorage().CreateLevelDBSnapshot();

const auto blockIndex = ::ChainActive().Tip();
CBlockSnapshotKey key{SnapshotType::HISTORY, blockIndex->nHeight, blockIndex->GetBlockHash()};
auto globalSnapshot = std::make_unique<CCheckedOutSnapshot>(snapshot, key);

// Set global as current snapshot
currentHistorySnapshot = std::make_unique<CBlockSnapshot>(globalSnapshot->GetLevelDBSnapshot(), MapKV{}, key);

// Track checked out snapshot
::CheckoutSnapshot(checkedOutHistoryMap, *currentHistorySnapshot);

// Create checked out snapshot
return globalSnapshot;
}

std::unique_ptr<CCheckedOutSnapshot> CSnapshotManager::GetGlobalVaultSnapshot() {
// Get database snapshot and flushable storage changed map
auto snapshot = pvaultHistoryDB->GetStorage().CreateLevelDBSnapshot();

const auto blockIndex = ::ChainActive().Tip();
CBlockSnapshotKey key{SnapshotType::VAULT, blockIndex->nHeight, blockIndex->GetBlockHash()};
auto globalSnapshot = std::make_unique<CCheckedOutSnapshot>(snapshot, key);

// Set global as current snapshot
currentVaultSnapshot = std::make_unique<CBlockSnapshot>(globalSnapshot->GetLevelDBSnapshot(), MapKV{}, key);

// Track checked out snapshot
::CheckoutSnapshot(checkedOutVaultMap, *currentVaultSnapshot);

// Create checked out snapshot
return globalSnapshot;
}

std::pair<MapKV, std::unique_ptr<CStorageLevelDB>> CSnapshotManager::CheckoutViewSnapshot() {
// Create checked out snapshot
auto snapshot =
std::make_unique<CCheckedOutSnapshot>(currentViewSnapshot->GetLevelDBSnapshot(), currentViewSnapshot->GetKey());

// Track checked out snapshot
::CheckoutSnapshot(checkedOutViewMap, *currentViewSnapshot);

return {currentViewSnapshot->GetChanged(), std::make_unique<CStorageLevelDB>(viewDB, snapshot)};
}

std::unique_ptr<CCheckedOutSnapshot> CSnapshotManager::CheckoutHistorySnapshot() {
// Create checked out snapshot
auto snapshot = std::make_unique<CCheckedOutSnapshot>(currentHistorySnapshot->GetLevelDBSnapshot(),
currentHistorySnapshot->GetKey());

// Track checked out snapshot
::CheckoutSnapshot(checkedOutHistoryMap, *currentHistorySnapshot);

return snapshot;
}

std::unique_ptr<CCheckedOutSnapshot> CSnapshotManager::CheckoutVaultSnapshot() {
// Create checked out snapshot
auto snapshot = std::make_unique<CCheckedOutSnapshot>(currentVaultSnapshot->GetLevelDBSnapshot(),
currentVaultSnapshot->GetKey());

// Track checked out snapshot
::CheckoutSnapshot(checkedOutVaultMap, *currentVaultSnapshot);

return snapshot;
}

template <typename T, typename U, typename V>
static void DestructSnapshot(const CBlockSnapshotKey &key, T &checkedOutMap, U &currentSnapshot, V &db) {
if (checkedOutMap.count(key)) {
--checkedOutMap.at(key).count;

bool isCurrentKey{};
if (currentSnapshot) {
isCurrentKey = currentSnapshot->GetKey().hash == key.hash;
}

// Release if not in use and not the current block
if (!checkedOutMap.at(key).count && !isCurrentKey) {
db->ReleaseSnapshot(checkedOutMap.at(key).snapshot);
checkedOutMap.erase(key);
}
}
}

void CSnapshotManager::ReturnSnapshot(const CBlockSnapshotKey &key) {
std::unique_lock lock(mtx);
::DestructSnapshot(key, checkedOutViewMap, currentViewSnapshot, viewDB);
::DestructSnapshot(key, checkedOutHistoryMap, currentHistorySnapshot, historyDB);
::DestructSnapshot(key, checkedOutVaultMap, currentVaultSnapshot, vaultDB);
}

std::unique_ptr<CSnapshotManager> psnapshotManager;
Loading

0 comments on commit 2352c26

Please sign in to comment.