Re: [RFC] helping smart-http/stateless-rpc fetch race

Subsystems: the rest

3 messages, 3 authors, 2016-06-15 · open the first message on its own page

Re: [RFC] helping smart-http/stateless-rpc fetch race

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:51:46

Junio C Hamano [off-list ref] writes:
Shawn Pearce [off-list ref] writes:
quoted
Why a new --allow-non-tip flag? Why not always do this with the
existing --stateless-rpc flag?
It certainly would be much easier from implementation point of view, but I
did it that way for two and half reasons:

 (1) It might make sense to give admins who run upload-pack not behind
     smart-http an option to allow fetching from a non-tip; and

 (2) It also might make sense to let admins who do run upload-pack behind
     smart-http force re-fetching when the race is encountered.

and the remaining half-reason was that I was too lazy to think things
through to refute the above two "might make sense" and convince myself
that they should instead be "is not necessary".
I still haven't convinced myself but here is a simplified one without the
new option (hence no need to touch the smart-http infrastructure).

-- >8 --
Subject: [PATCH] helping smart-http/stateless-rpc fetch race

A request to fetch from a client over smart HTTP protocol is served in
multiple steps. In the first round, the server side shows the set of refs
it has and their values, and the client picks from them and sends "I want
to fetch the history leading to these commits".

When the server tries to respond to this second request, its refs may have
progressed by a push from elsewhere. By design, we do not allow fetching
objects that are not at the tip of an advertised ref, and the server
rejects such a request. The client needs to try again, which is not ideal
especially for a busy server.

Teach upload-pack (which is the workhorse driven by git-daemon and smart
http server interface) that it is OK for a smart-http client to ask for
commits that are not at the tip of any advertised ref, as long as they are
reachable from advertised refs.

Signed-off-by: Junio C Hamano <redacted>
---
 upload-pack.c |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 99 insertions(+), 9 deletions(-)
diff --git a/upload-pack.c b/upload-pack.c
index ce5cbbe..30cf941 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -10,6 +10,7 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "run-command.h"
+#include "sigchain.h"
 
 static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 
@@ -498,11 +499,96 @@ static int get_common_commits(void)
 	}
 }
 
+static void check_non_tip(void)
+{
+	static const char *argv[] = {
+		"rev-list", "--stdin", NULL,
+	};
+	static struct child_process cmd;
+	struct object *o;
+	char namebuf[42]; /* ^ + SHA-1 + LF */
+	int i;
+
+	/* In the normal in-process case non-tip request can never happen */
+	if (!stateless_rpc)
+		goto error;
+
+	cmd.argv = argv;
+	cmd.git_cmd = 1;
+	cmd.no_stderr = 1;
+	cmd.in = -1;
+	cmd.out = -1;
+
+	if (start_command(&cmd))
+		goto error;
+
+	/*
+	 * If rev-list --stdin encounters an unknown commit, it
+	 * terminates, which will cause SIGPIPE in the write loop
+	 * below.
+	 */
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	namebuf[0] = '^';
+	namebuf[41] = '\n';
+	for (i = get_max_object_index(); 0 < i; ) {
+		o = get_indexed_object(--i);
+		if (!(o->flags & OUR_REF))
+			continue;
+		memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
+		if (write_in_full(cmd.in, namebuf, 42) < 0)
+			goto error;
+	}
+	namebuf[40] = '\n';
+	for (i = 0; i < want_obj.nr; i++) {
+		o = want_obj.objects[i].item;
+		if (o->flags & OUR_REF)
+			continue;
+		memcpy(namebuf, sha1_to_hex(o->sha1), 40);
+		if (write_in_full(cmd.in, namebuf, 41) < 0)
+			goto error;
+	}
+	close(cmd.in);
+
+	sigchain_pop(SIGPIPE);
+
+	/*
+	 * The commits out of the rev-list are not ancestors of
+	 * our ref.
+	 */
+	i = read_in_full(cmd.out, namebuf, 1);
+	if (i)
+		goto error;
+	close(cmd.out);
+
+	/*
+	 * rev-list may have died by encountering a bad commit
+	 * in the history, in which case we do want to bail out
+	 * even when it showed no commit.
+	 */
+	if (finish_command(&cmd))
+		goto error;
+
+	/* All the non-tip ones are ancestors of what we advertised */
+	return;
+
+error:
+	/* Pick one of them (we know there at least is one) */
+	for (i = 0; i < want_obj.nr; i++) {
+		o = want_obj.objects[i].item;
+		if (!(o->flags & OUR_REF))
+			break;
+	}
+	die("git upload-pack: not our ref %s",
+	    sha1_to_hex(o->sha1));
+}
+
 static void receive_needs(void)
 {
 	struct object_array shallows = OBJECT_ARRAY_INIT;
 	static char line[1000];
 	int len, depth = 0;
+	int has_non_tip = 0;
 
 	shallow_nr = 0;
 	if (debug_fd)
@@ -559,26 +645,30 @@ static void receive_needs(void)
 		if (strstr(line+45, "include-tag"))
 			use_include_tag = 1;
 
-		/* We have sent all our refs already, and the other end
-		 * should have chosen out of them; otherwise they are
-		 * asking for nonsense.
-		 *
-		 * Hmph.  We may later want to allow "want" line that
-		 * asks for something like "master~10" (symbolic)...
-		 * would it make sense?  I don't know.
-		 */
 		o = lookup_object(sha1_buf);
-		if (!o || !(o->flags & OUR_REF))
+		if (!o)
 			die("git upload-pack: not our ref %s",
 			    sha1_to_hex(sha1_buf));
 		if (!(o->flags & WANTED)) {
 			o->flags |= WANTED;
+			if (!(o->flags & OUR_REF))
+				has_non_tip = 1;
 			add_object_array(o, NULL, &want_obj);
 		}
 	}
 	if (debug_fd)
 		write_str_in_full(debug_fd, "#E\n");
 
+	/*
+	 * We have sent all our refs already, and the other end
+	 * should have chosen out of them. When we are operating
+	 * in the stateless RPC mode, however, their choice may
+	 * have been based on the set of older refs advertised
+	 * by another process that handled the initial request.
+	 */
+	if (has_non_tip)
+		check_non_tip();
+
 	if (!use_sideband && daemon_mode)
 		no_progress = 1;
 
-- 
1.7.6.409.ge7a85

Re: [RFC] helping smart-http/stateless-rpc fetch race

From: Sverre Rabbelier <hidden>
Date: 2016-06-15 22:51:46

Heya,

On Mon, Aug 8, 2011 at 19:13, Junio C Hamano [off-list ref] wrote:
quoted
 (1) It might make sense to give admins who run upload-pack not behind
     smart-http an option to allow fetching from a non-tip; and
You said earlier it isn't needed since the server process caches the
refs for git and ssh, that leaves dumb-http right? If that is indeed
the case I think we can just argue that since smart-http is our
solution to the http problems, if admins want to make life easier for
http fetches on busy repositories they should be using smart-http.
quoted
 (2) It also might make sense to let admins who do run upload-pack behind
     smart-http force re-fetching when the race is encountered.
This would mean that if you're running smart-http without this option
enabled (because, say, you don't know it exists), your users have to
re-fetch (a lot). The only upside would be that if the server _knows_
what the user is asking for is outdated, that the user will know this
right away. That doesn't fly though, since we allow exactly that for
git and ssh transfer.

-- 
Cheers,

Sverre Rabbelier

Re: [RFC] helping smart-http/stateless-rpc fetch race

From: Ilari Liusvaara <hidden>
Date: 2016-06-15 22:51:46

On Mon, Aug 08, 2011 at 11:05:27PM +0200, Sverre Rabbelier wrote:
Heya,

On Mon, Aug 8, 2011 at 19:13, Junio C Hamano [off-list ref] wrote:
quoted
quoted
 (1) It might make sense to give admins who run upload-pack not behind
     smart-http an option to allow fetching from a non-tip; and
You said earlier it isn't needed since the server process caches the
refs for git and ssh, that leaves dumb-http right?
It seems that everything currently possible falls into three
categories:

1) Stateful upload-pack (git://, file://, ssh://, CONNECT): No fix
needed.
2) Stateless upload-pack (smart http://, some bizarre helper):
Needs fix to avoid races.
3) Dumb protocols (dumb http://, ftp://, rsync://): Won't invoke
upload-pack anyway, no fix needed.

So I think that the only thing that needs the option to allow
fetching from non-tips is anything using --stateless-rpc.

-Ilari
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help