Subject: | Git::Repository / System::Command modules |
Date: | Tue, 10 May 2016 13:43:41 +0200 |
To: | bug-git-repository [...] rt.cpan.org |
From: | Lisa Hansen <Lisa.Hansen [...] sas.com> |
Hi,
I have been using your Git::Repository module for some time, with great success (it was in fact the only Git module I could ever get working in Windows). Unfortunately I have run into a problem (due to a stupid hook that I am not able to change), which I am not sure if it is related to System::Command or IPC::Run, but I am hoping that you can help me.
My apologies if this seems like a long story...
A couple of years ago, Git::Repository ran perfect on Windows 2003. When forced to upgrade to Windows 2008, we started having hangs, but only every 3 or 4 weeks and occasionally a few times over the period of a day or two and then nothing again for a few weeks. Basically the three pipes would be left open, and the script would hang, even if I forked the Git::Repository "run" command. I wrote this off as a problem with IPC::Run and I created a way to detect when a process was hanging and email me and then I manually killed the pipe to stderr, and the script continued as it should (I usually got all of the output to stdout, and this was good enough) or I could easily recover from that point and re-run our process.
You have recently made great improvements and most of the hangs were resolved, but I found that on one of the repositories we use, there is a server side trigger that prints a diff when running git push, and if there were many files changed, IPC::Run would hang because the stderr pipe would receive too much data (sometimes a change was several hundred files and therefore the output was huge). Since the output was coming from the server, using a --quiet for the git push command didn't work. But with your new changes, the hang was different and with the Git::Repository "run" command forked, my script could automatically detect when it was hanging and kill the command (but then I didn't get any output from the command) and then an automatic retry of the command would say everything was up to date, and no manual intervention was required. And since I could actually see what was happening, I also found a way to reproduce it and attempted to fix System::Command.
Unfortunately, I could not find a solution that I can submit to you for a fix, but I can tell you what I tried. Then, maybe you can fix it, or you can tell me if I need to open a bug against IPC::Run, if it isn't something you can fix (or isn't something you want to fix because it isn't a problem with your module).
The first thing I tried was changing System::Command::Reaper.pm from:
$out and $out->opened and $out->close || carp "error closing stdout: $!";
$err and $err->opened and $err->close || carp "error closing stderr: $!";
To:
$out and $out->opened and $out->flush and $out->close || carp "error closing stdout: $!";
$err and $err->opened and $err->flush and $err->close || carp "error closing stderr: $!";
This at least gave me the output that System::Command had received, but the process didn't continue. I am passing this on, because I think that maybe this would still be a good idea to implement, because at least if someone encounters a similar error and kills the command, they might see some output.
Then I tried changing the stderr pipe to use a file instead, so, from:
$pid = IPC::Run::start(
[@cmd],
'<pipe' => $in,
'>pipe' => $out,
'2>pipe' => $err,
);
To:
my ($fh, $file);
eval {
($fh, $file) = tempfile(TEMPLATE => 'errXXXXX',
DIR => $ENV{TEMP},
SUFFIX => '.txt',
UNLINK => 1);
};
if ($@) {
croak "error: Unable to open temp file to capture stderr: $@\n";
} else {
#this call passes stderr via a file
$pid = IPC::Run::start(
debug => 0,
[@cmd],
'<pipe' => $in,
'>pipe' => $out,
'2>' => $file,
);
$err = \*$fh;
}
But I found that the following no longer worked:
$cmd = $r->command( 'checkout', $branch );
@errput = $cmd->stderr->getlines();
I, for the life of me, cannot figure out why getlines doesn't read from the file handle, but maybe you can figure that out.
Then I changed it to:
my ($fh, $file);
eval {
($fh, $file) = tempfile(TEMPLATE => 'errXXXXX',
DIR => $ENV{TEMP},
SUFFIX => '.txt',
UNLINK => 1,
EXLOCK => 0
);
};
if ($@) {
croak "error: Unable to open temp file to capture stderr: $@\n";
} else {
#this call combines stderr with stdout
$pid = IPC::Run::start(
debug => 0,
[@cmd],
'<pipe' => $in,
'>pipe' => $out,
'2>&1'
);
$err = \*$fh; #using an empty temp file keeps the reaper for complaining that $err isn't open
}
A surprise to me, but this doesn't hang. Of course it only works because all output goes to stdout and therefore this still doesn't work:
$cmd = $r->command( 'checkout', $branch );
@errput = $cmd->stderr->getlines();
But to get what I need, I can just use the following command, and parse the output looking for fatal or error after running this:
@output = $r->run( 'checkout', $branch );
Anyway, I hope this is useful and I hope you can help me and fix System::Command so it won't hang. If you need more information, please let me know.
Kind Regards,
Lisa