[PATCH v5 0/3] packfile URIs: support concurrent downloads
Ted Nyman <[email protected]>
| Newsgroups | org.kernel.vger.git |
|---|---|
| Message-ID | <[email protected]> |
Packfile URI and dumb HTTP downloads stage packs at
objects/pack/pack-<hash>.pack.temp so an interrupted transfer can
resume. Opening that file in append mode forces every write to its
current end. Two Git processes fetching the same pack into one object
database can therefore append duplicate data and corrupt the pack.
The first patch separates the unrelated --index-pack-arg documentation
and error-message correction requested during review.
The second patch keeps the predictable staging name but removes append
mode. Each downloader seeks once to the current end, requests the
corresponding Range, and writes using its own descriptor offset. Since
the staging key must identify immutable pack contents, overlapping
responses write identical bytes at identical offsets. There is no need
for pwrite(2) or cross-process coordination, and resumption continues to
work for both packfile URI and ordinary dumb HTTP downloads.
A downloader can also find that the partial pack has completed and
request a range starting at EOF. Servers may respond with HTTP 416 in
that case. Treat the response as a completed download and let
index-pack validate the pack.
On MinGW, the non-append O_RDWR open grants FILE_SHARE_DELETE only for an
existing file. Create a missing staging file exclusively, close it, and
reopen it without O_CREAT so every retained descriptor permits another
downloader to unlink the path. Keep the open descriptor for index-pack;
it installs its own pack, so the shared staging file is only unlinked,
never renamed.
The third patch handles the related .keep race. When another process has
already created the keep file, index-pack reports "pack<TAB><hash>"
instead of "keep<TAB><hash>". Accept both successful forms and remove
only keep files created by the current process. Read only the prefix and
hash so any following fsck output remains available to fetch-pack.
The tests cover resumption, a completed partial returning 416,
overlapping downloads, unlinking the staging path while index-pack holds
its descriptor, and a pre-existing .keep file. The unlink test does not
require FIFOs, so it can exercise MinGW's sharing behavior even though
the concurrent-download tests are skipped there.
Changes since v4:
* Clarify that the first --index-pack-arg specifies the command and
subsequent instances specify its arguments.
* Drop assumptions about which concurrent response reaches the
staging file first. Either write order exercises the same
overlapping-download behavior.
* No production code changes.
The overlapping-download test passes 240 runs with 12 parallel stress
jobs.
The v4 discussion is at:
https://lore.kernel.org/git/[email protected]/
Ted Nyman (3):
http-fetch: correct --index-pack-arg documentation
http: avoid concurrent appends to partial packs
fetch-pack: accept "pack" output for packfile URIs
Documentation/git-http-fetch.adoc | 14 +-
fetch-pack.c | 33 ++--
http-fetch.c | 7 +-
http-push.c | 3 +-
http-walker.c | 3 +-
http.c | 56 ++++---
t/t5550-http-fetch-dumb.sh | 246 ++++++++++++++++++++++++++++++
t/t5702-protocol-v2.sh | 31 ++++
8 files changed, 347 insertions(+), 46 deletions(-)
Range-diff against v4:
1: a6a40b8046 ! 1: a79af009ea http-fetch: correct --index-pack-arg documentation
@@ Documentation/git-http-fetch.adoc: commit-id::
- For internal use only. The command to run on the contents of the
- downloaded pack. Arguments are URL-encoded separated by spaces.
+--index-pack-arg=<arg>::
-+ For internal use only. An argument to the command run on the contents
-+ of the downloaded pack. This option can be specified multiple times.
++ For internal use only. The first instance specifies the command run on
++ the contents of the downloaded pack. Subsequent instances specify its
++ arguments.
--recover::
Verify that everything reachable from target is fetched. Used after
2: 144c98cdfa ! 2: d9667c93b0 http: avoid concurrent appends to partial packs
@@ t/t5550-http-fetch-dumb.sh: test_expect_success 'http-fetch --packfile' '
+ read ready <&8 &&
+ test "$ready" = ready &&
+ test_path_is_file "$tmpfile" &&
-+ test -s "$tmpfile" &&
+ {
+ GIT_TRACE_CURL="$TRASH_DIRECTORY/overlap-second.trace" \
+ GIT_TRACE_CURL_NO_DATA=1 \
@@ t/t5550-http-fetch-dumb.sh: test_expect_success 'http-fetch --packfile' '
+ wait "$second_pid" &&
+ wait "$first_pid" &&
+ wait "$server_pid" &&
-+ test_grep "HTTP/[0-9.]* 200" overlap-first.trace &&
-+ test_grep "Range: bytes=[1-9][0-9]*-" overlap-second.trace &&
-+ test_grep "HTTP/[0-9.]* 206" overlap-second.trace &&
+ printf "keep\t%s\npack\t%s\n" "$packhash" "$packhash" | sort >expect &&
+ sort first.out second.out >actual &&
+ test_cmp expect actual &&
3: d9063deb60 = 3: fee6f292cb fetch-pack: accept "pack" output for packfile URIs
base-commit: 5d2e7709234afea1b6ddb25cd4f60d3d5fb3c200
--
2.55.0.openai.131.g83a728de1eb6