Skip to content

Commit bce1308

Browse files
committed
maintenance: auto-size incremental-repack batch
When repacking during the 'incremental-repack' task, we use the --batch-size option in 'git multi-pack-index repack'. The initial setting used --batch-size=0 to repack everything into a single pack-file. This is not sustainable for a large repository. The amount of work required is also likely to use too many system resources for a background job. Update the 'incremental-repack' task by dynamically computing a --batch-size option based on the current pack-file structure. The dynamic default size is computed with this idea in mind for a client repository that was cloned from a very large remote: there is likely one "big" pack-file that was created at clone time. Thus, do not try repacking it as it is likely packed efficiently by the server. Instead, we select the second-largest pack-file, and create a batch size that is one larger than that pack-file. If there are three or more pack-files, then this guarantees that at least two will be combined into a new pack-file. Of course, this means that the second-largest pack-file size is likely to grow over time and may eventually surpass the initially-cloned pack-file. Recall that the pack-file batch is selected in a greedy manner: the packs are considered from oldest to newest and are selected if they have size smaller than the batch size until the total selected size is larger than the batch size. Thus, that oldest "clone" pack will be first to repack after the new data creates a pack larger than that. We also want to place some limits on how large these pack-files become, in order to bound the amount of time spent repacking. A maximum batch-size of two gigabytes means that large repositories will never be packed into a single pack-file using this job, but also that repack is rather expensive. This is a trade-off that is valuable to have if the maintenance is being run automatically or in the background. Users who truly want to optimize for space and performance (and are willing to pay the upfront cost of a full repack) can use the 'gc' task to do so. Create a test for this two gigabyte limit by creating an EXPENSIVE test that generates two pack-files of roughly 2.5 gigabytes in size, then performs an incremental repack. Check that the --batch-size argument in the subcommand uses the hard-coded maximum. Helped-by: Chris Torek <chris.torek@gmail.com> Reported-by: Son Luong Ngoc <sluongng@gmail.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
1 parent eecdf99 commit bce1308

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

builtin/gc.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,46 @@ static int multi_pack_index_expire(struct maintenance_opts *opts)
10941094
return 0;
10951095
}
10961096

1097+
#define TWO_GIGABYTES (INT32_MAX)
1098+
1099+
static off_t get_auto_pack_size(void)
1100+
{
1101+
/*
1102+
* The "auto" value is special: we optimize for
1103+
* one large pack-file (i.e. from a clone) and
1104+
* expect the rest to be small and they can be
1105+
* repacked quickly.
1106+
*
1107+
* The strategy we select here is to select a
1108+
* size that is one more than the second largest
1109+
* pack-file. This ensures that we will repack
1110+
* at least two packs if there are three or more
1111+
* packs.
1112+
*/
1113+
off_t max_size = 0;
1114+
off_t second_largest_size = 0;
1115+
off_t result_size;
1116+
struct packed_git *p;
1117+
struct repository *r = the_repository;
1118+
1119+
reprepare_packed_git(r);
1120+
for (p = get_all_packs(r); p; p = p->next) {
1121+
if (p->pack_size > max_size) {
1122+
second_largest_size = max_size;
1123+
max_size = p->pack_size;
1124+
} else if (p->pack_size > second_largest_size)
1125+
second_largest_size = p->pack_size;
1126+
}
1127+
1128+
result_size = second_largest_size + 1;
1129+
1130+
/* But limit ourselves to a batch size of 2g */
1131+
if (result_size > TWO_GIGABYTES)
1132+
result_size = TWO_GIGABYTES;
1133+
1134+
return result_size;
1135+
}
1136+
10971137
static int multi_pack_index_repack(struct maintenance_opts *opts)
10981138
{
10991139
struct child_process child = CHILD_PROCESS_INIT;
@@ -1104,7 +1144,8 @@ static int multi_pack_index_repack(struct maintenance_opts *opts)
11041144
if (opts->quiet)
11051145
strvec_push(&child.args, "--no-progress");
11061146

1107-
strvec_push(&child.args, "--batch-size=0");
1147+
strvec_pushf(&child.args, "--batch-size=%"PRIuMAX,
1148+
(uintmax_t)get_auto_pack_size());
11081149

11091150
close_object_store(the_repository->objects);
11101151

t/t7900-maintenance.sh

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,42 @@ test_expect_success 'incremental-repack task' '
179179
test_line_count = 4 packs-between &&
180180
181181
# the job deletes the two old packs, and does not write
182-
# a new one because only one pack remains.
182+
# a new one because the batch size is not high enough to
183+
# pack the largest pack-file.
183184
git maintenance run --task=incremental-repack &&
184185
ls .git/objects/pack/*.pack >packs-after &&
185-
test_line_count = 1 packs-after
186+
test_line_count = 2 packs-after
187+
'
188+
189+
test_expect_success EXPENSIVE 'incremental-repack 2g limit' '
190+
for i in $(test_seq 1 5)
191+
do
192+
test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
193+
return 1
194+
done &&
195+
git add big &&
196+
git commit -m "Add big file (1)" &&
197+
198+
# ensure any possible loose objects are in a pack-file
199+
git maintenance run --task=loose-objects &&
200+
201+
rm big &&
202+
for i in $(test_seq 6 10)
203+
do
204+
test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
205+
return 1
206+
done &&
207+
git add big &&
208+
git commit -m "Add big file (2)" &&
209+
210+
# ensure any possible loose objects are in a pack-file
211+
git maintenance run --task=loose-objects &&
212+
213+
# Now run the incremental-repack task and check the batch-size
214+
GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \
215+
--task=incremental-repack 2>/dev/null &&
216+
test_subcommand git multi-pack-index repack \
217+
--no-progress --batch-size=2147483647 <run-2g.txt
186218
'
187219

188220
test_done

0 commit comments

Comments
 (0)