Skip to content

Commit 3bcec13

Browse files
author
Yehonatan Buchnik
authored
fix db checkpoint async bug (#2998)
The current implementation of the db checkpoint feature has a synchronization bug: While we take the db checkpoint in the background, we don't align anything with the checkpoint sequence number, i.e. the block number, bft metadata, pending reserved pages, and more. Once we put more client requests in a different resolution than 150 we start to see a wide set of issues: For example: The recovered replica won't start from a stable checkpoint, instead, it starts from the point where the db checkpoint was taken. In a case where the checkpoint was taken in the middle of another execution phase, we won't have the pending reserved pages to recover correctly. We trim the block at the point where the db checkpoint was taken, but we don't update the bft metadata accordingly. Below is an example of part of these issues: On replica 0, block 302 was created on sequence number 304 0|03-04-2023 10:48:24.125|INFO |skvbctest.replica|post-execution-thread|||304||executeWriteCommand|L:482|ConditionalWrite message handled; writesCounter=302 currBlock=302 | [SQ:1215] However, on recovery, the recovered replica has the same block that was created on sequence number 305: 5|03-04-2023 10:48:34.293|INFO |skvbctest.replica|post-execution-thread|||305||executeWriteCommand|L:482|ConditionalWrite message handled; writesCounter=1 currBlock=302 | [SQ:3] In this PR we propose a wide change that fixes the problem. Till now, the decision of whether to create a db checkpoint was the primary only. If the primary decided that it's time to create the db checkpoint, it sends a bft command whose execution is, creating a DB checkpoint. (Note that this approach, regardless of the above bugs, is not safe in terms of DOS attacks, a malicious primary can order the replicas to continuously create db checkpoints). Here we propose a different solution: the decision to create a db checkpoint is based on a deterministic local event (such as how much time has passed since the last created db checkpoint). This way, once decided a db checkpoint creation callback is registered to the stable sequence number event. Once the replica reaches this stable sequence number, it starts to create the db checkpoint asynchronously, but now the block number is aligned with the sequence number because it was taken right after the sequence number execution. To make the above feasible, (1) we cannot rely on local timeouts (instead, we consider only the time being received by consensus), (2) the db checkpoint metadata (such as sequence number and timestamp) has to be shared between all replicas (via reserved pages).
1 parent fa7a860 commit 3bcec13

File tree

14 files changed

+141
-185
lines changed

14 files changed

+141
-185
lines changed

bftengine/include/bftengine/DbCheckpointManager.hpp

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,17 @@
3434
#include "storage/db_interface.h"
3535
#include "util/filesystem.hpp"
3636
#include "kv_types.hpp"
37+
#include "ReservedPagesClient.hpp"
38+
3739
namespace _fs = fs;
3840
namespace bftEngine::impl {
3941
using std::chrono::duration_cast;
4042
using Status = concordUtils::Status;
4143
using SystemClock = std::chrono::system_clock;
42-
using Seconds = std::chrono::seconds;
4344
using InternalBftClient = bftEngine::impl::InternalBFTClient;
4445
using DbCheckpointId = uint64_t;
4546
using BlockId = uint64_t;
46-
class DbCheckpointManager {
47+
class DbCheckpointManager : public bftEngine::ResPagesClient<DbCheckpointManager, 1> {
4748
public:
4849
/***************************************************
4950
*@Input parameter1: Request sequnce number
@@ -64,7 +65,6 @@ class DbCheckpointManager {
6465
*@Return: returns a unique db checkpoint id. Else return std::nullopt
6566
***************************************************/
6667
std::optional<CheckpointId> createDbCheckpointAsync(const SeqNum& seqNum,
67-
const std::optional<Timestamp>& timestamp,
6868
const std::optional<DbCheckpointId>& blockId);
6969

7070
/***************************************************
@@ -95,7 +95,7 @@ class DbCheckpointManager {
9595
// * the path to the checkpoint
9696
using PrepareCheckpointCallback = std::function<void(BlockId, const std::string&)>;
9797

98-
Seconds getLastCheckpointCreationTime() const { return lastCheckpointCreationTime_; }
98+
std::chrono::seconds getLastCheckpointCreationTime() const { return lastCheckpointCreationTime_; }
9999
void initializeDbCheckpointManager(std::shared_ptr<concord::storage::IDBClient> dbClient,
100100
std::shared_ptr<bftEngine::impl::PersistentStorage> p,
101101
std::shared_ptr<concordMetrics::Aggregator> aggregator,
@@ -113,27 +113,19 @@ class DbCheckpointManager {
113113
void setNextStableSeqNumToCreateSnapshot(const std::optional<SeqNum>& seqNum);
114114
std::optional<SeqNum> getNextStableSeqNumToCreateSnapshot() const { return nextSeqNumToCreateCheckPt_; }
115115

116-
static DbCheckpointManager& instance(InternalBftClient* client = nullptr) {
117-
static DbCheckpointManager instance(client);
116+
static DbCheckpointManager& instance() {
117+
static DbCheckpointManager instance;
118118
return instance;
119119
}
120-
~DbCheckpointManager() {
120+
~DbCheckpointManager() override {
121121
stopped_ = true;
122122
shutdownCond_.notify_all(); // to wake up monitor thread so it can be destructed
123123
if (monitorThread_.joinable()) monitorThread_.join();
124124
}
125-
void sendInternalCreateDbCheckpointMsg(const SeqNum& seqNum, bool noop);
126125
BlockId getLastReachableBlock() const;
127-
SeqNum getLastStableSeqNum() const;
128126
void setCheckpointInProcess(bool, concord::kvbc::BlockId) const;
129-
void setOnStableSeqNumCb_(std::function<void(SeqNum)> cb) { onStableSeqNumCb_ = cb; }
130-
void onStableSeqNum(SeqNum s) {
131-
if (onStableSeqNumCb_) onStableSeqNumCb_(s);
132-
}
133-
void setGetLastStableSeqNumCb(std::function<SeqNum()> cb) { getLastStableSeqNumCb_ = cb; }
134127
inline void checkAndCreateDbSnapshot(SeqNum seqNum) {
135-
if (ReplicaConfig::instance().dbCheckpointFeatureEnabled)
136-
createDbCheckpointAsync(seqNum, std::nullopt, std::nullopt);
128+
if (ReplicaConfig::instance().dbCheckpointFeatureEnabled) createDbCheckpointAsync(seqNum, std::nullopt);
137129
}
138130
void addOnDbCheckpointCreatedCb(std::function<void(SeqNum)> cb) {
139131
if (cb) onDbCheckpointCreated_.push_back(cb);
@@ -145,26 +137,24 @@ class DbCheckpointManager {
145137
// only used for apollo test
146138
std::map<uint64_t, uint64_t> getDbSize();
147139
void setIsMetadataErased(bool isMetadataErased) { isMetadataErased_ = isMetadataErased; }
140+
void saveDbMetadataToReservedPages(SeqNum, std::chrono::seconds timestamp);
141+
void loadDbMetadataFromReservedPages();
148142

149143
private:
150144
logging::Logger getLogger() {
151145
static logging::Logger logger_(logging::getLogger("concord.bft.db_checkpoint_manager"));
152146
return logger_;
153147
}
154-
DbCheckpointManager(InternalBftClient* client)
155-
: client_(client),
156-
metrics_{concordMetrics::Component("rocksdbCheckpoint", std::make_shared<concordMetrics::Aggregator>())},
148+
DbCheckpointManager()
149+
: metrics_{concordMetrics::Component("rocksdbCheckpoint", std::make_shared<concordMetrics::Aggregator>())},
157150
maxDbCheckpointCreationTimeMsec_(metrics_.RegisterGauge("maxDbCheckpointCreationTimeInMsecSoFar", 0)),
158151
lastDbCheckpointSizeInMb_(metrics_.RegisterGauge("lastDbCheckpointSizeInMb", 0)),
159152
lastDbCheckpointBlockId_(metrics_.RegisterGauge("lastDbCheckpointBlockId", 0)),
160153
numOfDbCheckpointsCreated_(metrics_.RegisterCounter("numOfDbCheckpointsCreated", 0)) {
161154
metrics_.Register();
162155
}
163156
void init();
164-
Status createDbCheckpoint(const DbCheckpointId& checkPointId,
165-
const BlockId& lastBlockId,
166-
const SeqNum& seqNum,
167-
const std::optional<Timestamp>& timestamp);
157+
Status createDbCheckpoint(const DbCheckpointId& checkPointId, const BlockId& lastBlockId, const SeqNum& seqNum);
168158
void removeCheckpoint(const DbCheckpointId& checkPointId);
169159
void removeAllCheckpoints() const;
170160
void cleanUp();
@@ -179,11 +169,9 @@ class DbCheckpointManager {
179169
void checkAndRemove();
180170
void removeOldestDbCheckpoint();
181171
void updateDbCheckpointMetadata();
182-
void updateLastCmdInfo(const SeqNum&, const std::optional<Timestamp>&);
183172
void removeDbCheckpointFuture(CheckpointId);
184173
void builMetadataFromFileSystem();
185174
void updateMetrics();
186-
InternalBftClient* client_{nullptr};
187175
std::atomic<bool> stopped_ = false;
188176
DbCheckpointMetadata dbCheckptMetadata_;
189177
std::map<CheckpointId, std::future<void>> dbCreateCheckPtFuture_;
@@ -203,9 +191,8 @@ class DbCheckpointManager {
203191
SeqNum lastCheckpointSeqNum_{0};
204192
std::optional<DbCheckpointMetadata::DbCheckPointDescriptor> lastCreatedCheckpointMetadata_{std::nullopt};
205193
std::optional<SeqNum> nextSeqNumToCreateCheckPt_{std::nullopt};
206-
std::chrono::seconds lastCheckpointCreationTime_{duration_cast<Seconds>(SystemClock::now().time_since_epoch())};
194+
std::chrono::seconds lastCheckpointCreationTime_;
207195
std::function<void(SeqNum)> onStableSeqNumCb_;
208-
std::function<SeqNum()> getLastStableSeqNumCb_;
209196
std::vector<std::function<void(SeqNum)>> onDbCheckpointCreated_;
210197
std::string dbCheckPointDirPath_;
211198
bool isMetadataErased_ = false;
@@ -214,6 +201,12 @@ class DbCheckpointManager {
214201
concordMetrics::GaugeHandle lastDbCheckpointSizeInMb_;
215202
concordMetrics::GaugeHandle lastDbCheckpointBlockId_;
216203
concordMetrics::CounterHandle numOfDbCheckpointsCreated_;
204+
205+
// The db checkpoint is created as a local decision in the replica once several conditions are met. Hence we need to
206+
// share the knowledge about the latest created db checkpoint such that a recovered replica will able to make the same
207+
// decision as all other replicas.
208+
std::string page_;
209+
DbLastCheckpointMetadata shared_metadata_;
217210
};
218211

219212
} // namespace bftEngine::impl

bftengine/include/bftengine/DbCheckpointMetadata.hpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ using CheckpointId = uint64_t;
2424
using TimeDuration = std::chrono::duration<long>;
2525
using SeqNum = bftEngine::impl::SeqNum;
2626

27+
// The data about the latest checkpoint is a shared data, hence it is shared via the reserved pages
28+
struct DbLastCheckpointMetadata : public concord::serialize::SerializableFactory<DbLastCheckpointMetadata> {
29+
uint64_t lastCmdSeqNum_{0};
30+
TimeDuration lastCmdTimestamp_{0};
31+
32+
void serializeDataMembers(std::ostream& outStream) const override {
33+
serialize(outStream, lastCmdSeqNum_);
34+
serialize(outStream, lastCmdTimestamp_);
35+
}
36+
void deserializeDataMembers(std::istream& inStream) override {
37+
deserialize(inStream, lastCmdSeqNum_);
38+
deserialize(inStream, lastCmdTimestamp_);
39+
}
40+
};
2741
struct DbCheckpointMetadata : public concord::serialize::SerializableFactory<DbCheckpointMetadata> {
2842
struct DbCheckPointDescriptor : public concord::serialize::SerializableFactory<DbCheckPointDescriptor> {
2943
// unique id for the checkpoint
@@ -54,21 +68,10 @@ struct DbCheckpointMetadata : public concord::serialize::SerializableFactory<DbC
5468
}
5569
};
5670
std::map<CheckpointId, DbCheckPointDescriptor> dbCheckPoints_;
57-
uint64_t lastCmdSeqNum_{0};
58-
TimeDuration lastCmdTimestamp_{0};
5971
DbCheckpointMetadata() = default;
60-
void serializeDataMembers(std::ostream& outStream) const override {
61-
serialize(outStream, lastCmdSeqNum_);
62-
serialize(outStream, lastCmdTimestamp_);
63-
serialize(outStream, dbCheckPoints_);
64-
}
65-
void deserializeDataMembers(std::istream& inStream) override {
66-
deserialize(inStream, lastCmdSeqNum_);
67-
deserialize(inStream, lastCmdTimestamp_);
68-
deserialize(inStream, dbCheckPoints_);
69-
}
72+
void serializeDataMembers(std::ostream& outStream) const override { serialize(outStream, dbCheckPoints_); }
73+
void deserializeDataMembers(std::istream& inStream) override { deserialize(inStream, dbCheckPoints_); }
7074
};
7175
constexpr size_t DB_CHECKPOINT_METADATA_MAX_SIZE{
72-
MAX_ALLOWED_CHECKPOINTS * sizeof(std::pair<CheckpointId, DbCheckpointMetadata::DbCheckPointDescriptor>) +
73-
sizeof(DbCheckpointMetadata::lastCmdSeqNum_) + sizeof(DbCheckpointMetadata::lastCmdTimestamp_)};
76+
MAX_ALLOWED_CHECKPOINTS * sizeof(std::pair<CheckpointId, DbCheckpointMetadata::DbCheckPointDescriptor>)};
7477
} // namespace bftEngine::impl

bftengine/include/bftengine/Replica.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ enum MsgFlag : uint64_t {
5454
INTERNAL_FLAG = 0x40,
5555
PUBLISH_ON_CHAIN_OBJECT_FLAG = 0x80,
5656
CLIENTS_PUB_KEYS_FLAG = 0x100,
57-
DB_CHECKPOINT_FLAG = 0x200,
57+
NOOP_FLAG = 0x200,
5858
PRIMARY_ONLY_FLAG = 0x401
5959
};
6060

bftengine/src/bftengine/DbCheckpointManager.cpp

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ struct HumanReadable {
3939
};
4040
Status DbCheckpointManager::createDbCheckpoint(const CheckpointId& checkPointId,
4141
const BlockId& lastBlockId,
42-
const SeqNum& seqNum,
43-
const std::optional<Timestamp>& timestamp) {
42+
const SeqNum& seqNum) {
4443
ConcordAssert(dbClient_.get() != nullptr);
4544
ConcordAssert(ps_.get() != nullptr);
4645
{
@@ -105,12 +104,10 @@ void DbCheckpointManager::loadCheckpointDataFromPersistence() {
105104
}
106105
if (auto it = dbCheckptMetadata_.dbCheckPoints_.rbegin(); it != dbCheckptMetadata_.dbCheckPoints_.rend()) {
107106
lastCreatedCheckpointMetadata_ = it->second;
108-
lastCheckpointSeqNum_ = it->second.lastDbCheckpointSeqNum_;
109107
lastDbCheckpointBlockId_.Get().Set(it->second.lastBlockId_);
110108
metrics_.UpdateAggregator();
111109
LOG_INFO(getLogger(), KVLOG(lastCheckpointSeqNum_));
112110
}
113-
lastCheckpointCreationTime_ = dbCheckptMetadata_.lastCmdTimestamp_;
114111
}
115112
}
116113

@@ -177,6 +174,27 @@ uint64_t DbCheckpointManager::directorySize(const _fs::path& directory, const bo
177174
}
178175
return size;
179176
}
177+
void DbCheckpointManager::saveDbMetadataToReservedPages(SeqNum sn, std::chrono::seconds timestamp) {
178+
shared_metadata_.lastCmdSeqNum_ = sn;
179+
shared_metadata_.lastCmdTimestamp_ = timestamp;
180+
std::ostringstream outStream;
181+
concord::serialize::Serializable::serialize(outStream, shared_metadata_);
182+
auto data = outStream.str();
183+
saveReservedPage(0, data.size(), data.data());
184+
lastCheckpointCreationTime_ = shared_metadata_.lastCmdTimestamp_;
185+
}
186+
187+
void DbCheckpointManager::loadDbMetadataFromReservedPages() {
188+
if (!loadReservedPage(0, sizeOfReservedPage(), page_.data())) {
189+
LOG_WARN(getLogger(), "unable to read db checkpoint shared metadata from reserved pages");
190+
return;
191+
}
192+
std::istringstream inStream;
193+
inStream.str(page_);
194+
concord::serialize::Serializable::deserialize(inStream, shared_metadata_);
195+
lastCheckpointCreationTime_ = shared_metadata_.lastCmdTimestamp_;
196+
lastCheckpointSeqNum_ = shared_metadata_.lastCmdSeqNum_;
197+
}
180198

181199
void DbCheckpointManager::initializeDbCheckpointManager(
182200
std::shared_ptr<concord::storage::IDBClient> dbClient,
@@ -195,12 +213,9 @@ void DbCheckpointManager::initializeDbCheckpointManager(
195213
if (getLastBlockIdCb) getLastBlockIdCb_ = getLastBlockIdCb;
196214
if (checkpointInProcessCb) checkpointInProcessCb_ = checkpointInProcessCb;
197215
prepareCheckpointCb_ = prepareCheckpointCb;
216+
page_.resize(sizeOfReservedPage());
217+
loadDbMetadataFromReservedPages();
198218
if (ReplicaConfig::instance().dbCheckpointFeatureEnabled) {
199-
// in case of upgrade, we need to set the lastStableCheckpointSeqNum from persistence
200-
// instead of 0. if there is a db checkpoint metatdata present in persistence
201-
// then this gets overriden by the lastCheckpointSeqNum loaded from persistence
202-
if (getLastStableSeqNumCb_) lastCheckpointSeqNum_ = getLastStableSeqNumCb_();
203-
204219
init();
205220
} else {
206221
// db checkpoint is disabled. Cleanup metadata and checkpoints created if any
@@ -232,18 +247,11 @@ DbCheckpointManager::CheckpointState DbCheckpointManager::getCheckpointState(Che
232247
}
233248

234249
std::optional<CheckpointId> DbCheckpointManager::createDbCheckpointAsync(const SeqNum& seqNum,
235-
const std::optional<Timestamp>& timestamp,
236250
const std::optional<DbCheckpointId>& blockId) {
237251
if (seqNum <= lastCheckpointSeqNum_) {
238252
LOG_ERROR(getLogger(), "createDb checkpoint failed." << KVLOG(seqNum, lastCheckpointSeqNum_));
239253
return std::nullopt;
240254
}
241-
// We can have some replicas configured to not create db checkpoint. This is because,
242-
// backup functionality can be supported with a minimum of (f+1) replicas also
243-
// However, we need to store the last request seqNum and timestamp because, all replicas do consensus
244-
// to start the command execution at some seqNum but only replicas with non-zero numOfDbCheckpoints config
245-
// actually creates db checkpoint. Hence, primary needs to know what was the last cmd seqNum and timestamp
246-
updateLastCmdInfo(seqNum, timestamp);
247255
if (ReplicaConfig::instance().getmaxNumberOfDbCheckpoints() == 0) {
248256
LOG_WARN(getLogger(),
249257
"createDb checkpoint failed. Max allowed db checkpoint is configured to "
@@ -271,8 +279,8 @@ std::optional<CheckpointId> DbCheckpointManager::createDbCheckpointAsync(const S
271279
if (!blockId.has_value()) {
272280
DbCheckpointManager::instance().setCheckpointInProcess(true, lastBlockid);
273281
}
274-
auto ret = std::async(std::launch::async, [this, seqNum, timestamp, lastBlockid]() -> void {
275-
createDbCheckpoint(lastBlockid, lastBlockid, seqNum, timestamp);
282+
auto ret = std::async(std::launch::async, [this, seqNum, lastBlockid]() -> void {
283+
createDbCheckpoint(lastBlockid, lastBlockid, seqNum);
276284
});
277285
// store the future for the async task
278286
dbCreateCheckPtFuture_.emplace(std::make_pair(lastBlockid, std::move(ret)));
@@ -335,23 +343,6 @@ void DbCheckpointManager::updateDbCheckpointMetadata() {
335343
std::vector<uint8_t> v(data.begin(), data.end());
336344
ps_->setDbCheckpointMetadata(v);
337345
}
338-
void DbCheckpointManager::sendInternalCreateDbCheckpointMsg(const SeqNum& seqNum, bool noop) {
339-
auto replica_id = bftEngine::ReplicaConfig::instance().getreplicaId();
340-
concord::messages::db_checkpoint_msg::CreateDbCheckpoint req;
341-
req.sender = replica_id;
342-
req.seqNum = seqNum;
343-
req.noop = noop;
344-
std::vector<uint8_t> data_vec;
345-
concord::messages::db_checkpoint_msg::serialize(data_vec, req);
346-
std::string sig(SigManager::instance()->getMySigLength(), '\0');
347-
SigManager::instance()->sign(reinterpret_cast<char*>(data_vec.data()), data_vec.size(), sig.data());
348-
req.signature = std::vector<uint8_t>(sig.begin(), sig.end());
349-
data_vec.clear();
350-
concord::messages::db_checkpoint_msg::serialize(data_vec, req);
351-
std::string strMsg(data_vec.begin(), data_vec.end());
352-
std::string cid = "replicaDbCheckpoint_" + std::to_string(seqNum) + "_" + std::to_string(replica_id);
353-
if (client_) client_->sendRequest(bftEngine::DB_CHECKPOINT_FLAG, strMsg.size(), strMsg.c_str(), cid);
354-
}
355346

356347
void DbCheckpointManager::setNextStableSeqNumToCreateSnapshot(const std::optional<SeqNum>& seqNum) {
357348
if (seqNum == std::nullopt) {
@@ -376,24 +367,6 @@ void DbCheckpointManager::updateMetrics() {
376367
LOG_DEBUG(GL, "-- RocksDb checkpoint metrics dump--" + metrics_.ToJson());
377368
}
378369
}
379-
void DbCheckpointManager::updateLastCmdInfo(const SeqNum& seqNum, const std::optional<Timestamp>& timestamp) {
380-
dbCheckptMetadata_.lastCmdSeqNum_ = seqNum;
381-
if (timestamp.has_value()) {
382-
dbCheckptMetadata_.lastCmdTimestamp_ = std::chrono::duration_cast<Seconds>(timestamp.value().time_since_epoch);
383-
} else {
384-
dbCheckptMetadata_.lastCmdTimestamp_ = std::chrono::duration_cast<Seconds>(SystemClock::now().time_since_epoch());
385-
}
386-
lastCheckpointCreationTime_ = dbCheckptMetadata_.lastCmdTimestamp_;
387-
if (maxNumOfCheckpoints_ == 0) {
388-
// update lastCmdSeqNum and timestamp
389-
std::scoped_lock lock(lock_);
390-
updateDbCheckpointMetadata();
391-
}
392-
}
393-
SeqNum DbCheckpointManager::getLastStableSeqNum() const {
394-
if (getLastStableSeqNumCb_) return getLastStableSeqNumCb_();
395-
return 0;
396-
}
397370

398371
void DbCheckpointManager::setCheckpointInProcess(bool flag, concord::kvbc::BlockId blockId) const {
399372
if (checkpointInProcessCb_) checkpointInProcessCb_(flag, blockId);
@@ -479,8 +452,6 @@ void DbCheckpointManager::builMetadataFromFileSystem() {
479452

480453
if (auto it = dbCheckptMetadata_.dbCheckPoints_.rbegin(); it != dbCheckptMetadata_.dbCheckPoints_.rend()) {
481454
lastCreatedCheckpointMetadata_ = it->second;
482-
dbCheckptMetadata_.lastCmdTimestamp_ = it->second.creationTimeSinceEpoch_;
483-
lastCheckpointCreationTime_ = dbCheckptMetadata_.lastCmdTimestamp_;
484455
lastDbCheckpointBlockId_.Get().Set(it->second.lastBlockId_);
485456
metrics_.UpdateAggregator();
486457
updateMetrics();

bftengine/src/bftengine/Reconfiguration.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ void ReconfigurationHandler::handleWedgeCommands(bool bft_support,
165165
// so, we create a snapshot of the database, when we stop the replicas
166166
void ReconfigurationHandler::addCreateDbSnapshotCbOnWedge(bool bft_support) {
167167
auto wedgePt = bftEngine::ControlStateManager::instance().getCheckpointToStopAt();
168+
// We don't save the shared data about the creation time and sequence number of the db checkpoint because this data is
169+
// being saved only as part of the automated flow of creating a db checkpoint once in a period.
168170
if (wedgePt.has_value()) {
169171
if (bft_support) {
170172
bftEngine::IControlHandler::instance()->addOnStableCheckpointCallBack(

0 commit comments

Comments
 (0)