Files
rippled/include/xrpl/rdb/DatabaseCon.h
2026-02-19 23:30:00 +00:00

235 lines
6.3 KiB
C++

#pragma once
#include <xrpl/core/PerfLog.h>
#include <xrpl/core/StartUpType.h>
#include <xrpl/rdb/DBInit.h>
#include <xrpl/rdb/SociDB.h>
#include <boost/filesystem/path.hpp>
#include <mutex>
#include <optional>
#include <string>
namespace soci {
class session;
}
namespace xrpl {
class LockedSociSession
{
public:
using mutex = std::recursive_mutex;
private:
std::shared_ptr<soci::session> session_;
std::unique_lock<mutex> lock_;
public:
LockedSociSession(std::shared_ptr<soci::session> it, mutex& m)
: session_(std::move(it)), lock_(m)
{
}
LockedSociSession(LockedSociSession&& rhs) noexcept
: session_(std::move(rhs.session_)), lock_(std::move(rhs.lock_))
{
}
LockedSociSession() = delete;
LockedSociSession(LockedSociSession const& rhs) = delete;
LockedSociSession&
operator=(LockedSociSession const& rhs) = delete;
soci::session*
get()
{
return session_.get();
}
soci::session&
operator*()
{
return *session_;
}
soci::session*
operator->()
{
return session_.get();
}
explicit
operator bool() const
{
return bool(session_);
}
};
class DatabaseCon
{
public:
struct Setup
{
explicit Setup() = default;
StartUpType startUp = StartUpType::NORMAL;
bool standAlone = false;
boost::filesystem::path dataDir;
// Indicates whether or not to return the `globalPragma`
// from commonPragma()
bool useGlobalPragma = false;
std::vector<std::string> const*
commonPragma() const
{
XRPL_ASSERT(
!useGlobalPragma || globalPragma,
"xrpl::DatabaseCon::Setup::commonPragma : consistent global "
"pragma");
return useGlobalPragma && globalPragma ? globalPragma.get() : nullptr;
}
static std::unique_ptr<std::vector<std::string> const> globalPragma;
std::array<std::string, 4> txPragma;
std::array<std::string, 1> lgrPragma;
};
struct CheckpointerSetup
{
JobQueue* jobQueue;
Logs* logs;
};
template <std::size_t N, std::size_t M>
DatabaseCon(
Setup const& setup,
std::string const& dbName,
std::array<std::string, N> const& pragma,
std::array<char const*, M> const& initSQL,
beast::Journal journal)
// Use temporary files or regular DB files?
: DatabaseCon(
setup.standAlone && setup.startUp != StartUpType::LOAD &&
setup.startUp != StartUpType::LOAD_FILE &&
setup.startUp != StartUpType::REPLAY
? ""
: (setup.dataDir / dbName),
setup.commonPragma(),
pragma,
initSQL,
journal)
{
}
// Use this constructor to setup checkpointing
template <std::size_t N, std::size_t M>
DatabaseCon(
Setup const& setup,
std::string const& dbName,
std::array<std::string, N> const& pragma,
std::array<char const*, M> const& initSQL,
CheckpointerSetup const& checkpointerSetup,
beast::Journal journal)
: DatabaseCon(setup, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
}
template <std::size_t N, std::size_t M>
DatabaseCon(
boost::filesystem::path const& dataDir,
std::string const& dbName,
std::array<std::string, N> const& pragma,
std::array<char const*, M> const& initSQL,
beast::Journal journal)
: DatabaseCon(dataDir / dbName, nullptr, pragma, initSQL, journal)
{
}
// Use this constructor to setup checkpointing
template <std::size_t N, std::size_t M>
DatabaseCon(
boost::filesystem::path const& dataDir,
std::string const& dbName,
std::array<std::string, N> const& pragma,
std::array<char const*, M> const& initSQL,
CheckpointerSetup const& checkpointerSetup,
beast::Journal journal)
: DatabaseCon(dataDir, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
}
~DatabaseCon();
soci::session&
getSession()
{
return *session_;
}
LockedSociSession
checkoutDb()
{
using namespace std::chrono_literals;
LockedSociSession session = perf::measureDurationAndLog(
[&]() { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_);
return session;
}
private:
void
setupCheckpointing(JobQueue*, Logs&);
template <std::size_t N, std::size_t M>
DatabaseCon(
boost::filesystem::path const& pPath,
std::vector<std::string> const* commonPragma,
std::array<std::string, N> const& pragma,
std::array<char const*, M> const& initSQL,
beast::Journal journal)
: session_(std::make_shared<soci::session>()), j_(journal)
{
open(*session_, "sqlite", pPath.string());
for (auto const& p : pragma)
{
soci::statement st = session_->prepare << p;
st.execute(true);
}
if (commonPragma)
{
for (auto const& p : *commonPragma)
{
soci::statement st = session_->prepare << p;
st.execute(true);
}
}
for (auto const& sql : initSQL)
{
soci::statement st = session_->prepare << sql;
st.execute(true);
}
}
LockedSociSession::mutex lock_;
// checkpointer may outlive the DatabaseCon when the checkpointer jobQueue
// callback locks a weak pointer and the DatabaseCon is then destroyed. In
// this case, the checkpointer needs to make sure it doesn't use an already
// destroyed session. Thus this class keeps a shared_ptr to the session (so
// the checkpointer can keep a weak_ptr) and the checkpointer is a
// shared_ptr in this class. session_ will never be null.
std::shared_ptr<soci::session> const session_;
std::shared_ptr<Checkpointer> checkpointer_;
beast::Journal const j_;
};
// Return the checkpointer from its id. If the checkpointer no longer exists, an
// nullptr is returned
std::shared_ptr<Checkpointer>
checkpointerFromId(std::uintptr_t id);
} // namespace xrpl