|
# This script demonstrates the issue with mixing DRb calls and
|
|
# forking child processes.
|
|
#
|
|
# If the parent process calls a remote DRb method prior to forking
|
|
# a child process it appears there is still a connection in the
|
|
# DRbConn global connection pool. This seems to cause messages to
|
|
# be sent to the wrong client and typically leads to one of three
|
|
# symptoms:
|
|
#
|
|
# 1. A message is sent to the wrong client
|
|
# Ex: 'Failure! expected BBBB but got AAAA'
|
|
#
|
|
# 2. A message is spliced and what should be the message size is another string
|
|
# Ex: 'Failure! too large packet 1090927110'
|
|
#
|
|
# 3. A message is spliced and what should be the Marshal version is another string
|
|
# Ex: 'Failure! incompatible marshal file format (can't be read)
|
|
# format version 4.8 required; 14.4 given'
|
|
#
|
|
# For this case we added some client-side logging to dump the hex_bytes of the string:
|
|
# [TypeError]: incompatible marshal file format (can't be read)
|
|
# format version 4.8 required; 58.12 given
|
|
# 0x00000000 3a 0c 56 69 6d 48 61 73 68 7b 09 49 22 0b 65 6e :.VimHash{.I".en
|
|
# 0x00000010 74 69 74 79 06 3a 06 45 54 49 43 3a 0e 56 69 6d tity.:.ETIC:.Vim
|
|
# 0x00000020 53 74 72 69 6e 67 22 0d 76 6d 2d 31 38 37 35 37 String".vm-18757
|
|
#
|
|
# The first bytes here which should be the marshal version look like the middle of
|
|
# another message.
|
|
|
|
require 'drb/drb'
|
|
|
|
def echo_client(str)
|
|
echo_obj = DRbObject.new_with_uri('druby://localhost:9999')
|
|
|
|
1_000.times do
|
|
begin
|
|
ret = echo_obj.echo(str)
|
|
if ret != str
|
|
puts "PID #{Process.pid}: Failure! expected #{str} but got #{ret}"
|
|
exit 1
|
|
end
|
|
rescue TypeError, DRb::DRbConnError => err
|
|
puts "PID #{Process.pid}: Failure! #{err}"
|
|
exit 1
|
|
end
|
|
end
|
|
puts "PID #{Process.pid}: Succeded."
|
|
end
|
|
|
|
# Execute a DRb call in the parent process prior to forking the children
|
|
echo_obj = DRbObject.new_with_uri('druby://localhost:9999')
|
|
echo_obj.echo("CCCC") # Comment this out and the script passes
|
|
|
|
# Create 20 child processes to echo some string to the server and verify
|
|
# the same string was returned
|
|
child_pids = []
|
|
%w(AAAA BBBB).each do |str|
|
|
10.times do
|
|
child_pids << Kernel.fork { echo_client(str) }
|
|
end
|
|
end
|
|
|
|
child_pids.each { |pid| Process.wait(pid) }
|