namespace {
/// Rate-limited progress reporter for the long single-threaded hash table
-/// operations that run before the daemon's status file and worker threads
-/// exist. Loading or resizing a multi-gigabyte table takes minutes to
-/// tens of minutes, and BEESNOTE alone is invisible to an operator who is
-/// only watching the log. Emits at most one line per
-/// BEES_HASH_PROGRESS_INTERVAL seconds, plus one summary line at the end.
+/// operations that run at startup. Loading or resizing a multi-gigabyte
+/// table takes minutes to tens of minutes, and the log is the only channel
+/// available: bees.cc runs start_hash_writeback() (which loads and resizes)
+/// before BeesContext::start() creates the reporter thread, so BEESNOTE in
+/// this path publishes thread status that nothing will ever render. Emits
+/// at most one line per BEES_HASH_PROGRESS_INTERVAL seconds, plus one
+/// summary line at the end.
class BeesProgressLogger {
const string m_what;
Timer m_start;
void
BeesHashTable::open_file()
{
- // OK open hash table
- BEESNOTE("opening hash table '" << m_filename << "' target size " << m_size << " (" << pretty(m_size) << ")");
+ // Everything from here to the end of prepare() runs before the
+ // reporter thread exists (bees.cc calls start_hash_writeback() before
+ // BeesContext::start()), so BEESNOTE would publish thread status that
+ // nothing is left to render. Log instead.
+ BEESLOGDEBUG("Opening hash table '" << m_filename << "' target size " << m_size << " (" << pretty(m_size) << ")");
// Try to open existing hash table
Fd new_fd = openat(m_ctx->home_fd(), m_filename.c_str(), FLAGS_OPEN_FILE_RW, 0700);
THROW_ERRNO("hash table '" << m_filename << "' does not exist and state.create = no");
}
string tmp_filename = m_filename + ".tmp";
- BEESNOTE("creating new hash table '" << tmp_filename << "'");
BEESLOGINFO("Creating new hash table '" << tmp_filename << "'");
unlinkat(m_ctx->home_fd(), tmp_filename.c_str(), 0);
new_fd = openat_or_die(m_ctx->home_fd(), tmp_filename, FLAGS_CREATE_FILE, 0700);
- BEESNOTE("truncating new hash table '" << tmp_filename << "' size " << m_size << " (" << pretty(m_size) << ")");
BEESLOGINFO("Truncating new hash table '" << tmp_filename << "' size " << m_size << " (" << pretty(m_size) << ")");
ftruncate_or_die(new_fd, m_size);
- BEESNOTE("truncating new hash table '" << tmp_filename << "' -> '" << m_filename << "'");
BEESLOGINFO("Truncating new hash table '" << tmp_filename << "' -> '" << m_filename << "'");
renameat_or_die(m_ctx->home_fd(), tmp_filename, m_ctx->home_fd(), m_filename);
}
BeesProgressLogger load_progress("Loading hash table '" + m_filename + "'");
for (uint64_t ext = 0; ext < m_extents; ++ext) {
if (m_persistent) {
- BEESNOTE("loading hash extent #" << ext << " of " << m_extents);
uint8_t *const extent_begin = m_extent_ptr[ext ].p_byte;
uint8_t *const extent_end = m_extent_ptr[ext + 1].p_byte;
pread_or_die(m_fd, extent_begin, extent_end - extent_begin, extent_begin - m_byte_ptr);
// the requested size fails fast and visibly. catch_all to support
// users who don't want to use mlock().
if (m_cell_ptr) {
- BEESNOTE("mlock " << pretty(m_size));
BEESLOGINFO("mlock(" << pretty(m_size) << ")...");
Timer lock_time;
catch_all([&]() {
// loop has no partial tail extent to special-case.
THROW_CHECK2(invalid_argument, new_size, BLOCK_SIZE_HASHTAB_EXTENT, (new_size % BLOCK_SIZE_HASHTAB_EXTENT) == 0);
const uint64_t new_extents = new_size / BLOCK_SIZE_HASHTAB_EXTENT;
- BEESNOTE("allocating " << pretty(new_size) << " for the resized hash table");
Timer alloc_timer;
// Raw bytes, not vector<Extent>: Cell has no default constructor by
// design, so Bucket and Extent have none either. Both are union
const uint64_t reads_before = (c_cells_per_bucket - ci) * old_extents;
for (uint64_t pass = 0; pass < old_extents; ++pass) {
const uint64_t ei = old_extents - 1 - pass;
- BEESNOTE("resizing hash table: cell-pass " << (c_cells_per_bucket - cell_idx)
- << "/" << c_cells_per_bucket
- << ", reading old extent " << (pass + 1) << "/" << old_extents);
resize_progress.update(reads_before + pass + 1, total_reads);
const off_t offset = ranged_cast<off_t>(ei) * BLOCK_SIZE_HASHTAB_EXTENT;
pread_or_die(m_fd, extent_buf, offset);
// and rename below, not from the size of any one write.
BeesProgressLogger write_progress("Writing resized hash table to '" + tmp_filename + "'");
for (uint64_t ext = 0; ext < new_extents; ++ext) {
- BEESNOTE("writing resized hash table to '" << tmp_filename << "': extent "
- << (ext + 1) << "/" << new_extents);
const Extent &extent = new_extent_ptr[ext];
pwrite_or_die(tmp_fd, extent.p_byte, sizeof(extent.p_byte),
ranged_cast<off_t>(ext * sizeof(Extent)));
// good one. bees_fsync() decides whether the syscall is needed at
// all: on btrfs before 5.16 the rename already flushes, and fsync
// there caused ghost dirents in $BEESHOME.
- BEESNOTE("syncing resized hash table '" << tmp_filename << "'");
Timer fsync_timer;
bees_fsync(tmp_fd);
BEESLOGDEBUG("bees_fsync of resized hash table returned in " << fsync_timer << " sec");