forked from vmware/concord-bft
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a reserved pages client for pruning (vmware#1637)
Provide persistence of pruning agreement and progress information in reserved pages. Pseudo code for how it can be used is provided as an inline comment. Introduce the `ReservedPagesMock` class and reuse it in ccron and pruning reserved pages client unit tests. In order to make it work, add the static `numberOfReservedPagesForClient()` method and make the `my_offset()` and `calc_my_offset()` methods static instead of const in `ResPagesClient`. That allows for a simpler init procedure in unit tests as we can now call them without an instance. Move the `INITIAL_GENESIS_BLOCK_ID` constant to kv_types.hpp and reuse it across the codebase. Add an unit test to verify the pruning reserved pages client's functionality. * Make `ReservedPagesMock` a friend of the client Also, make `my_offset()` private. * Address review comments * numberOfReservedPagesForClient() * use composition in ReservedPagesClient
- Loading branch information
1 parent
32d48c9
commit 1923351
Showing
14 changed files
with
510 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Concord | ||
// | ||
// Copyright (c) 2021 VMware, Inc. All Rights Reserved. | ||
// | ||
// This product is licensed to you under the Apache 2.0 license (the | ||
// "License"). You may not use this product except in compliance with the | ||
// Apache 2.0 License. | ||
// | ||
// This product may include a number of subcomponents with separate copyright | ||
// notices and license terms. Your use of these subcomponents is subject to the | ||
// terms and conditions of the subcomponent's license, as noted in the LICENSE | ||
// file. | ||
|
||
#pragma once | ||
|
||
#include "IReservedPages.hpp" | ||
|
||
#include <algorithm> | ||
#include <cstring> | ||
#include <map> | ||
#include <string> | ||
|
||
namespace bftEngine::test { | ||
|
||
template <typename ReservedPagesClient, uint32_t kSizeOfReservedPage = 4096> | ||
class ReservedPagesMock : public bftEngine::IReservedPages { | ||
public: | ||
uint32_t numberOfReservedPages() const override { return ReservedPagesClient::numberOfReservedPagesForClient(); } | ||
|
||
uint32_t sizeOfReservedPage() const override { return kSizeOfReservedPage; } | ||
|
||
bool loadReservedPage(uint32_t page_id, uint32_t size, char* data) const override { | ||
auto it = pages_.find(page_id); | ||
if (it != pages_.cend()) { | ||
std::memcpy(data, it->second.data(), std::min<std::size_t>(size, it->second.size())); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
void saveReservedPage(uint32_t page_id, uint32_t size, const char* data) override { | ||
pages_[page_id].assign(data, std::min<std::size_t>(size, sizeOfReservedPage())); | ||
} | ||
|
||
void zeroReservedPage(uint32_t page_id) override { | ||
auto it = pages_.find(page_id); | ||
if (it != pages_.cend()) { | ||
for (auto i = 0ul; i < it->second.size(); ++i) { | ||
it->second[i] = '\0'; | ||
} | ||
} | ||
} | ||
|
||
bool isReservedPageZeroed(uint32_t page_id) const { | ||
auto it = pages_.find(ReservedPagesClient::my_offset() + page_id); | ||
if (it == pages_.cend()) { | ||
return false; | ||
} | ||
return (it->second.find_first_not_of('\0') == std::string::npos); | ||
} | ||
|
||
const std::map<uint32_t, std::string>& pages() const { return pages_; } | ||
|
||
private: | ||
// reserved page ID -> contents | ||
std::map<uint32_t, std::string> pages_; | ||
}; | ||
|
||
} // namespace bftEngine::test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Represents a pruning agreement, i.e. when processing a PruneRequest. | ||
Msg Agreement 1 { | ||
# TicksGenerator period in seconds. | ||
uint32 tick_period_seconds | ||
|
||
# The number of blocks in a pruning batch. | ||
uint64 batch_blocks_num | ||
|
||
# The last agreed prunable block ID for all replicas. | ||
uint64 last_agreed_prunable_block_id | ||
} | ||
|
||
# Represents a pruning batch. | ||
Msg Batch 2 { | ||
# The latest block ID to prune in the batch. | ||
uint64 latest_batch_block_id_to | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Concord | ||
// | ||
// Copyright (c) 2021 VMware, Inc. All Rights Reserved. | ||
// | ||
// This product is licensed to you under the Apache 2.0 license (the "License"). You may not use this product except in | ||
// compliance with the Apache 2.0 License. | ||
// | ||
// This product may include a number of subcomponents with separate copyright notices and license terms. Your use of | ||
// these subcomponents is subject to the terms and conditions of the sub-component's license, as noted in the LICENSE | ||
// file. | ||
|
||
#pragma once | ||
|
||
#include "ReservedPagesClient.hpp" | ||
|
||
#include "kv_types.hpp" | ||
|
||
#include "pruning_msgs.cmf.hpp" | ||
|
||
#include <cstdint> | ||
#include <chrono> | ||
|
||
namespace concord::kvbc::pruning { | ||
|
||
// Creates a pruning agreement. | ||
// `tick_period` must be at least 1 second. | ||
// `batch_blocks_num` must be non-zero. | ||
// `last_agreed_prunable_block_id` must be at least INITIAL_GENESIS_BLOCK_ID. | ||
Agreement createAgreement(const std::chrono::seconds& tick_period, | ||
std::uint64_t batch_blocks_num, | ||
BlockId last_agreed_prunable_block_id); | ||
|
||
// Provides persistence of pruning agreement and progress information in reserved pages. | ||
// | ||
// Pseudo code for how it can be used: | ||
// | ||
// * On PruneRequest: | ||
// client.saveAgreement(agreement); | ||
// if (genesis <= agreement.last_agreed_prunable_block_id) ticks_gen.start(); | ||
// | ||
// * On Tick: | ||
// auto agreement = client.lastAgreement(); | ||
// if (agreement) { | ||
// auto until = std::min(genesis + agreement->batch_blocks_num, agreement->last_agreed_prunable_block_id + 1); | ||
// if (until >= genesis) { | ||
// client.saveLatestBatch(until - 1); | ||
// blockchain.deleteBlocksUntil(until); | ||
// } | ||
// if (genesis >= agreement->last_agreed_prunable_block_id) ticks_gen.stop(); | ||
// } | ||
// | ||
// * On Startup and State Transfer: | ||
// auto agreement = client.lastAgreement(); | ||
// const auto to = client.latestBatchBlockIdTo(); | ||
// if (to && *to + 1 > genesis) blockchain.deleteUntil(*to + 1); | ||
// if (genesis < agreement->last_agreed_prunable_block_id) ticks_gen.start(); | ||
// else ticks_gen.stop(); | ||
class ReservedPagesClient { | ||
private: | ||
static constexpr auto kPruningResPageCount = 2; | ||
using ClientType = bftEngine::ResPagesClient<ReservedPagesClient, kPruningResPageCount>; | ||
|
||
public: | ||
// Returns the number of reserved pages for this client. | ||
static std::uint32_t numberOfReservedPagesForClient() { return ClientType::numberOfReservedPagesForClient(); } | ||
|
||
public: | ||
// Loads data from reserved pages on construction. | ||
ReservedPagesClient(); | ||
|
||
public: | ||
// Saves the given agreement to reserved pages. | ||
void saveAgreement(const Agreement& agreement); | ||
|
||
// Updates an existing agreement's `tick_period` and `batch_blocks_num` in reserved pages. | ||
// Precondition: an agreement must already exist. If not, behaviour is undefined. | ||
void updateExistingAgreement(const std::chrono::seconds& tick_period, std::uint64_t batch_blocks_num); | ||
|
||
// Updates an existing agreement's `tick_period` in reserved pages. | ||
// Precondition: an agreement must already exist. If not, behaviour is undefined. | ||
void updateExistingAgreement(const std::chrono::seconds& tick_period); | ||
|
||
// Updates an existing agreement's `batch_blocks_num` in reserved pages. | ||
// Precondition: an agreement must already exist. If not, behaviour is undefined. | ||
void updateExistingAgreement(std::uint64_t batch_blocks_num); | ||
|
||
// Saves the `to` block ID of the latest batch. A batch is a range [genesis, to]. | ||
void saveLatestBatch(BlockId to); | ||
|
||
public: | ||
// Returns the latest agreement or std::nullopt if no agreement has been reached yet. | ||
const std::optional<Agreement>& latestAgreement() const { return latest_agreement_; } | ||
|
||
// Returns the `to` block ID of the latest batch or std::nullopt if no batch pruning has started yet. | ||
const std::optional<BlockId>& latestBatchBlockIdTo() const { return latest_batch_block_id_to_; } | ||
|
||
private: | ||
static constexpr std::uint32_t kLatestAgreementPageId{0}; | ||
static constexpr std::uint32_t kLatestBatchBlockIdToPageId{1}; | ||
|
||
private: | ||
std::optional<Agreement> latest_agreement_; | ||
std::optional<BlockId> latest_batch_block_id_to_; | ||
ClientType client_; | ||
}; | ||
|
||
} // namespace concord::kvbc::pruning |
Oops, something went wrong.