Skip to content

Commit 5d4b7dd

Browse files
pks-tgitster
authored andcommitted
upload-pack: adapt keepalives based on buffering
The function `create_pack_file()` is responsible for sending the packfile data to the client of git-upload-pack(1). As generating the bytes may take significant computing resources we also have a mechanism in place that optionally sends keepalive pktlines in case we haven't sent out any data. The keepalive logic is purely based poll(3p): we pass a timeout to that syscall, and if the call times out we send out the keepalive pktline. While reasonable, this logic isn't entirely sufficient: even if the call to poll(3p) ends because we have received data on any of the file descriptors we may not necessarily send data to the client. The most important edge case here happens in `relay_pack_data()`. When we haven't seen the initial "PACK" signature from git-pack-objects(1) yet we buffer incoming data. So in the worst case, if each of the bytes of that signature arrive shortly before the configured keepalive timeout, then we may not send out any data for a time period that is (almost) four times as long as the configured timeout. This edge case is rather unlikely to matter in practice. But in a subsequent commit we're going to adapt our buffering mechanism to become more aggressive, which makes it more likely that we don't send any data for an extended amount of time. Adapt the logic so that instead of using a fixed timeout on every call to poll(3p), we instead figure out how much time has passed since the last-sent data. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9f4cdf5 commit 5d4b7dd

1 file changed

Lines changed: 40 additions & 9 deletions

File tree

upload-pack.c

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "commit-graph.h"
3030
#include "commit-reach.h"
3131
#include "shallow.h"
32+
#include "trace.h"
3233
#include "write-or-die.h"
3334
#include "json-writer.h"
3435
#include "strmap.h"
@@ -218,7 +219,8 @@ struct output_state {
218219
};
219220

220221
static int relay_pack_data(int pack_objects_out, struct output_state *os,
221-
int use_sideband, int write_packfile_line)
222+
int use_sideband, int write_packfile_line,
223+
bool *did_send_data)
222224
{
223225
/*
224226
* We keep the last byte to ourselves
@@ -232,6 +234,8 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
232234
*/
233235
ssize_t readsz;
234236

237+
*did_send_data = false;
238+
235239
readsz = xread(pack_objects_out, os->buffer + os->used,
236240
sizeof(os->buffer) - os->used);
237241
if (readsz < 0) {
@@ -247,6 +251,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
247251
if (os->packfile_uris_started)
248252
packet_delim(1);
249253
packet_write_fmt(1, "\1packfile\n");
254+
*did_send_data = true;
250255
}
251256
break;
252257
}
@@ -259,6 +264,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
259264
}
260265
*p = '\0';
261266
packet_write_fmt(1, "\1%s\n", os->buffer);
267+
*did_send_data = true;
262268

263269
os->used -= p - os->buffer + 1;
264270
memmove(os->buffer, p + 1, os->used);
@@ -279,6 +285,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
279285
os->used = 0;
280286
}
281287

288+
*did_send_data = true;
282289
return readsz;
283290
}
284291

@@ -290,6 +297,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
290297
char progress[128];
291298
char abort_msg[] = "aborting due to possible repository "
292299
"corruption on the remote side.";
300+
uint64_t last_sent_ms = 0;
293301
ssize_t sz;
294302
int i;
295303
FILE *pipe_fd;
@@ -365,10 +373,14 @@ static void create_pack_file(struct upload_pack_data *pack_data,
365373
*/
366374

367375
while (1) {
376+
uint64_t now_ms = getnanotime() / 1000000;
368377
struct pollfd pfd[2];
369-
int pe, pu, pollsize, polltimeout;
378+
int pe, pu, pollsize, polltimeout_ms;
370379
int ret;
371380

381+
if (!last_sent_ms)
382+
last_sent_ms = now_ms;
383+
372384
reset_timeout(pack_data->timeout);
373385

374386
pollsize = 0;
@@ -390,11 +402,21 @@ static void create_pack_file(struct upload_pack_data *pack_data,
390402
if (!pollsize)
391403
break;
392404

393-
polltimeout = pack_data->keepalive < 0
394-
? -1
395-
: 1000 * pack_data->keepalive;
405+
if (pack_data->keepalive < 0) {
406+
polltimeout_ms = -1;
407+
} else {
408+
/*
409+
* The polling timeout needs to be adjusted based on
410+
* the time we have sent our last package. The longer
411+
* it's been in the past, the shorter the timeout
412+
* becomes until we eventually don't block at all.
413+
*/
414+
polltimeout_ms = 1000 * pack_data->keepalive - (now_ms - last_sent_ms);
415+
if (polltimeout_ms < 0)
416+
polltimeout_ms = 0;
417+
}
396418

397-
ret = poll(pfd, pollsize, polltimeout);
419+
ret = poll(pfd, pollsize, polltimeout_ms);
398420

399421
if (ret < 0) {
400422
if (errno != EINTR) {
@@ -403,16 +425,18 @@ static void create_pack_file(struct upload_pack_data *pack_data,
403425
}
404426
continue;
405427
}
428+
406429
if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
407430
/* Status ready; we ship that in the side-band
408431
* or dump to the standard error.
409432
*/
410433
sz = xread(pack_objects.err, progress,
411434
sizeof(progress));
412-
if (0 < sz)
435+
if (0 < sz) {
413436
send_client_data(2, progress, sz,
414437
pack_data->use_sideband);
415-
else if (sz == 0) {
438+
last_sent_ms = now_ms;
439+
} else if (sz == 0) {
416440
close(pack_objects.err);
417441
pack_objects.err = -1;
418442
}
@@ -421,18 +445,24 @@ static void create_pack_file(struct upload_pack_data *pack_data,
421445
/* give priority to status messages */
422446
continue;
423447
}
448+
424449
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
450+
bool did_send_data;
425451
int result = relay_pack_data(pack_objects.out,
426452
output_state,
427453
pack_data->use_sideband,
428-
!!uri_protocols);
454+
!!uri_protocols,
455+
&did_send_data);
429456

430457
if (result == 0) {
431458
close(pack_objects.out);
432459
pack_objects.out = -1;
433460
} else if (result < 0) {
434461
goto fail;
435462
}
463+
464+
if (did_send_data)
465+
last_sent_ms = now_ms;
436466
}
437467

438468
/*
@@ -448,6 +478,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
448478
if (!ret && pack_data->use_sideband) {
449479
static const char buf[] = "0005\1";
450480
write_or_die(1, buf, 5);
481+
last_sent_ms = now_ms;
451482
}
452483
}
453484

0 commit comments

Comments
 (0)