20 #include <ripple/app/misc/NetworkOPs.h>
21 #include <ripple/basics/Archive.h>
22 #include <ripple/basics/BasicConfig.h>
23 #include <ripple/core/ConfigSections.h>
24 #include <ripple/nodestore/DatabaseShard.h>
25 #include <ripple/rpc/ShardArchiveHandler.h>
26 #include <ripple/rpc/impl/Handler.h>
33 using namespace boost::filesystem;
34 using namespace std::chrono_literals;
36 boost::filesystem::path
52 return std::make_unique<ShardArchiveHandler>(app, parent);
58 auto const downloadDir(getDownloadDirectory(app.
config()));
65 return std::make_unique<RecoveryHandler>(app, parent);
72 :
Stoppable(
"ShardArchiveHandler", parent)
75 , j_(app.journal(
"ShardArchiveHandler"))
76 , downloadDir_(getDownloadDirectory(app.config()))
77 , timer_(app_.getIOService())
78 , verificationScheduler_(
81 "shard_verification_retry_interval")),
85 "shard_verification_max_attempts"))
97 JLOG(
j_.
warn()) <<
"Archives already being processed";
118 sqliteDB_ = std::make_unique<DatabaseCon>(
127 <<
"exception: " << e.
what() <<
" in function: " << __func__;
141 using namespace boost::filesystem;
147 sqliteDB_ = std::make_unique<DatabaseCon>(
155 soci::rowset<soci::row> rs =
156 (session.prepare <<
"SELECT * FROM State;");
158 for (
auto it = rs.begin(); it != rs.end(); ++it)
165 <<
"Failed to parse url: " << it->get<
std::string>(1);
170 add(it->get<
int>(0), std::move(url), lock);
184 <<
" in function: " << __func__;
223 if (!
add(shardIndex, std::forward<parsedURL>(url.first), lock))
228 session <<
"INSERT INTO State VALUES (:index, :url);",
229 soci::use(shardIndex), soci::use(url.second);
242 JLOG(
j_.
error()) <<
"Download and import already in progress";
246 auto const it{
archives_.find(shardIndex)};
248 return url == it->second;
250 archives_.emplace(shardIndex, std::move(url));
261 JLOG(
j_.
error()) <<
"No shard store available";
266 JLOG(
j_.
warn()) <<
"Archives already being processed";
271 JLOG(
j_.
warn()) <<
"No archives to process";
279 shardIndexes.
begin(),
280 [](
auto const& entry) { return entry.first; });
326 auto const shardIndex{
archives_.begin()->first};
333 boost::optional<uint256> expectedHash;
334 bool shouldHaveHash =
false;
346 if (ec != boost::asio::error::operation_aborted)
355 "failed to wrap closure for last ledger confirmation timer", l);
359 JLOG(
j_.
error()) <<
"failed to find last ledger hash for shard "
360 << shardIndex <<
", maximum attempts reached";
372 create_directory(dstDir);
383 auto const& url{
archives_.begin()->second};
385 auto const ssl = (url.scheme ==
"https");
386 auto const defaultPort = ssl ? 443 : 80;
393 dstDir /
"archive.tar.lz4",
394 [
this](path dstPath) { complete(dstPath); },
404 "failed to wrap closure for starting download", l);
421 if (!is_regular_file(dstPath))
425 <<
"Downloading shard id " << ar->first <<
" from URL "
426 << ar->second.domain << ar->second.path;
446 auto const mode{
app_.
getOPs().getOperatingMode()};
456 [=, dstPath = std::move(dstPath)](
457 boost::system::error_code
const& ec)
mutable {
458 if (ec != boost::asio::error::operation_aborted)
464 "failed to wrap closure for operating mode timer",
467 timer_.async_wait(*wrapper);
482 JLOG(
j_.
error()) <<
"failed to wrap closure for process()";
501 auto const shardDir{dstPath.parent_path() /
std::to_string(shardIndex)};
508 if (!is_directory(shardDir))
510 JLOG(
j_.
error()) <<
"Shard " << shardIndex
511 <<
" mismatches archive shard directory";
524 JLOG(
j_.
error()) <<
"Importing shard " << shardIndex;
528 JLOG(
j_.
debug()) <<
"Shard " << shardIndex <<
" downloaded and imported";
536 auto const shardIndex{
archives_.begin()->first};
542 session <<
"DELETE FROM State WHERE ShardIndex = :index;",
543 soci::use(shardIndex);
567 session <<
"DROP TABLE State;";
580 <<
" in function: " << __func__;
static boost::filesystem::path getDownloadDirectory(Config const &config)
std::shared_ptr< DatabaseDownloader > downloader_
const boost::filesystem::path downloadDir_
void remove(std::lock_guard< std::mutex > const &)
TimerOpCounter timerCounter_
ShardVerificationScheduler verificationScheduler_
bool removeAndProceed(std::lock_guard< std::mutex > const &lock)
bool start()
Starts downloading and importing archives.
bool add(std::uint32_t shardIndex, std::pair< parsedURL, std::string > &&url)
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
LedgerIndex getValidLedgerIndex()
static std::string shardDatabase()
void onStop() override
Override called when the stop notification is issued.
virtual std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const =0
Calculates the last ledger sequence for a given shard index.
void doRelease(std::lock_guard< std::mutex > const &)
virtual NodeStore::DatabaseShard * getShardStore()=0
boost::optional< LedgerHash > walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason)
Walk to a ledger's hash using the skip list.
static constexpr std::array< char const *, 3 > ShardArchiveHandlerDBInit
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
bool initFromDB(std::lock_guard< std::mutex > const &)
bool onClosureFailed(std::string const &errorMsg, std::lock_guard< std::mutex > const &lock)
void extractTarLz4(boost::filesystem::path const &src, boost::filesystem::path const &dst)
Extract a tar archive compressed with lz4.
virtual NetworkOPs & getOPs()=0
static constexpr std::array< char const *, 2 > DownloaderDBPragma
boost::asio::basic_waitable_timer< std::chrono::steady_clock > timer_
Provides an interface for starting and stopping.
void process(boost::filesystem::path const &dstPath)
virtual LedgerMaster & getLedgerMaster()=0
virtual Config & config()=0
virtual JobQueue & getJobQueue()=0
bool parseUrl(parsedURL &pUrl, std::string const &strUrl)
std::unique_ptr< DatabaseCon > sqliteDB_
void complete(boost::filesystem::path dstPath)
bool retry(Application &app, bool shouldHaveHash, retryFunction f)
boost::optional< Wrapper< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
virtual bool prepareShards(std::vector< std::uint32_t > const &shardIndexes)=0
Prepare one or more shard indexes to be imported into the database.
static std::unique_ptr< ShardArchiveHandler > makeShardArchiveHandler(Application &app, Stoppable &parent)
RecoveryHandler(Application &app, Stoppable &parent)
virtual boost::asio::io_service & getIOService()=0
static std::unique_ptr< ShardArchiveHandler > tryMakeRecoveryHandler(Application &app, Stoppable &parent)
static constexpr auto stateDBName
virtual bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir)=0
Import a shard into the shard database.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool next(std::lock_guard< std::mutex > const &l)
std::map< std::uint32_t, parsedURL > archives_
Handles the download and import of one or more shard archives.
ShardArchiveHandler()=delete
void join(char const *name, std::chrono::milliseconds wait, beast::Journal j)
Returns once all counted in-flight closures are destroyed.
virtual void removePreShard(std::uint32_t shardIndex)=0
Remove a previously prepared shard index for import.
std::shared_ptr< DatabaseDownloader > make_DatabaseDownloader(boost::asio::io_service &io_service, Config const &config, beast::Journal j)
T & get(EitherAmount &amt)
Section & section(std::string const &name)
Returns the section with the given name.
bool isStopping() const
Returns true if the stoppable should stop.
@ FULL
we have the ledger and can even validate