The can_ow branch of LogManager's expect_overflow computed:
```
remain = capacity() - get_last_pos() - reserved_len
```
all uint32 operands, once the log leaf node is near full
(get_last_pos() + reserved_len >= capacity()) this subtraction underflows.
The node never rolls to a new leaf, instead it overflows the
16 KB block at commit-time delta replay.
Captured at the replay overflow point (per-collection batching, 4K randwrite):
```
LogNode OVERFLOW at replay: pos=16641 size=58 reserved_len=3098
reserved_size=6 capacity=16328 LIMIT=16384
log_node.h : In function 'void LogKVNodeLayout::set_last_pos(uint32_t)',
ceph_assert(pos <= LOG_NODE_BLOCK_SIZE)
```
Here reserved_len (3098) is accurate: ~13543 committed + 3098 pending =
16641 = pos, i.e. the node was already ~13.5 KB full and the predictor
should have rolled over. Instead capacity - get_last_pos - reserved_len
went negative (16328 - ~13273 - 3098 < 0), wrapped in uint32, and the
check passed; pos reached 16641 > 16384 and set_last_pos asserted.
This was exposed by the per-coll batching where many overwrites append in
a single txn.
Signed-off-by: Matan Breizman <mbreizma@redhat.com>
size_t ksize = key.size();
if (can_ow) {
int gap = ow_gap_from_last_entry(key.size(), vsize);
- uint64_t remain = capacity() - get_last_pos() - reserved_len;
if (gap >= 0) {
gap += static_cast<uint64_t>(gap);
} else {
uint64_t d = static_cast<uint64_t>(-gap);
gap -= d;
}
- return remain < get_entry_size(ksize, vsize);
+ return get_last_pos() + reserved_len + get_entry_size(ksize, vsize)
+ > capacity();
} else if (get_size() + reserved_size + 1 > d_bitmap_t::MAX_ENTRY) {
return true;
} else if (is_ow_key(key) && !can_ow) {