unchanged until an operator opts in).
* Accepts a [size value](config-format.md#size-values).
+* **`prefer-canonical-src`**
+ When several source extents match a destination equally well — they cover the
+ same span, so the choice between them does not change the plan's cost — prefer
+ the source that already has the most references. This consolidates duplicates
+ onto a single canonical extent (a star topology) rather than spreading them
+ across arbitrary sources; when off, the first equally-good source is used and
+ references spread out (the historical behavior).
+ * Only safe with a non-zero [`ref-op-cost`](#rewrite-section). Consolidation
+ concentrates references onto a few extents, and `ref-op-cost` is what prices
+ — and therefore bounds — the reference movement that concentration drives.
+ With `ref-op-cost = 0` there is no such bound: references pile onto canonical
+ extents up to `refs-max` and re-canonicalize along a chain, doing far more
+ reference-rewriting work than spreading them would. Enable this only
+ together with a non-zero `ref-op-cost`.
+ * Default: `yes`.
+ * Accepts a [boolean value](config-format.md#boolean-values).
+
* **`plan-max-vertices`**
Boundary-vertex budget for the shortest-path coverage search. The exact
search is O(V²) in the boundary count V (the distinct match endpoints of a
# Size value in bytes; 0 disables. See docs/config-file.md.
ref-op-cost = 0
+ # Prefer the most-referenced source on equal-length dedupe matches, to
+ # consolidate duplicates onto a canonical extent. Boolean; only safe
+ # with a non-zero ref-op-cost. See docs/config-file.md.
+ prefer-canonical-src = yes
+
# Boundary-vertex budget for the coverage search. Above this V the
# planner uses a linear greedy cover instead of the O(V^2) exact
# search. Integer count, or unlimited to disable the fallback.
rv.m_inode_locking = load("rewrite.inode-locking", bees_parse_bool);
rv.m_keep_older = load("rewrite.keep-older", bees_parse_bool);
rv.m_keep_newer = load("rewrite.keep-newer", bees_parse_bool);
+ rv.m_prefer_canonical_src = load("rewrite.prefer-canonical-src", bees_parse_bool);
{
BEESTRACE("loading rewrite.read-size");
rv.m_read_size = get("rewrite.read-size", bees_parse_size);
for (const auto &match : group.m_matches) {
if (match.size() == 0) continue;
// Carry the src ref count as the search's equal-reach tie-break
- // (prefer consolidating onto the higher-ref, canonical src). The
- // refs() lookup is layer-cached; the src extents here were already
- // resolved to produce these matches, so this is warm.
- const uint64_t src_refs = match.m_src.refs(layer)->size();
+ // (rewrite.prefer-canonical-src): prefer consolidating onto the
+ // higher-ref, canonical src. When disabled leave it 0, which the
+ // search reads as "no preference" (ties resolve as before) and
+ // also skips the refs() lookup. The lookup is layer-cached; the
+ // src extents here were already resolved to produce these matches.
+ const uint64_t src_refs = rewrite_policy.m_prefer_canonical_src
+ ? match.m_src.refs(layer)->size() : 0;
in.m_matches.push_back(PlanSearchMatch{
match.m_dst_begin, match.m_dst_end, flat.size(), src_refs });
flat.push_back(FlatMatch{ &group, &match });
/// reference cost. Authoritative default lives in bees-config-v2.cc
/// (0 = disabled); the C++ initializer is an invalid sentinel.
uint64_t m_ref_op_cost = UINT64_MAX;
+ /// When several source extents match a destination equally well (they cover
+ /// the same span), prefer the source that already has the most references,
+ /// so duplicates consolidate onto a canonical extent instead of an
+ /// arbitrary one; off spreads references across sources (the historical
+ /// behavior). Consolidating is only safe when reference moves are priced:
+ /// with rewrite.ref-op-cost = 0 it concentrates references onto a few
+ /// extents with nothing to bound the resulting re-canonicalization, so pair
+ /// it with a non-zero ref-op-cost. Authoritative default in
+ /// bees-config-v2.cc; the C++ initializer (false) is only the pre-load
+ /// value (a bool has no invalid sentinel — see code that sets it).
+ bool m_prefer_canonical_src = false;
/// Boundary-vertex budget for the shortest-path coverage search.
/// The exact search is O(V^2) in the boundary count V and holds the
/// dst extent's Exclusion for its whole duration, so a high-V extent
p.m_inode_locking = true;
p.m_keep_older = true;
p.m_keep_newer = true;
+ p.m_prefer_canonical_src = true;
p.ref_cost = 53;
p.extent_cost = 53;
p.m_ref_op_cost = 0;