Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 1, 2013 at 11:19 PM, Felipe Contreras felipe.contre...@gmail.com wrote: On Mon, Apr 1, 2013 at 11:01 PM, Jeff King p...@peff.net wrote: On Mon, Apr 01, 2013 at 10:51:20PM -0600, Felipe Contreras wrote: So in fetch_with_import, we have a remote-helper, and we have a bidirectional pipe to it. We then call get_importer, which starts fast-import, whose stdin is connected to the stdout of the remote helper. We tell the remote-helper to run the import, then we wait for fast-import to finish (and complain if it fails). Then what? We seem to do some more work, which I think is what causes the errors you see; but should we instead be reaping the helper at this point unconditionally? Its stdout has presumably been flushed out to fast-import; is there anything else for us to get from it besides its exit code? The problem is not with import, since fast-import would generally wait properly for a 'done' status, the problem is with export. Your patch modified fetch_with_import. Are you saying that it isn't necessary to do so? It's not, I added it for symmetry. But that's the case *if* the remote-helper is properly using the done feature. Actually, it is a problem, because without this check the transport-helper just goes on without realizing the whole thing has failed and doesn't produce a proper error message: fatal: bad object error: testgit::/home/felipec/dev/git/t/trash directory.t5801-remote-helpers/server did not send all necessary objects It's possible to send a ping command to the remote-helper, but doing so triggers a SIGPIPE. I would rather show a proper error message as my patch suggests by just checking if the command is running. Also, the design is such that the remote-helper stays alive, even after fast-export has finished. So if we expect to be able to communicate with the remote-helper after fast-export has exited, is it a protocol failure that the helper does not say yes, I finished the export or similar? If so, can we fix that? I am not too familiar with this protocol, but it looks like we read from helper-out right after closing the exporter, to get the ref statuses. Shouldn't we be detecting the error if the helper hangs up there? I guess that should be possible, I'll give that a try. I gave this a try and it does work, but it seems rather convoluted to me: diff --git a/transport-helper.c b/transport-helper.c index f0d28aa..10b7842 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -25,7 +25,8 @@ struct helper_data { option : 1, push : 1, connect : 1, - no_disconnect_req : 1; + no_disconnect_req : 1, + done_export : 1; char *export_marks; char *import_marks; /* These go from remote name (as in list) to private name */ @@ -46,7 +47,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer) die_errno(Full write to remote helper failed); } -static int recvline_fh(FILE *helper, struct strbuf *buffer) +static int recvline_fh(FILE *helper, struct strbuf *buffer, int safe) { strbuf_reset(buffer); if (debug) @@ -54,7 +55,10 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer) if (strbuf_getline(buffer, helper, '\n') == EOF) { if (debug) fprintf(stderr, Debug: Remote helper quit.\n); - exit(128); + if (safe) + return -1; + else + exit(128); } if (debug) @@ -64,7 +68,12 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer) static int recvline(struct helper_data *helper, struct strbuf *buffer) { - return recvline_fh(helper-out, buffer); + return recvline_fh(helper-out, buffer, 0); +} + +static int recvline_safe(struct helper_data *helper, struct strbuf *buffer) +{ + return recvline_fh(helper-out, buffer, 1); } static void xchgline(struct helper_data *helper, struct strbuf *buffer) @@ -201,6 +210,8 @@ static struct child_process *get_helper(struct transport *transport) strbuf_addstr(arg, --import-marks=); strbuf_addstr(arg, capname + strlen(import-marks )); data-import_marks = strbuf_detach(arg, NULL); + } else if (!strcmp(capname, done-export)) { + data-done_export = 1; } else if (mandatory) { die(Unknown mandatory capability %s. This remote helper probably needs newer version of Git., @@ -540,7 +551,7 @@ static int process_connect_service(struct transport *transport, goto exit; sendline(data, cmdbuf); - recvline_fh(input, cmdbuf); + recvline_fh(input, cmdbuf, 0); if (!strcmp(cmdbuf.buf, )) {
[PATCH 2/4] transport-helper: check if remote helper is alive
Otherwise transport-helper will continue checking for refs and other things what will confuse the user more. --- git-remote-testgit| 11 +++ t/t5801-remote-helpers.sh | 19 +++ transport-helper.c| 8 3 files changed, 38 insertions(+) diff --git a/git-remote-testgit b/git-remote-testgit index b395c8d..ca0cf09 100755 --- a/git-remote-testgit +++ b/git-remote-testgit @@ -61,12 +61,23 @@ do echo feature import-marks=$gitmarks echo feature export-marks=$gitmarks fi + + if test -n $GIT_REMOTE_TESTGIT_FAILURE + then + exit -1 + fi + echo feature done git fast-export ${testgitmarks_args[@]} $refs | sed -e s#refs/heads/#${prefix}/heads/#g echo done ;; export) + if test -n $GIT_REMOTE_TESTGIT_FAILURE + then + exit -1 + fi + before=$(git for-each-ref --format='%(refname) %(objectname)') git fast-import ${testgitmarks_args[@]} --quiet diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index f387027..26e9a5b 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh @@ -166,4 +166,23 @@ test_expect_success 'push ref with existing object' ' compare_refs local dup server dup ' +test_expect_success 'proper failure checks for fetching' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 + export GIT_REMOTE_TESTGIT_FAILURE + cd local + test_must_fail git fetch 21 | \ + grep Error while running helper + ) +' + +# We sleep to give fast-export a chance to catch the SIGPIPE +test_expect_failure 'proper failure checks for pushing' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 + export GIT_REMOTE_TESTGIT_FAILURE + cd local + test_must_fail git push --all 21 | \ + grep Error while running helper + ) +' + test_done diff --git a/transport-helper.c b/transport-helper.c index cb3ef7d..dfdfa7a 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -460,6 +460,10 @@ static int fetch_with_import(struct transport *transport, if (finish_command(fastimport)) die(Error while running fast-import); + + if (!check_command(data-helper)) + die(Error while running helper); + argv_array_free_detached(fastimport.argv); /* @@ -818,6 +822,10 @@ static int push_refs_with_export(struct transport *transport, if (finish_command(exporter)) die(Error while running fast-export); + + if (!check_command(data-helper)) + die(Error while running helper); + push_update_refs_status(data, remote_refs); return 0; } -- 1.8.2 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 01, 2013 at 03:46:42PM -0600, Felipe Contreras wrote: Otherwise transport-helper will continue checking for refs and other things what will confuse the user more. [...] diff --git a/transport-helper.c b/transport-helper.c index cb3ef7d..dfdfa7a 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -460,6 +460,10 @@ static int fetch_with_import(struct transport *transport, if (finish_command(fastimport)) die(Error while running fast-import); + + if (!check_command(data-helper)) + die(Error while running helper); + argv_array_free_detached(fastimport.argv); Can you be more specific about what happens when we miss the death here, what happens next, etc? Checking asynchronously for death like this is subject to a race condition; the helper may be about to die but not have died yet. In practice we may catch some cases, but this seems like an indication that the protocol is not well thought-out. Usually we would wait for a confirmation over the read pipe from a child, and know that the child failed when either: 1. It tells us so on the pipe. 2. The pipe closes (at which point we know it is time to reap the child). Why doesn't that scheme work here? I am not doubting you that it does not; the import helper protocol is a bit of a mess, and I can easily believe it has such a problem. But I'm wondering if it's possible to improve it in a more robust way. -Peff -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 1, 2013 at 5:33 PM, Jeff King p...@peff.net wrote: On Mon, Apr 01, 2013 at 03:46:42PM -0600, Felipe Contreras wrote: Otherwise transport-helper will continue checking for refs and other things what will confuse the user more. [...] diff --git a/transport-helper.c b/transport-helper.c index cb3ef7d..dfdfa7a 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -460,6 +460,10 @@ static int fetch_with_import(struct transport *transport, if (finish_command(fastimport)) die(Error while running fast-import); + + if (!check_command(data-helper)) + die(Error while running helper); + argv_array_free_detached(fastimport.argv); Can you be more specific about what happens when we miss the death here, what happens next, etc? I have seen problems sporadically, like git trying to update refs to object that don't exist. I also remember seeing mismatches between the marks and the remote branches refs. Checking asynchronously for death like this is subject to a rac condition; the helper may be about to die but not have died yet. In practice we may catch some cases, but this seems like an indication that the protocol is not well thought-out. Usually we would wait for a confirmation over the read pipe from a child, and know that the child failed when either: 1. It tells us so on the pipe. 2. The pipe closes (at which point we know it is time to reap the child). Why doesn't that scheme work here? I am not doubting you that it does not; the import helper protocol is a bit of a mess, and I can easily believe it has such a problem. But I'm wondering if it's possible to improve it in a more robust way. The pipe is between fast-export and the remote-helper, we (transport-helper) are not part of the pipe any more. That's the problem. Cheers. -- Felipe Contreras -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
Felipe Contreras felipe.contre...@gmail.com writes: Otherwise transport-helper will continue checking for refs and other things what will confuse the user more. --- Sign-off? git-remote-testgit| 11 +++ t/t5801-remote-helpers.sh | 19 +++ transport-helper.c| 8 3 files changed, 38 insertions(+) diff --git a/git-remote-testgit b/git-remote-testgit index b395c8d..ca0cf09 100755 --- a/git-remote-testgit +++ b/git-remote-testgit @@ -61,12 +61,23 @@ do echo feature import-marks=$gitmarks echo feature export-marks=$gitmarks fi + + if test -n $GIT_REMOTE_TESTGIT_FAILURE + then + exit -1 + fi + echo feature done git fast-export ${testgitmarks_args[@]} $refs | sed -e s#refs/heads/#${prefix}/heads/#g echo done ;; export) + if test -n $GIT_REMOTE_TESTGIT_FAILURE + then + exit -1 + fi + before=$(git for-each-ref --format='%(refname) %(objectname)') git fast-import ${testgitmarks_args[@]} --quiet diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index f387027..26e9a5b 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh @@ -166,4 +166,23 @@ test_expect_success 'push ref with existing object' ' compare_refs local dup server dup ' +test_expect_success 'proper failure checks for fetching' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 + export GIT_REMOTE_TESTGIT_FAILURE + cd local + test_must_fail git fetch 21 | \ + grep Error while running helper This will not care if git fetch succeeds or fails and returns the exit code from grep. Perhaps something like this instead? ( GIT_REMOTE_TESTGIT_FAILURE=1 export GIT_REMOTE_TESTGIT_FAILURE cd local test_must_fail git fetch 2error grep Error while running helper error ) +# We sleep to give fast-export a chance to catch the SIGPIPE +test_expect_failure 'proper failure checks for pushing' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 + export GIT_REMOTE_TESTGIT_FAILURE + cd local + test_must_fail git push --all 21 | \ + grep Error while running helper Ditto. + ) +' + test_done diff --git a/transport-helper.c b/transport-helper.c index cb3ef7d..dfdfa7a 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -460,6 +460,10 @@ static int fetch_with_import(struct transport *transport, if (finish_command(fastimport)) die(Error while running fast-import); + + if (!check_command(data-helper)) + die(Error while running helper); + argv_array_free_detached(fastimport.argv); /* @@ -818,6 +822,10 @@ static int push_refs_with_export(struct transport *transport, if (finish_command(exporter)) die(Error while running fast-export); + + if (!check_command(data-helper)) + die(Error while running helper); + push_update_refs_status(data, remote_refs); return 0; } OK, so the idea is that fetch_with_import() does - get_helper(transport), which spawns a helper process; - get_importer(transport, fastimport), which spawns a fast-import and make it read from the output of the helper process; - we did finish_command() to wait for the fast-import to finish, expecting that the fast-import would finish when the helper stops feeding it, which in turn would mean the helper would have died. The same for the pushing side. Shouldn't transport_disconnect() have called release_helper() which in turn calls disconnect_helper() to call finish_command() on the helper to wait for that procesanyway? Is somebody discarding return value from transport_disconnect() or the current calling site of transport_disconnect() is too late to notice the error? Puzzled... -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 01, 2013 at 06:12:45PM -0600, Felipe Contreras wrote: Checking asynchronously for death like this is subject to a rac condition; the helper may be about to die but not have died yet. In practice we may catch some cases, but this seems like an indication that the protocol is not well thought-out. Usually we would wait for a confirmation over the read pipe from a child, and know that the child failed when either: 1. It tells us so on the pipe. 2. The pipe closes (at which point we know it is time to reap the child). Why doesn't that scheme work here? I am not doubting you that it does not; the import helper protocol is a bit of a mess, and I can easily believe it has such a problem. But I'm wondering if it's possible to improve it in a more robust way. The pipe is between fast-export and the remote-helper, we (transport-helper) are not part of the pipe any more. That's the problem. So in fetch_with_import, we have a remote-helper, and we have a bidirectional pipe to it. We then call get_importer, which starts fast-import, whose stdin is connected to the stdout of the remote helper. We tell the remote-helper to run the import, then we wait for fast-import to finish (and complain if it fails). Then what? We seem to do some more work, which I think is what causes the errors you see; but should we instead be reaping the helper at this point unconditionally? Its stdout has presumably been flushed out to fast-import; is there anything else for us to get from it besides its exit code? -Peff -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 1, 2013 at 8:30 PM, Jeff King p...@peff.net wrote: On Mon, Apr 01, 2013 at 06:12:45PM -0600, Felipe Contreras wrote: Checking asynchronously for death like this is subject to a rac condition; the helper may be about to die but not have died yet. In practice we may catch some cases, but this seems like an indication that the protocol is not well thought-out. Usually we would wait for a confirmation over the read pipe from a child, and know that the child failed when either: 1. It tells us so on the pipe. 2. The pipe closes (at which point we know it is time to reap the child). Why doesn't that scheme work here? I am not doubting you that it does not; the import helper protocol is a bit of a mess, and I can easily believe it has such a problem. But I'm wondering if it's possible to improve it in a more robust way. The pipe is between fast-export and the remote-helper, we (transport-helper) are not part of the pipe any more. That's the problem. So in fetch_with_import, we have a remote-helper, and we have a bidirectional pipe to it. We then call get_importer, which starts fast-import, whose stdin is connected to the stdout of the remote helper. We tell the remote-helper to run the import, then we wait for fast-import to finish (and complain if it fails). Then what? We seem to do some more work, which I think is what causes the errors you see; but should we instead be reaping the helper at this point unconditionally? Its stdout has presumably been flushed out to fast-import; is there anything else for us to get from it besides its exit code? The problem is not with import, since fast-import would generally wait properly for a 'done' status, the problem is with export. Also, the design is such that the remote-helper stays alive, even after fast-export has finished. -- Felipe Contreras -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 1, 2013 at 6:26 PM, Junio C Hamano gits...@pobox.com wrote: OK, so the idea is that fetch_with_import() does - get_helper(transport), which spawns a helper process; - get_importer(transport, fastimport), which spawns a fast-import and make it read from the output of the helper process; - we did finish_command() to wait for the fast-import to finish, expecting that the fast-import would finish when the helper stops feeding it, which in turn would mean the helper would have died. The same for the pushing side. The difference with the pushing side is that it's the helper the one waiting for fast-export and it can easily die. Shouldn't transport_disconnect() have called release_helper() which in turn calls disconnect_helper() to call finish_command() on the helper to wait for that procesanyway? Is somebody discarding return value from transport_disconnect() or the current calling site of transport_disconnect() is too late to notice the error? It's too late to notice the error. However, only in the case of pushing. -- Felipe Contreras -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 01, 2013 at 10:51:20PM -0600, Felipe Contreras wrote: So in fetch_with_import, we have a remote-helper, and we have a bidirectional pipe to it. We then call get_importer, which starts fast-import, whose stdin is connected to the stdout of the remote helper. We tell the remote-helper to run the import, then we wait for fast-import to finish (and complain if it fails). Then what? We seem to do some more work, which I think is what causes the errors you see; but should we instead be reaping the helper at this point unconditionally? Its stdout has presumably been flushed out to fast-import; is there anything else for us to get from it besides its exit code? The problem is not with import, since fast-import would generally wait properly for a 'done' status, the problem is with export. Your patch modified fetch_with_import. Are you saying that it isn't necessary to do so? Also, the design is such that the remote-helper stays alive, even after fast-export has finished. So if we expect to be able to communicate with the remote-helper after fast-export has exited, is it a protocol failure that the helper does not say yes, I finished the export or similar? If so, can we fix that? I am not too familiar with this protocol, but it looks like we read from helper-out right after closing the exporter, to get the ref statuses. Shouldn't we be detecting the error if the helper hangs up there? -Peff -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 2/4] transport-helper: check if remote helper is alive
On Mon, Apr 1, 2013 at 11:01 PM, Jeff King p...@peff.net wrote: On Mon, Apr 01, 2013 at 10:51:20PM -0600, Felipe Contreras wrote: So in fetch_with_import, we have a remote-helper, and we have a bidirectional pipe to it. We then call get_importer, which starts fast-import, whose stdin is connected to the stdout of the remote helper. We tell the remote-helper to run the import, then we wait for fast-import to finish (and complain if it fails). Then what? We seem to do some more work, which I think is what causes the errors you see; but should we instead be reaping the helper at this point unconditionally? Its stdout has presumably been flushed out to fast-import; is there anything else for us to get from it besides its exit code? The problem is not with import, since fast-import would generally wait properly for a 'done' status, the problem is with export. Your patch modified fetch_with_import. Are you saying that it isn't necessary to do so? It's not, I added it for symmetry. But that's the case *if* the remote-helper is properly using the done feature. Also, the design is such that the remote-helper stays alive, even after fast-export has finished. So if we expect to be able to communicate with the remote-helper after fast-export has exited, is it a protocol failure that the helper does not say yes, I finished the export or similar? If so, can we fix that? I am not too familiar with this protocol, but it looks like we read from helper-out right after closing the exporter, to get the ref statuses. Shouldn't we be detecting the error if the helper hangs up there? I guess that should be possible, I'll give that a try. Cheers. -- Felipe Contreras -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html