reftable/stack: add function to check if optimization is required

The reftable backend performs auto-compaction as part of its regular flow, which is required to keep the number of tables part of a stack at bay. This allows it to stay optimized. Compaction can also be triggered voluntarily by the user via the 'git pack-refs' or the 'git refs optimize' command. However, currently there is no way for the user to check if optimization is required without actually performing it. Extract out the heuristics logic from 'reftable_stack_auto_compact()' into an internal function 'update_segment_if_compaction_required()'. Then use this to add and expose `reftable_stack_compaction_required()` which will allow users to check if the reftable backend can be optimized. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Nov 8, 2025 at 22:51 UTC e35155588aa9f0355eb7e116ea418c189479f62d
3 files changed +58 -7
reftable/reftable-stack.h
+11
@@ -123,6 +123,17 @@ struct reftable_log_expiry_config {
123 int reftable_stack_compact_all(struct reftable_stack *st,
124 struct reftable_log_expiry_config *config);
125
126 +/*
127 + * Check if compaction is required.
128 + *
129 + * When `use_heuristics` is false, check if all tables can be compacted to a
130 + * single table. If true, use heuristics to determine if the tables need to be
131 + * compacted to maintain geometric progression.
132 + */
133 +int reftable_stack_compaction_required(struct reftable_stack *st,
134 + bool use_heuristics,
135 + bool *required);
136 +
137 /* heuristically compact unbalanced table stack. */
138 int reftable_stack_auto_compact(struct reftable_stack *st);
139
reftable/stack.c
+37 -5
@@ -1647,19 +1647,51 @@ static int stack_segments_for_compaction(struct reftable_stack *st,
1647 return 0;
1648 }
1649
1650 -int reftable_stack_auto_compact(struct reftable_stack *st)
1650 +static int update_segment_if_compaction_required(struct reftable_stack *st,
1651 + struct segment *seg,
1652 + bool use_geometric,
1653 + bool *required)
1654 {
1652 - struct segment seg;
1655 int err;
1656
1655 - if (st->merged->tables_len < 2)
1657 + if (st->merged->tables_len < 2) {
1658 + *required = false;
1659 + return 0;
1660 + }
1661 +
1662 + if (!use_geometric) {
1663 + *required = true;
1664 return 0;
1665 + }
1666 +
1667 + err = stack_segments_for_compaction(st, seg);
1668 + if (err)
1669 + return err;
1670 +
1671 + *required = segment_size(seg) > 0;
1672 + return 0;
1673 +}
1674 +
1675 +int reftable_stack_compaction_required(struct reftable_stack *st,
1676 + bool use_heuristics,
1677 + bool *required)
1678 +{
1679 + struct segment seg;
1680 + return update_segment_if_compaction_required(st, &seg, use_heuristics,
1681 + required);
1682 +}
1683 +
1684 +int reftable_stack_auto_compact(struct reftable_stack *st)
1685 +{
1686 + struct segment seg;
1687 + bool required;
1688 + int err;
1689
1658 - err = stack_segments_for_compaction(st, &seg);
1690 + err = update_segment_if_compaction_required(st, &seg, true, &required);
1691 if (err)
1692 return err;
1693
1662 - if (segment_size(&seg) > 0)
1694 + if (required)
1695 return stack_compact_range(st, seg.start, seg.end - 1,
1696 NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
1697
t/unit-tests/u-reftable-stack.c
+10 -2
@@ -1067,6 +1067,7 @@ void test_reftable_stack__add_performs_auto_compaction(void)
1067 .value_type = REFTABLE_REF_SYMREF,
1068 .value.symref = (char *) "master",
1069 };
1070 + bool required = false;
1071 char buf[128];
1072
1073 /*
@@ -1087,10 +1088,17 @@ void test_reftable_stack__add_performs_auto_compaction(void)
1088 * auto compaction is disabled. When enabled, we should merge
1089 * all tables in the stack.
1090 */
1090 - if (i != n)
1091 + cl_assert_equal_i(reftable_stack_compaction_required(st, true, &required), 0);
1092 + if (i != n) {
1093 cl_assert_equal_i(st->merged->tables_len, i + 1);
1092 - else
1094 + if (i < 1)
1095 + cl_assert_equal_b(required, false);
1096 + else
1097 + cl_assert_equal_b(required, true);
1098 + } else {
1099 cl_assert_equal_i(st->merged->tables_len, 1);
1100 + cl_assert_equal_b(required, false);
1101 + }
1102 }
1103
1104 reftable_stack_destroy(st);