Skip to content

Commit b35c2f0

Browse files
committed
backfill: add --min-batch-size=<n> option
Users may want to specify a minimum batch size for their needs. This is only a minimum: the path-walk API provides a list of OIDs that correspond to the same path, and thus it is optimal to allow delta compression across those objects in a single server request. We could consider limiting the request to have a maximum batch size in the future. For now, we let the path-walk API batches determine the boundaries. To get a feeling for the value of specifying the --min-batch-size parameter, I tested a number of open source repositories available on GitHub. The procedure was generally: 1. git clone --filter=blob:none <url> 2. git backfill Checking the number of packfiles and the size of the .git/objects/pack directory helps to identify the effects of different batch sizes. For the Git repository, we get these results: | Batch Size | Pack Count | Pack Size | Time | |-----------------|------------|-----------|-------| | (Initial clone) | 2 | 119 MB | | | 25K | 8 | 290 MB | 24s | | 50K | 5 | 290 MB | 24s | | 100K | 4 | 290 MB | 29s | Other than the packfile counts decreasing as we need fewer batches, the size and time required is not changing much for this small example. For the nodejs/node repository, we see these results: | Batch Size | Pack Count | Pack Size | Time | |-----------------|------------|-----------|--------| | (Initial clone) | 2 | 330 MB | | | 25K | 19 | 1,222 MB | 1m 22s | | 50K | 11 | 1,221 MB | 1m 24s | | 100K | 7 | 1,223 MB | 1m 40s | | 250K | 4 | 1,224 MB | 2m 23s | | 500K | 3 | 1,216 MB | 4m 38s | Here, we don't have much difference in the size of the repo, though the 500K batch size results in a few MB gained. That comes at a cost of a much longer time. This extra time is due to server-side delta compression happening as the on-disk deltas don't appear to be reusable all the time. But for smaller batch sizes, the server is able to find reasonable deltas partly because we are asking for objects that appear in the same region of the directory tree and include all versions of a file at a specific path. To contrast this example, I tested the microsoft/fluentui repo, which has been known to have inefficient packing due to name hash collisions. These results are found before GitHub had the opportunity to repack the server with more advanced name hash versions: | Batch Size | Pack Count | Pack Size | Time | |-----------------|------------|-----------|--------| | (Initial clone) | 2 | 105 MB | | | 5K | 53 | 348 MB | 2m 26s | | 10K | 28 | 365 MB | 2m 22s | | 15K | 19 | 407 MB | 2m 21s | | 20K | 15 | 393 MB | 2m 28s | | 25K | 13 | 417 MB | 2m 06s | | 50K | 8 | 509 MB | 1m 34s | | 100K | 5 | 535 MB | 1m 56s | | 250K | 4 | 698 MB | 1m 33s | | 500K | 3 | 696 MB | 1m 42s | Here, a larger variety of batch sizes were chosen because of the great variation in results. By asking the server to download small batches corresponding to fewer paths at a time, the server is able to provide better compression for these batches than it would for a regular clone. A typical full clone for this repository would require 738 MB. This example justifies the choice to batch requests by path name, leading to improved communication with a server that is not optimally packed. Finally, the same experiment for the Linux repository had these results: | Batch Size | Pack Count | Pack Size | Time | |-----------------|------------|-----------|---------| | (Initial clone) | 2 | 2,153 MB | | | 25K | 63 | 6,380 MB | 14m 08s | | 50K | 58 | 6,126 MB | 15m 11s | | 100K | 30 | 6,135 MB | 18m 11s | | 250K | 14 | 6,146 MB | 18m 22s | | 500K | 8 | 6,143 MB | 33m 29s | Even in this example, where the default name hash algorithm leads to decent compression of the Linux kernel repository, there is value for selecting a smaller batch size, to a limit. The 25K batch size has the fastest time, but uses 250 MB more than the 50K batch size. The 500K batch size took much more time due to server compression time and thus we should avoid large batch sizes like this. Based on these experiments, a batch size of 50,000 was chosen as the default value. Signed-off-by: Derrick Stolee <[email protected]>
1 parent d0cb9cb commit b35c2f0

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

Documentation/git-backfill.txt

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-backfill - Download missing objects in a partial clone
99
SYNOPSIS
1010
--------
1111
[synopsis]
12-
git backfill [<options>]
12+
git backfill [--min-batch-size=<n>]
1313

1414
DESCRIPTION
1515
-----------
@@ -47,6 +47,16 @@ commit. This set can be restricted or expanded using various options.
4747

4848
THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR MAY CHANGE IN THE FUTURE.
4949

50+
51+
OPTIONS
52+
-------
53+
54+
`--min-batch-size=<n>`::
55+
Specify a minimum size for a batch of missing objects to request
56+
from the server. This size may be exceeded by the last set of
57+
blobs seen at a given path. The default minimum batch size is
58+
50,000.
59+
5060
SEE ALSO
5161
--------
5262
linkgit:git-clone[1].

builtin/backfill.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "path-walk.h"
2222

2323
static const char * const builtin_backfill_usage[] = {
24-
N_("git backfill [<options>]"),
24+
N_("git backfill [--min-batch-size=<n>]"),
2525
NULL
2626
};
2727

@@ -108,6 +108,8 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
108108
.min_batch_size = 50000,
109109
};
110110
struct option options[] = {
111+
OPT_INTEGER(0, "min-batch-size", &ctx.min_batch_size,
112+
N_("Minimum number of objects to request at a time")),
111113
OPT_END(),
112114
};
113115

t/t5620-backfill.sh

+18
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ test_expect_success 'do partial clone 1, backfill gets all objects' '
5959
test_line_count = 0 revs2
6060
'
6161

62+
test_expect_success 'do partial clone 2, backfill min batch size' '
63+
git clone --no-checkout --filter=blob:none \
64+
--single-branch --branch=main \
65+
"file://$(pwd)/srv.bare" backfill2 &&
66+
67+
GIT_TRACE2_EVENT="$(pwd)/batch-trace" git \
68+
-C backfill2 backfill --min-batch-size=20 &&
69+
70+
# Batches were used
71+
test_trace2_data promisor fetch_count 20 <batch-trace >matches &&
72+
test_line_count = 2 matches &&
73+
test_trace2_data promisor fetch_count 8 <batch-trace &&
74+
75+
# No more missing objects!
76+
git -C backfill2 rev-list --quiet --objects --missing=print HEAD >revs2 &&
77+
test_line_count = 0 revs2
78+
'
79+
6280
. "$TEST_DIRECTORY"/lib-httpd.sh
6381
start_httpd
6482

0 commit comments

Comments
 (0)