/// Per-dst raw inputs for the do-nothing space-debt model
/// (do-nothing-plan.md §4). Aggregated across all dsts of a plan into
-/// CostReport's m_refs_added / m_extents_delta / m_physical_freed, then
-/// combined by CostReport::space_debt() into the m_space_debt ordering key.
+/// CostReport's m_refs_added / m_refs_created / m_extents_delta /
+/// m_physical_freed, then combined by CostReport::space_debt() into the
+/// m_space_debt ordering key. Must mirror scan_next_covering_debt
+/// (bees-plan.cc) field-for-field — the search and the plan-cost path share
+/// one space_debt() formula, so a term set in one but not the other (as
+/// m_refs_created once was) silently drops out of orientation selection.
struct ScanNextDstDebt {
uint64_t m_refs_added = 0;
+ uint64_t m_refs_created = 0;
int64_t m_extents_delta = 0;
uint64_t m_physical_freed = 0;
};
* (static_cast<uint64_t>(frag_count) - 1);
}
+ // refs_created = dst_ref_count * F: every dst reference is re-written once
+ // per fragment, the actual FILE_EXTENT_SAME work. Unlike refs_added (net
+ // metadata growth, F-1 per ref) this is charged even for a whole-extent
+ // (F == 1) dedupe, so it is NOT guarded by frag_count > 1 — mirrors
+ // scan_next_covering_debt (bees-plan.cc). This is the term ref-op-cost
+ // weights; without it the plan-level cost that drives orientation
+ // selection never prices the references a dedupe moves, so draining a
+ // high-ref extent as dst looks free.
+ rv.m_refs_created = static_cast<uint64_t>(dst_ref_count)
+ * static_cast<uint64_t>(frag_count);
+
// physical_freed: a compressed dst is removed wholesale (the
// compressed-dst-with-copy reject forbids retaining any fragment),
// so the recovered space is exactly its phys_size. An uncompressed
const auto debt = scan_next_dst_debt(
Borrower::current().layer(), m_start, cp);
m_cost.m_refs_added = debt.m_refs_added;
+ m_cost.m_refs_created = debt.m_refs_created;
m_cost.m_extents_delta = debt.m_extents_delta;
m_cost.m_physical_freed = debt.m_physical_freed;
}
const auto debt = scan_next_dst_debt(
Borrower::current().layer(), candidate, *chosen);
entry.m_entry_cost.m_refs_added = debt.m_refs_added;
+ entry.m_entry_cost.m_refs_created = debt.m_refs_created;
entry.m_entry_cost.m_extents_delta = debt.m_extents_delta;
entry.m_entry_cost.m_physical_freed = debt.m_physical_freed;
}
m_cost.m_copy_bytes += e.m_entry_cost.m_copy_bytes;
m_cost.m_plan_ops += e.m_entry_cost.m_plan_ops;
m_cost.m_refs_added += e.m_entry_cost.m_refs_added;
+ m_cost.m_refs_created += e.m_entry_cost.m_refs_created;
m_cost.m_extents_delta += e.m_entry_cost.m_extents_delta;
m_cost.m_physical_freed += e.m_entry_cost.m_physical_freed;
}