[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.