views:

213

answers:

1

This is weird and I'm not sure who the culprit really is.

I'm doing some scripting, on FreeBSD (6.2)? which makes extensive use of the following bashism:

do_something <(mysql --skip-column-names -B -e 'select ... from ... where ...;')

... where "do_something is a somewhat crufty utility (in Perl) that won't read from a pipeline. If I use a regular file it works fine. My bash script using things like exec 4< <(...) with these sorts of queries (following by loops of the form while read x y z <&4; do ... never seem to have any issues.

However, Perl (5.8.x) seems to periodically block (apparently forever). I tried changing out the chomp(my $data = <MYDATA>); with a routine that used sysread and I wrote some test cases in Python for comparison. These seem to block far less often than the idiomatic Perl code, but they still do it sometimes. (The Python code using f.read() or os.read(f.fileno()...) seems to behave about equally in this issue).

I've tried reproducing the issue using ... <(cat ...) (where I'm cating the regular file) and that never seems to reproduce that stall.

I've glanced at some ktrace/kdump data ... but I'm far more familiar with Linux strace or even Solaris truss ... so I haven't figured out what's going from there yet, either.

I suppose we can mostly rule out Perl, because I've reproduced the same issue using Python ... I don't see how the bash could be doing anything wrong here (it's just creating a named pipe in /var/tmp/sh-np-xxx and wiring the processes up to that).

What could the mysql shell/utility be doing that might cause this? I don't think I've seen it from anything else (such as cat or dd). I haven't tested this scenario under Linux ... but I've used <(...) (process substitution) for years under Linux and don't recall ever seeing this.

Is it a FreeBSD issue?

Sure I can work around the issue using temporary files ... but I'd sure rather understand why it's doing this (and avoid some of the races and clean-up messiness that temporary files entail).

Any suggestions?

+3  A: 

The big difference between operating on the output of mysql and directly on a file is timing. When the perl process is stalled, the big question is: "why is it not making forward progress"? You can use the "l" option to ps to see the wait channel for the perl process; that way you can see if it blocked on a read, or if something else is going on. If it is really blocked on pipe input, I expect the MWCHAN entry for perl to be "piperd".

The same information would be interesting for the mysql process.

What does your Python test code look like?

Another way of writing this while avoiding the bashism is this; that would allow you to rule out bash:

mysql --skip-column-names -B -e 'select ... from ... where ...;' | do_something /dev/stdin

Other interesting questions:

  • Does the --unbuffered option to mysql change anything?

  • Does piping the mysql output through dd change anything? (eg. "perlscript <(mysql ... | dd)

Summary: Need more information.

janm
Tried the <(... | cat) and <(... | dd bs=1) hacks without any change in behavior. I'll try mysql's --unbuffered option soon. That sounds intriguing and I hadn't know of it before.
Jim Dennis
The really interesting thing to find out is the state of the mysql and the perl processes when you seem to stop making forward progress. The 'dd' test will be much more interesting with a large buffer than a small buffer. If you make the buffer size larger than the expected output you will have dd emit output in much the same way as a temporary file but without a temporary file.
janm