From: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
To: Robin Jarry <robin.jarry@6wind.com>
Cc: Junio C Hamano <gitster@pobox.com>,
git@vger.kernel.org, Emily Shaffer <emilyshaffer@google.com>,
Nicolas Dichtel <nicolas.dichtel@6wind.com>,
Patryk Obara <patryk.obara@gmail.com>,
Jiang Xin <zhiyou.jx@alibaba-inc.com>
Subject: Re: [PATCH v4] receive-pack: check if client is alive before completing the push
Date: Fri, 04 Feb 2022 12:37:23 +0100 [thread overview]
Message-ID: <220204.864k5e4yvf.gmgdl@evledraar.gmail.com> (raw)
In-Reply-To: <20220128194811.3396281-1-robin.jarry@6wind.com>
On Fri, Jan 28 2022, Robin Jarry wrote:
> Abort the push operation (i.e. do not migrate the objects from temporary
> to permanent storage) if the client has disconnected while the
> pre-receive hook was running.
>
> This reduces the risk of inconsistencies on network errors or if the
> user hits ctrl-c while the pre-receive hook is running.
>
> Send a keepalive packet (empty) on sideband 2 (the one to report
> progress). If the client has exited the write() operation should fail
> and the push will be aborted. This only works when sideband*
> capabilities are advertised by the client.
>
> Note: if the write() operation fails, receive-pack will likely be killed
> via SIGPIPE and even so, since the client is likely gone already, the
> error strings will go nowhere. I only added them for code consistency.
>
> Signed-off-by: Robin Jarry <robin.jarry@6wind.com>
> ---
> v3 -> v4:
> - reworded the comment block s/ensure/notice/
> - used write_in_full() instead of write_or_die()
> - set error_string fields for code consistency
>
> builtin/receive-pack.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
> index 9f4a0b816cf9..f8b9a9312733 100644
> --- a/builtin/receive-pack.c
> +++ b/builtin/receive-pack.c
> @@ -1971,6 +1971,22 @@ static void execute_commands(struct command *commands,
> return;
> }
>
> + /*
> + * Send a keepalive packet on sideband 2 (progress info) to notice
> + * a client that has disconnected (e.g. killed with ^C) while
> + * pre-receive was running.
> + */
> + if (use_sideband) {
> + static const char buf[] = "0005\2";
> + if (write_in_full(1, buf, sizeof(buf) - 1) < 0) {
> + for (cmd = commands; cmd; cmd = cmd->next) {
> + if (!cmd->error_string)
> + cmd->error_string = "pusher went away";
> + }
> + return;
> + }
> + }
> +
> /*
> * Now we'll start writing out refs, which means the objects need
> * to be in their final positions so that other processes can see them.
I've read the upthread, but I still don't quite get why it's a must to
unconditionally abort the push because the pusher went away.
At this point we've passed the pre-receive hook, are about to migrate
the objects, still have proc-receive left to run, and finally will
update the refs.
Is the motivation purely a UX change where it's considered that the user
*must* be shown the output, or are we doing the wrong thing and not
continuing at all if we run into SIGPIPE here (then presumably only for
hooks that produce output?).
I admit this is somewhat contrived, but aren't we now doing worse for
users where the pre-receive hook takes 10s, but they already asked for
their push to be performed. Then they disconnect from WiFi unexpectedly,
and find that that it didn't go through?
Anyway, I see you made this opt-in configurable in earlier iterations. I
wonder if that's still something worth doing, or if we should just take
this change as-is.
What I don't get is *if* we're doing this for the UX reason why are we
singling out the pre-receive hook in particular, and not covering
proc-receive? I.e. we'll also produce output the user might see there,
as you can see with this ad-hoc testing change (showhing changed "git
push" output when I add to the hook output):
diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c
index cc08506cf0b..933f0599497 100644
--- a/t/helper/test-proc-receive.c
+++ b/t/helper/test-proc-receive.c
@@ -188,6 +188,7 @@ int cmd__proc_receive(int argc, const char **argv)
if (returns.nr)
for_each_string_list_item(item, &returns)
fprintf(stderr, "proc-receive> %s\n", item->string);
+ fprintf(stderr, "showing a custom message\n");
}
if (die_write_report)
$ ./t5411-proc-receive-hook.sh --run=1-3,5-42 -vixd
[...]
+ diff -u expect actual
--- expect 2022-02-04 11:53:52.006413296 +0000
+++ actual 2022-02-04 11:53:52.006413296 +0000
@@ -3,6 +3,7 @@
remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
remote: # proc-receive hook
remote: proc-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
+remote: showing a custom message
remote: # post-receive hook
remote: post-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
To <URL/of/upstream.git>
error: last command exited with $?=1
Is the unstated reason that we consider the tmp_objdir_migrate() more of
a a point of no return?
IOW I'm wondering why it doesn't look more like this (the object
migration could probably be dropped, it should be near-ish instant, but
proc-receive can take a long time):
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f8b9a931273..33bbafbc9e2 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1907,6 +1907,26 @@ static void execute_commands_atomic(struct command *commands,
strbuf_release(&err);
}
+static int pusher_went_away(struct command *commands, const char *msg)
+{
+ struct command *cmd;
+ static const char buf[] = "0005\2";
+
+ /*
+ * Send a keepalive packet on sideband 2 (progress info) to notice
+ * a client that has disconnected (e.g. killed with ^C) while
+ * pre-receive was running.
+ */
+ if (write_in_full(1, buf, sizeof(buf) - 1) < 0) {
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ if (!cmd->error_string)
+ cmd->error_string = msg;
+ }
+ return 1;
+ }
+ return 0;
+}
+
static void execute_commands(struct command *commands,
const char *unpacker_error,
struct shallow_info *si,
@@ -1971,21 +1991,9 @@ static void execute_commands(struct command *commands,
return;
}
- /*
- * Send a keepalive packet on sideband 2 (progress info) to notice
- * a client that has disconnected (e.g. killed with ^C) while
- * pre-receive was running.
- */
- if (use_sideband) {
- static const char buf[] = "0005\2";
- if (write_in_full(1, buf, sizeof(buf) - 1) < 0) {
- for (cmd = commands; cmd; cmd = cmd->next) {
- if (!cmd->error_string)
- cmd->error_string = "pusher went away";
- }
- return;
- }
- }
+ if (use_sideband && pusher_went_away(commands,
+ "pusher can't be contacted post-pre-receive"))
+ return;
/*
* Now we'll start writing out refs, which means the objects need
@@ -2000,6 +2008,10 @@ static void execute_commands(struct command *commands,
}
tmp_objdir = NULL;
+ if (use_sideband && pusher_went_away(commands,
+ "pusher can't be contacted post-object migration"))
+ return;
+
check_aliased_updates(commands);
free(head_name_to_free);
@@ -2013,6 +2025,10 @@ static void execute_commands(struct command *commands,
(cmd->run_proc_receive || use_atomic))
cmd->error_string = "fail to run proc-receive hook";
+ if (use_sideband && pusher_went_away(commands,
+ "pusher can't be contacted post-proc-receive"))
+ return;
+
if (use_atomic)
execute_commands_atomic(commands, si);
else
But also, this whole thing is "if the pre-receive hook etc. etc.", but
we do in fact run this when there's no hook at all. See how this
interacts with run_and_feed_hook() and the "!hook_path" check.
So isn't this unnecessary if there's no such hook, and we should unfold
the find_hook() etc. from that codepath (or pass up a "I ran the hook"
state)?
next prev parent reply other threads:[~2022-02-04 12:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-01-25 9:54 [PATCH] receive-pack: interrupt pre-receive when client disconnects Robin Jarry
2022-01-26 7:17 ` Jiang Xin
2022-01-26 12:46 ` Robin Jarry
2022-01-26 21:44 ` [PATCH v2] receive-pack: add option to interrupt pre-receive when client exits Robin Jarry
2022-01-27 3:21 ` Jiang Xin
2022-01-27 8:38 ` Robin Jarry
2022-01-27 4:36 ` Junio C Hamano
2022-01-27 9:32 ` Robin Jarry
2022-01-27 18:26 ` Junio C Hamano
2022-01-27 20:53 ` Robin Jarry
2022-01-27 21:55 ` [PATCH v3] receive-pack: check if client is alive before completing the push Robin Jarry
2022-01-28 1:19 ` Junio C Hamano
2022-01-28 9:13 ` Robin Jarry
2022-01-28 17:52 ` Junio C Hamano
2022-01-28 19:32 ` Robin Jarry
2022-01-28 19:48 ` [PATCH v4] " Robin Jarry
2022-02-04 11:37 ` Ævar Arnfjörð Bjarmason [this message]
2022-02-04 19:19 ` Junio C Hamano
2022-02-07 19:26 ` Robin Jarry
2022-01-27 23:47 ` [PATCH v2] receive-pack: add option to interrupt pre-receive when client exits Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=220204.864k5e4yvf.gmgdl@evledraar.gmail.com \
--to=avarab@gmail.com \
--cc=emilyshaffer@google.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=nicolas.dichtel@6wind.com \
--cc=patryk.obara@gmail.com \
--cc=robin.jarry@6wind.com \
--cc=zhiyou.jx@alibaba-inc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.