} else {
dirty_tail = *maybe_dirty_tail;
}
- auto tails = journal_tail_delta_t{
- get_oldest_backref_dirty_from().value_or(JOURNAL_SEQ_NULL),
- dirty_tail
- };
+ journal_seq_t alloc_tail;
+ auto maybe_alloc_tail = get_oldest_backref_dirty_from();
+ if (!maybe_alloc_tail.has_value()) {
+ alloc_tail = JOURNAL_SEQ_NULL;
+ // see notes in Cache::complete_commit().
+ SUBWARNT(seastore_t, "backref_buffer all trimmed", t);
+ } else if (*maybe_alloc_tail == JOURNAL_SEQ_NULL) {
+ ceph_abort("impossible");
+ } else {
+ alloc_tail = *maybe_alloc_tail;
+ }
+ auto tails = journal_tail_delta_t{alloc_tail, dirty_tail};
SUBDEBUGT(seastore_t, "update tails as delta {}", t, tails);
bufferlist bl;
encode(tails, bl);
{
LOG_PREFIX(Cache::backref_batch_update);
DEBUG("inserting {} entries at {}", list.size(), seq);
+ ceph_assert(seq != JOURNAL_SEQ_NULL);
if (!backref_buffer) {
backref_buffer = std::make_unique<backref_cache_t>();
}
t,
i->get_paddr(),
i->get_length());
+ // FIXME: In theroy, adding backref_list in the finalize phase
+ // can result in wrong alloc_tail for replay:
+ // * trans-1 enters prepare record with allocations.
+ // * trans-2 (cleaner) enters prepare record, see no alloc-tail,
+ // so it assume the alloc-tail is the start-seq of trans-2.
+ // * trans-1 enters finalize and update the backref_list,
+ // implying that alloc-tail is the start-seq of trans-1.
+ // * trans-2 (cleaner) enters finalize.
+ // During replay, the alloc-tail will be set to the start-seq of
+ // trans-2 according to journal_tail_delta_t, but it should be
+ // actually the start-seq of trans-1.
+ // This can only happen if alloc_tail trimming is able to trim to
+ // the journal head.
backref_list.emplace_back(
std::make_unique<backref_buf_entry_t>(
i->get_paddr(),
add_extent(i, &t_src);
}
}
- if (!backref_list.empty())
+ if (!backref_list.empty()) {
backref_batch_update(std::move(backref_list), seq);
+ }
}
void Cache::init()
LOG_PREFIX(Cache::trim_backref_bufs);
SUBDEBUG(seastore_cache, "trimming to {}", trim_to);
if (backref_buffer && !backref_buffer->backrefs_by_seq.empty()) {
+ SUBDEBUG(seastore_cache, "backrefs {} ~ {}, size={}",
+ backref_buffer->backrefs_by_seq.rbegin()->first,
+ backref_buffer->backrefs_by_seq.begin()->first,
+ backref_buffer->backrefs_by_seq.size());
assert(backref_buffer->backrefs_by_seq.rbegin()->first >= trim_to);
auto iter = backref_buffer->backrefs_by_seq.upper_bound(trim_to);
backref_buffer->backrefs_by_seq.erase(
backref_buffer->backrefs_by_seq.begin(), iter);
}
+ if (!backref_buffer || backref_buffer->backrefs_by_seq.empty()) {
+ // see notes in Cache::complete_commit().
+ SUBWARN(seastore_cache, "backref_buffer all trimmed");
+ }
}
/**
journal_seq_t seq,
size_t max_bytes);
+ /// returns std::nullopt if no pending alloc-infos
std::optional<journal_seq_t> get_oldest_backref_dirty_from() const {
LOG_PREFIX(Cache::get_oldest_backref_dirty_from);
- journal_seq_t backref_oldest = JOURNAL_SEQ_NULL;
- if (backref_buffer && !backref_buffer->backrefs_by_seq.empty()) {
- backref_oldest = backref_buffer->backrefs_by_seq.begin()->first;
- }
- if (backref_oldest == JOURNAL_SEQ_NULL) {
+ if (!backref_buffer || backref_buffer->backrefs_by_seq.empty()) {
SUBDEBUG(seastore_cache, "backref_oldest: null");
return std::nullopt;
- } else {
- SUBDEBUG(seastore_cache, "backref_oldest: {}",
- backref_oldest);
- return backref_oldest;
}
+ auto oldest = backref_buffer->backrefs_by_seq.begin()->first;
+ SUBDEBUG(seastore_cache, "backref_oldest: {}", oldest);
+ ceph_assert(oldest != JOURNAL_SEQ_NULL);
+ return oldest;
}
/// returns std::nullopt if no dirty extents