mirror of
https://github.com/Zygo/bees.git
synced 2025-07-02 08:42:27 +02:00
crawl: implement two crawler algorithms and adjust scheduling parameters
There are two subvol scan algorithms implemented so far. The two modes are unimaginatively named 0 and 1. 0: sorts extents by (inode, subvol, offset), 1: scans extents round-robin from all subvols. Algorithm 0 scans references to the same extent at close to the same time, which is good for performance; however, whenever a snapshot is created, the scan of the entire filesystem restarts at the beginning of the new snapshot. Algorithm 1 makes continuous forward progress even when new snapshots are created, but it does not benefit from caching and will force the kernel to reread data multiple times when there are snapshots. The algorithm can be selected at run-time using the -m or --scan-mode option. We can collect some field data on these before replacing them with an extent-tree-based scanner. Alternatively, for pre-4.14 kernels, we can keep these two modes as non-default options. Currently these algorithms have terrible names. TODO: fix that, but also TODO: delete all that code and do scans directly from the extent tree instead. Augment the scan algorithms relative to their earlier implementation by batching multiple extents to scan from each subvol before switching to a different subvol. Sprinkle some BEESNOTEs on the Task objects so that they don't disappear from the thread status output. Adjust some timing constants to deal with the increased latency from competing threads. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
#include "bees.h"
|
||||
|
||||
#include "crucible/cache.h"
|
||||
#include "crucible/ntoa.h"
|
||||
#include "crucible/string.h"
|
||||
#include "crucible/task.h"
|
||||
|
||||
@ -10,6 +11,8 @@
|
||||
using namespace crucible;
|
||||
using namespace std;
|
||||
|
||||
BeesRoots::ScanMode BeesRoots::s_scan_mode = BeesRoots::SCAN_MODE_ZERO;
|
||||
|
||||
string
|
||||
format_time(time_t t)
|
||||
{
|
||||
@ -47,6 +50,26 @@ BeesCrawlState::operator<(const BeesCrawlState &that) const
|
||||
< tie(that.m_objectid, that.m_offset, that.m_root, that.m_min_transid, that.m_max_transid);
|
||||
}
|
||||
|
||||
string
|
||||
BeesRoots::scan_mode_ntoa(BeesRoots::ScanMode mode)
|
||||
{
|
||||
static const bits_ntoa_table table[] = {
|
||||
NTOA_TABLE_ENTRY_ENUM(SCAN_MODE_ZERO),
|
||||
NTOA_TABLE_ENTRY_ENUM(SCAN_MODE_ONE),
|
||||
NTOA_TABLE_ENTRY_ENUM(SCAN_MODE_COUNT),
|
||||
NTOA_TABLE_ENTRY_END()
|
||||
};
|
||||
return bits_ntoa(mode, table);
|
||||
}
|
||||
|
||||
void
|
||||
BeesRoots::set_scan_mode(ScanMode mode)
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, mode, mode < SCAN_MODE_COUNT);
|
||||
s_scan_mode = mode;
|
||||
BEESLOG("Scan mode set to " << mode << " (" << scan_mode_ntoa(mode) << ")");
|
||||
}
|
||||
|
||||
string
|
||||
BeesRoots::crawl_state_filename() const
|
||||
{
|
||||
@ -209,59 +232,75 @@ BeesRoots::crawl_roots()
|
||||
|
||||
auto ctx_copy = m_ctx;
|
||||
|
||||
#if 0
|
||||
// Scan the same inode/offset tuple in each subvol (good for snapshots)
|
||||
BeesFileRange first_range;
|
||||
shared_ptr<BeesCrawl> first_crawl;
|
||||
for (auto i : crawl_map_copy) {
|
||||
auto this_crawl = i.second;
|
||||
auto this_range = this_crawl->peek_front();
|
||||
if (this_range) {
|
||||
if (!first_range || this_range < first_range) {
|
||||
first_crawl = this_crawl;
|
||||
first_range = this_range;
|
||||
switch (s_scan_mode) {
|
||||
case SCAN_MODE_ZERO: {
|
||||
// Scan the same inode/offset tuple in each subvol (good for snapshots)
|
||||
BeesFileRange first_range;
|
||||
shared_ptr<BeesCrawl> first_crawl;
|
||||
for (auto i : crawl_map_copy) {
|
||||
auto this_crawl = i.second;
|
||||
auto this_range = this_crawl->peek_front();
|
||||
if (this_range) {
|
||||
if (!first_range || this_range < first_range) {
|
||||
first_crawl = this_crawl;
|
||||
first_range = this_range;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_range) {
|
||||
Task([ctx_copy, first_range]() {
|
||||
// BEESINFO("scan_forward " << first_range);
|
||||
ctx_copy->scan_forward(first_range);
|
||||
},
|
||||
[first_range](ostream &os) -> ostream & {
|
||||
return os << "scan_forward " << first_range;
|
||||
}).run();
|
||||
BEESCOUNT(crawl_scan);
|
||||
m_crawl_current = first_crawl->get_state();
|
||||
auto first_range_popped = first_crawl->pop_front();
|
||||
THROW_CHECK2(runtime_error, first_range, first_range_popped, first_range == first_range_popped);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Scan each subvol one extent at a time (good for continuous forward progress)
|
||||
bool crawled = false;
|
||||
for (auto i : crawl_map_copy) {
|
||||
auto this_crawl = i.second;
|
||||
auto this_range = this_crawl->peek_front();
|
||||
if (this_range) {
|
||||
Task([ctx_copy, this_range]() {
|
||||
// BEESINFO("scan_forward " << this_range);
|
||||
ctx_copy->scan_forward(this_range);
|
||||
},
|
||||
[this_range](ostream &os) -> ostream & {
|
||||
return os << "scan_forward " << this_range;
|
||||
}).run();
|
||||
crawled = true;
|
||||
BEESCOUNT(crawl_scan);
|
||||
m_crawl_current = this_crawl->get_state();
|
||||
auto this_range_popped = this_crawl->pop_front();
|
||||
THROW_CHECK2(runtime_error, this_range, this_range_popped, this_range == this_range_popped);
|
||||
}
|
||||
}
|
||||
size_t batch_count = 0;
|
||||
while (first_range && batch_count < BEES_MAX_CRAWL_BATCH) {
|
||||
Task([ctx_copy, first_range]() {
|
||||
BEESNOTE("scan_forward " << first_range);
|
||||
ctx_copy->scan_forward(first_range);
|
||||
},
|
||||
[first_range](ostream &os) -> ostream & {
|
||||
return os << "scan_forward " << first_range;
|
||||
}).run();
|
||||
BEESCOUNT(crawl_scan);
|
||||
m_crawl_current = first_crawl->get_state();
|
||||
auto first_range_popped = first_crawl->pop_front();
|
||||
THROW_CHECK2(runtime_error, first_range, first_range_popped, first_range == first_range_popped);
|
||||
first_range = first_crawl->peek_front();
|
||||
++batch_count;
|
||||
}
|
||||
|
||||
if (crawled) return;
|
||||
#endif
|
||||
if (first_range || batch_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SCAN_MODE_ONE: {
|
||||
// Scan each subvol one extent at a time (good for continuous forward progress)
|
||||
bool crawled = false;
|
||||
for (auto i : crawl_map_copy) {
|
||||
auto this_crawl = i.second;
|
||||
auto this_range = this_crawl->peek_front();
|
||||
size_t batch_count = 0;
|
||||
while (this_range && batch_count < BEES_MAX_CRAWL_BATCH) {
|
||||
Task([ctx_copy, this_range]() {
|
||||
BEESNOTE("scan_forward " << this_range);
|
||||
ctx_copy->scan_forward(this_range);
|
||||
},
|
||||
[this_range](ostream &os) -> ostream & {
|
||||
return os << "scan_forward " << this_range;
|
||||
}).run();
|
||||
crawled = true;
|
||||
BEESCOUNT(crawl_scan);
|
||||
m_crawl_current = this_crawl->get_state();
|
||||
auto this_range_popped = this_crawl->pop_front();
|
||||
THROW_CHECK2(runtime_error, this_range, this_range_popped, this_range == this_range_popped);
|
||||
this_range = this_crawl->peek_front();
|
||||
++batch_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (crawled) return;
|
||||
break;
|
||||
}
|
||||
case SCAN_MODE_COUNT: assert(false); break;
|
||||
}
|
||||
|
||||
BEESLOG("Crawl ran out of data after " << m_crawl_timer.lap() << "s, waiting for more...");
|
||||
BEESCOUNT(crawl_done);
|
||||
@ -283,14 +322,13 @@ BeesRoots::crawl_thread()
|
||||
auto shared_this = shared_from_this();
|
||||
Task([shared_this]() {
|
||||
auto tqs = TaskMaster::get_queue_count();
|
||||
BEESNOTE("queueing extents to scan, " << tqs << " of " << BEES_MAX_QUEUE_SIZE);
|
||||
while (tqs < BEES_MAX_QUEUE_SIZE) {
|
||||
// BEESLOG("Task queue size " << tqs << ", crawling...");
|
||||
catch_all([&]() {
|
||||
shared_this->crawl_roots();
|
||||
});
|
||||
tqs = TaskMaster::get_queue_count();
|
||||
}
|
||||
BEESLOG("Task queue size " << tqs << ", paused");
|
||||
Task::current_task().run();
|
||||
}, [](ostream &os) -> ostream& { return os << "crawl task"; }).run();
|
||||
}
|
||||
|
Reference in New Issue
Block a user