diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -238,53 +238,51 @@ } } -extern "C" void *ThreadDumper(void *) { - int count = 0; - do { - // First 100s, than 200s, 400s, 800s, 1600s, and then 3200s forever - UninterruptibleSleep(std::chrono::seconds(100 << count)); - if (count < 5) { - count++; - } +extern "C" void *ThreadDumper(void *data) { + assert(data); + const auto dumpInterval(*(const std::chrono::seconds *)data); - { - std::vector v = db.GetAll(); - sort(v.begin(), v.end(), StatCompare); - FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); - if (f) { - { - CAutoFile cf(f, SER_DISK, CLIENT_VERSION); - cf << db; - } - rename("dnsseed.dat.new", "dnsseed.dat"); + // First dump should occur no later than 10 seconds. Successive dumps will + // occur every dump interval. + UninterruptibleSleep(std::min(10s, dumpInterval)); + do { + std::vector v = db.GetAll(); + sort(v.begin(), v.end(), StatCompare); + FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); + if (f) { + { + CAutoFile cf(f, SER_DISK, CLIENT_VERSION); + cf << db; } - fsbridge::ofstream d{"dnsseed.dump"}; + rename("dnsseed.dat.new", "dnsseed.dat"); + } + fsbridge::ofstream d{"dnsseed.dump"}; + tfm::format(d, "# address good " + "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " + "%%(30d) blocks svcs version\n"); + double stat[5] = {0, 0, 0, 0, 0}; + for (CAddrReport rep : v) { tfm::format( - d, "# address good " - "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " - "%%(30d) blocks svcs version\n"); - double stat[5] = {0, 0, 0, 0, 0}; - for (CAddrReport rep : v) { - tfm::format( - d, - "%-47s %4d %11" PRId64 - " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 - " %5i \"%s\"\n", - rep.ip.ToString(), (int)rep.fGood, rep.lastSuccess, - 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], - 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], - 100.0 * rep.uptime[4], rep.blocks, rep.services, - rep.clientVersion, rep.clientSubVersion); - stat[0] += rep.uptime[0]; - stat[1] += rep.uptime[1]; - stat[2] += rep.uptime[2]; - stat[3] += rep.uptime[3]; - stat[4] += rep.uptime[4]; - } - fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app}; - tfm::format(ff, "%llu %g %g %g %g %g\n", GetTime(), stat[0], - stat[1], stat[2], stat[3], stat[4]); + d, + "%-47s %4d %11" PRId64 + " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 + " %5i \"%s\"\n", + rep.ip.ToString(), (int)rep.fGood, rep.lastSuccess, + 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], + 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], + 100.0 * rep.uptime[4], rep.blocks, rep.services, + rep.clientVersion, rep.clientSubVersion); + stat[0] += rep.uptime[0]; + stat[1] += rep.uptime[1]; + stat[2] += rep.uptime[2]; + stat[3] += rep.uptime[3]; + stat[4] += rep.uptime[4]; } + fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app}; + tfm::format(ff, "%llu %g %g %g %g %g\n", GetTime(), stat[0], stat[1], + stat[2], stat[3], stat[4]); + + UninterruptibleSleep(dumpInterval); } while (1); return nullptr; } @@ -445,7 +443,7 @@ pthread_attr_destroy(&attr_crawler); tfm::format(std::cout, "done\n"); pthread_create(&threadStats, nullptr, ThreadStats, nullptr); - pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); + pthread_create(&threadDump, nullptr, ThreadDumper, &opts.dumpInterval); void *res; pthread_join(threadDump, &res); return EXIT_SUCCESS; diff --git a/src/seeder/options.h b/src/seeder/options.h --- a/src/seeder/options.h +++ b/src/seeder/options.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_SEEDER_OPTIONS_H #define BITCOIN_SEEDER_OPTIONS_H +#include #include #include @@ -14,6 +15,7 @@ static const int CONTINUE_EXECUTION = -1; +static const int DEFAULT_DUMP_INTERVAL_SECONDS = 60 * 60; static const int DEFAULT_NUM_THREADS = 96; static const int DEFAULT_PORT = 53; static const int DEFAULT_NUM_DNS_THREADS = 4; @@ -30,6 +32,7 @@ class CDnsSeedOpts { public: ArgsManager *argsManager{nullptr}; + std::chrono::seconds dumpInterval; int nThreads; int nPort; int nDnsThreads; @@ -45,12 +48,14 @@ std::set filter_whitelist; CDnsSeedOpts(ArgsManager *argsMan) - : argsManager(argsMan), nThreads(DEFAULT_NUM_THREADS), - nPort(DEFAULT_PORT), nDnsThreads(DEFAULT_NUM_DNS_THREADS), - fWipeBan(DEFAULT_WIPE_BAN), fWipeIgnore(DEFAULT_WIPE_IGNORE), - mbox(DEFAULT_EMAIL), ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), - tor(DEFAULT_TOR_PROXY), ip_addr(DEFAULT_LISTEN_ADDRESS), - ipv4_proxy(DEFAULT_IPV4_PROXY), ipv6_proxy(DEFAULT_IPV6_PROXY) {} + : argsManager(argsMan), + dumpInterval(std::chrono::seconds(DEFAULT_DUMP_INTERVAL_SECONDS)), + nThreads(DEFAULT_NUM_THREADS), nPort(DEFAULT_PORT), + nDnsThreads(DEFAULT_NUM_DNS_THREADS), fWipeBan(DEFAULT_WIPE_BAN), + fWipeIgnore(DEFAULT_WIPE_IGNORE), mbox(DEFAULT_EMAIL), + ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), tor(DEFAULT_TOR_PROXY), + ip_addr(DEFAULT_LISTEN_ADDRESS), ipv4_proxy(DEFAULT_IPV4_PROXY), + ipv6_proxy(DEFAULT_IPV6_PROXY) {} int ParseCommandLine(int argc, const char **argv); diff --git a/src/seeder/options.cpp b/src/seeder/options.cpp --- a/src/seeder/options.cpp +++ b/src/seeder/options.cpp @@ -35,6 +35,15 @@ return EXIT_SUCCESS; } + dumpInterval = std::chrono::seconds( + argsManager->GetArg("-dumpinterval", DEFAULT_DUMP_INTERVAL_SECONDS)); + if (dumpInterval.count() <= 0) { + tfm::format( + std::cerr, + "Error: -dumpinterval argument expects only positive integers\n"); + return EXIT_FAILURE; + } + nThreads = argsManager->GetArg("-threads", DEFAULT_NUM_THREADS); if (nThreads <= 0) { tfm::format( @@ -117,6 +126,11 @@ argsManager->AddArg("-mbox=", "E-Mail address reported in SOA records", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsManager->AddArg( + "-dumpinterval=", + strprintf("Number of seconds between each database dump (default: %d)", + DEFAULT_DUMP_INTERVAL_SECONDS), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsManager->AddArg( "-threads=", strprintf("Number of crawlers to run in parallel (default: %d)", diff --git a/src/seeder/test/options_tests.cpp b/src/seeder/test/options_tests.cpp --- a/src/seeder/test/options_tests.cpp +++ b/src/seeder/test/options_tests.cpp @@ -24,6 +24,8 @@ BOOST_FIXTURE_TEST_CASE(options_defaults_test, ArgsTestingSetup) { const char *argv[] = {"ignored"}; BOOST_CHECK(opts.ParseCommandLine(1, argv) == seeder::CONTINUE_EXECUTION); + BOOST_CHECK(opts.dumpInterval == + std::chrono::seconds(seeder::DEFAULT_DUMP_INTERVAL_SECONDS)); BOOST_CHECK(opts.nPort == seeder::DEFAULT_PORT); BOOST_CHECK(opts.nThreads == seeder::DEFAULT_NUM_THREADS); BOOST_CHECK(opts.nDnsThreads == seeder::DEFAULT_NUM_DNS_THREADS); @@ -41,6 +43,26 @@ BOOST_CHECK(opts.nPort == 5555); } +BOOST_FIXTURE_TEST_CASE(options_dumpinterval_test, ArgsTestingSetup) { + const std::map expectedResults = { + {-9999, EXIT_FAILURE}, + {-1, EXIT_FAILURE}, + {0, EXIT_FAILURE}, + {1, seeder::CONTINUE_EXECUTION}, + {seeder::DEFAULT_DUMP_INTERVAL_SECONDS, seeder::CONTINUE_EXECUTION}, + {9999, seeder::CONTINUE_EXECUTION}}; + + for (const auto entry : expectedResults) { + const std::string testArg = "-dumpinterval=" + ToString(entry.first); + const char *argv[] = {"ignored", TEST_HOST, TEST_NAMESERVER, TEST_EMAIL, + testArg.c_str()}; + BOOST_CHECK(opts.ParseCommandLine(5, argv) == entry.second); + if (entry.second == seeder::CONTINUE_EXECUTION) { + BOOST_CHECK(opts.dumpInterval == std::chrono::seconds(entry.first)); + } + } +} + BOOST_FIXTURE_TEST_CASE(options_threads_test, ArgsTestingSetup) { const std::map expectedResults = { {-9999, EXIT_FAILURE},