From fadf0567619d9b8a929b19105b6a59fd5220aeeb Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Sat, 27 Jun 2026 12:07:10 +0200 Subject: [PATCH] upload-pack: send keepalive while feeding pack-objects When upload-pack feeds revision OIDs to pack-objects via pipe, there is a gap where no data is sent to the HTTP client. With many wants (e.g. 50k+ refs), this pipe feeding can take seconds as write() blocks on the pipe when the kernel buffer fills up. Meanwhile, an HTTP server sitting in front of git (e.g. Apache) may time out waiting for CGI output and kill the process. The existing keepalive mechanism in create_pack_file's poll loop handles keepalive after pack-objects starts producing output, but does not cover the earlier pipe feeding phase. Extend keepalive coverage by sending empty sideband packets during the pipe feeding loops, using the same "0005\1" format that the poll loop already uses. This reuses the existing uploadpack.keepalive config with no new options required. Signed-off-by: Kristofer Karlsson --- upload-pack.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index a52856d869891d..6396ee8225cd7f 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -296,6 +296,22 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, return readsz; } +static void send_pack_keepalive(const struct upload_pack_data *pack_data, + uint64_t *last_sent_ms) +{ + uint64_t now_ms; + + if (!pack_data->use_sideband || pack_data->keepalive <= 0) + return; + + now_ms = getnanotime() / 1000000; + if (now_ms - *last_sent_ms >= (uint64_t)pack_data->keepalive * 1000) { + static const char buf[] = "0005\1"; + write_or_die(1, buf, 5); + *last_sent_ms = now_ms; + } +} + static void create_pack_file(struct upload_pack_data *pack_data, const struct string_list *uri_protocols) { @@ -361,16 +377,24 @@ static void create_pack_file(struct upload_pack_data *pack_data, if (pack_data->shallow_nr) for_each_commit_graft(write_one_shallow, pipe_fd); - for (i = 0; i < pack_data->want_obj.nr; i++) + last_sent_ms = getnanotime() / 1000000; + + for (i = 0; i < pack_data->want_obj.nr; i++) { fprintf(pipe_fd, "%s\n", oid_to_hex(&pack_data->want_obj.objects[i].item->oid)); + send_pack_keepalive(pack_data, &last_sent_ms); + } fprintf(pipe_fd, "--not\n"); - for (i = 0; i < pack_data->have_obj.nr; i++) + for (i = 0; i < pack_data->have_obj.nr; i++) { fprintf(pipe_fd, "%s\n", oid_to_hex(&pack_data->have_obj.objects[i].item->oid)); - for (i = 0; i < pack_data->extra_edge_obj.nr; i++) + send_pack_keepalive(pack_data, &last_sent_ms); + } + for (i = 0; i < pack_data->extra_edge_obj.nr; i++) { fprintf(pipe_fd, "%s\n", oid_to_hex(&pack_data->extra_edge_obj.objects[i].item->oid)); + send_pack_keepalive(pack_data, &last_sent_ms); + } fprintf(pipe_fd, "\n"); fflush(pipe_fd); fclose(pipe_fd);