Previously the only clever compaction avoidance we'd try was in the
manifest walk. If we found that there were no overlapping segments in
the next level we'd just move the entry down a level and skip compaction
entirely.
But that's just one specific instance of the general case: either of the
lower or upper segments don't overlap with each other. There can be
many lower level segments that intersect with the full range of keys in
the upper level segment but which don't actually intersect with any
items in the upper segment.
So we refactor the compaction to notice this case. We get the first and
last keys and use them to skip each segment as we first start to iterate
through it.
We don't want to read segments that we never actually have to copy items
from so we read each segment on demand instead of concurrently as the
compaction starts. This means that item iteration can now have to read
a segment and can now return errors.
Signed-off-by: Zach Brown <zab@versity.com>