1
0
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:
Zygo Blaxell
2018-01-15 23:07:12 -05:00
parent 055c8d4c75
commit 56c23c4517
3 changed files with 118 additions and 57 deletions

View File

@ -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();
}