8000 refactor: Change recursive_mutex to mutex in DatabaseRotatingImp by ximinez · Pull Request #5276 · XRPLF/rippled · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

refactor: Change recursive_mutex to mutex in DatabaseRotatingImp #5276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ce650ad
refactor: Change recursive_mutex to mutex in DatabaseRotatingImp
ximinez Aug 7, 2024
b8413ae
Review feedback from @Bronek:
ximinez Feb 5, 2025
063e881
Merge branch 'develop' into ximinez/db-lock
ximinez Feb 6, 2025
9f564bc
Merge remote-tracking branch 'upstream/develop' into ximinez/db-lock
ximinez Feb 7, 2025
d912b50
Review feedback from @bthomee and @vvysokikh1:
ximinez Feb 7, 2025
3f7fb66
Update levelization tracking
ximinez Feb 7, 2025
4de9be2
Merge remote-tracking branch 'upstream/develop' into ximinez/db-lock
ximinez Feb 8, 2025
3b6984c
fixup! Review feedback from @bthomee and @vvysokikh1:
ximinez Feb 8, 2025
8000
52b68da
Merge remote-tracking branch 'upstream/develop' into ximinez/db-lock
ximinez Feb 11, 2025
b90f65f
Rewrite DatabaseRotatingImp again:
ximinez Feb 12, 2025
925c311
Add another test case, use boost locks, update comments
ximinez Feb 12, 2025
217bc1d
Review feedback: rename readlock, add and update comments
ximinez Feb 12, 2025
46e35b8
Merge remote-tracking branch 'upstream/develop' into ximinez/db-lock
ximinez Feb 13, 2025
a2fa56e
Revert back to upstream/develop, except unit tests
ximinez Feb 13, 2025
a983cc9
Fix the unit test for original logic
ximinez Feb 13, 2025
542b958
refactor: Change recursive_mutex to mutex in DatabaseRotatingImp
ximinez Aug 7, 2024
27cef47
[FOLD] Clean up #include's
ximinez Feb 13, 2025
4986662
Make the `rotate()` internals const
ximinez Feb 13, 2025
01cf3c4
Update src/test/app/SHAMapStore_test.cpp
ximinez Feb 13, 2025
3994ae3
Revert "Make the `rotate()` internals const"
ximinez Feb 13, 2025
c88668e
Add comments, some const
ximinez Feb 13, 2025
0f032cc
Fix levelization (again?)
ximinez Feb 13, 2025
ba94c0e
fixup! Add comments, some const
ximinez Feb 13, 2025
a38d43d
Merge remote-tracking branch 'upstream/develop' into ximinez/db-lock
ximinez Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Builds/levelization/results/ordering.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test.app > xrpl.basics
test.app > xrpld.app
test.app > xrpld.core
test.app > xrpld.ledger
test.app > xrpld.nodestore
test.app > xrpld.overlay
test.app > xrpld.rpc
test.app > xrpl.json
Expand Down
126 changes: 126 additions & 0 deletions src/test/app/SHAMapStore_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#include <test/jtx.h>
#include <test/jtx/envconfig.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/app/main/NodeStoreScheduler.h>
#include <xrpld/app/misc/SHAMapStore.h>
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
#include <xrpld/core/ConfigSections.h>
#include <xrpld/nodestore/detail/DatabaseRotatingImp.h>
#include <xrpl/protocol/jss.h>

namespace ripple {
Expand Down Expand Up @@ -518,12 +520,136 @@ class SHAMapStore_test : public beast::unit_test::suite
lastRotated = ledgerSeq - 1;
}

std::unique_ptr<NodeStore::Backend>
makeBackendRotating(
jtx::Env& env,
NodeStoreScheduler& scheduler,
std::string path)
{
Section section{
env.app().config().section(ConfigSection::nodeDatabase())};
boost::filesystem::path newPath;

if (!BEAST_EXPECT(path.size()))
return {};
newPath = path;
section.set("path", newPath.string());

auto backend{NodeStore::Manager::instance().make_Backend(
section,
megabytes(env.app().config().getValueFor(
SizedItem::burstSize, std::nullopt)),
scheduler,
env.app().logs().journal("NodeStoreTest"))};
backend->open();
return backend;
}

void
testRotate()
{
// The only purpose of this test is to ensure that if something that
// should never happen happens, we don't get a deadlock.
testcase("rotate with lock contention");

using namespace jtx;
Env env(*this, envconfig(onlineDelete));

/////////////////////////////////////////////////////////////
// Create the backend. Normally, SHAMapStoreImp handles all these
// details
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());

// Provide default values:
if (!nscfg.exists("cache_size"))
nscfg.set(
"cache_size",
std::to_string(env.app().config().getValueFor(
SizedItem::treeCacheSize, std::nullopt)));

if (!nscfg.exists("cache_age"))
nscfg.set(
"cache_age",
std::to_string(env.app().config().getValueFor(
SizedItem::treeCacheAge, std::nullopt)));

NodeStoreScheduler scheduler(env.app().getJobQueue());

std::string const writableDb = "write";
std::string const archiveDb = "archive";
auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);

// Create NodeStore with two backends to allow online deletion of
// data
constexpr int readThreads = 4;
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
scheduler,
readThreads,
std::move(writableBackend),
std::move(archiveBackend),
nscfg,
env.app().logs().journal("NodeStoreTest"));

/////////////////////////////////////////////////////////////
// Check basic functionality
using namespace std::chrono_literals;
std::atomic<int> threadNum = 0;

{
auto newBackend = makeBackendRotating(
env, scheduler, std::to_string(++threadNum));

auto const cb = [&](std::string const& writableName,
std::string const& archiveName) {
BEAST_EXPECT(writableName == "1");
BEAST_EXPECT(archiveName == "write");
// Ensure that dbr functions can be called from within the
// callback
BEAST_EXPECT(dbr->getName() == "1");
};

dbr->rotate(std::move(newBackend), cb);
}
BEAST_EXPECT(threadNum == 1);
BEAST_EXPECT(dbr->getName() == "1");

/////////////////////////////////////////////////////////////
// Do something stupid. Try to re-enter rotate from inside the callback.
{
auto const cb = [&](std::string const& writableName,
std::string const& archiveName) {
BEAST_EXPECT(writableName == "3");
BEAST_EXPECT(archiveName == "2");
// Ensure that dbr functions can be called from within the
// callback
BEAST_EXPECT(dbr->getName() == "3");
};
auto const cbReentrant = [&](std::string const& writableName,
std::string const& archiveName) {
BEAST_EXPECT(writableName == "2");
BEAST_EXPECT(archiveName == "1");
auto newBackend = makeBackendRotating(
env, scheduler, std::to_string(++threadNum));
// Reminder: doing this is stupid and should never happen
dbr->rotate(std::move(newBackend), cb);
};
auto newBackend = makeBackendRotating(
env, scheduler, std::to_string(++threadNum));
dbr->rotate(std::move(newBackend), cbReentrant);
}

BEAST_EXPECT(threadNum == 3);
BEAST_EXPECT(dbr->getName() == "3");
}

void
run() override
{
testClear();
testAutomatic();
testCanDelete();
testRotate();
}
};

Expand Down
12 changes: 6 additions & 6 deletions src/xrpld/app/misc/SHAMapStoreImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,17 +366,17 @@ SHAMapStoreImp::run()

lastRotated = validatedSeq;

dbRotating_->rotateWithLock(
[&](std::string const& writableBackendName) {
dbRotating_->rotate(
std::move(newBackend),
[&](std::string const& writableName,
std::string const& archiveName) {
SavedState savedState;
savedState.writableDb = newBackend->getName();
savedState.archiveDb = writableBackendName;
savedState.writableDb = writableName;
savedState.archiveDb = archiveName;
savedState.lastRotated = lastRotated;
state_db_.setState(savedState);

clearCaches(validatedSeq);

return std::move(newBackend);
});

JLOG(journal_.warn()) << "finished rotation " << validatedSeq;
Expand Down
12 changes: 9 additions & 3 deletions src/xrpld/nodestore/DatabaseRotating.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,17 @@ class DatabaseRotating : public Database

/** Rotates the backends.

@param f A function executed before the rotation and under the same lock
@param newBackend New writable backend
@param f A function executed after the rotation outside of lock. The
values passed to f will be the new backend database names _after_
rotation.
*/
virtual void
rotateWithLock(std::function<std::unique_ptr<NodeStore::Backend>(
std::string const& writableBackendName)> const& f) = 0;
rotate(
std::unique_ptr<NodeStore::Backend>&& newBackend,
std::function<void(
std::string const& writableName,
std::string const& archiveName)> const& f) = 0;
};

} // namespace NodeStore
Expand Down
32 changes: 24 additions & 8 deletions src/xrpld/nodestore/detail/DatabaseRotatingImp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,32 @@ DatabaseRotatingImp::DatabaseRotatingImp(
}

void
DatabaseRotatingImp::rotateWithLock(
std::function<std::unique_ptr<NodeStore::Backend>(
std::string const& writableBackendName)> const& f)
DatabaseRotatingImp::rotate(
std::unique_ptr<NodeStore::Backend>&& newBackend,
std::function<void(
std::string const& writableName,
std::string const& archiveName)> const& f)
{
std::lock_guard lock(mutex_);
// Pass these two names to the callback function
std::string const newWritableBackendName = newBackend->getName();
std::string newArchiveBackendName;
// Hold on to current archive backend pointer until after the
// callback finishes. Only then will the archive directory be
// deleted.
std::shared_ptr<NodeStore::Backend> oldArchiveBackend;
{
std::lock_guard lock(mutex_);

archiveBackend_->setDeletePath();
oldArchiveBackend = std::move(archiveBackend_);

archiveBackend_ = std::move(writableBackend_);
newArchiveBackendName = archiveBackend_->getName();

writableBackend_ = std::move(newBackend);
}

auto newBackend = f(writableBackend_->getName());
archiveBackend_->setDeletePath();
archiveBackend_ = std::move(writableBackend_);
writableBackend_ = std::move(newBackend);
f(newWritableBackendName, newArchiveBackendName);
}

std::string
Expand Down
16 changes: 6 additions & 10 deletions src/xrpld/nodestore/detail/DatabaseRotatingImp.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ class DatabaseRotatingImp : public DatabaseRotating
}

void
rotateWithLock(
std::function<std::unique_ptr<NodeStore::Backend>(
std::string const& writableBackendName)> const& f) override;
rotate(
std::unique_ptr<NodeStore::Backend>&& newBackend,
std::function<void(
std::string const& writableName,
std::string const& archiveName)> const& f) override;

std::string
getName() const override;
Expand Down Expand Up @@ -82,13 +84,7 @@ class DatabaseRotatingImp : public DatabaseRotating
private:
std::shared_ptr<Backend> writableBackend_;
std::shared_ptr<Backend> archiveBackend_;
// This needs to be a recursive mutex because callbacks in `rotateWithLock`
// can call function that also lock the mutex. A current example of this is
// a callback from SHAMapStoreImp, which calls `clearCaches`. This
// `clearCaches` call eventually calls `fetchNodeObject` which tries to
// relock the mutex. It would be desirable to rewrite the code so the lock
// was not held during a callback.
mutable std::recursive_mutex mutex_;
mutable std::mutex mutex_;

std::shared_ptr<NodeObject>
fetchNodeObject(
Expand Down
Loading
0