Project

General

Profile

Bug #14471 » echo_client.rb

jrafanie (Joe Rafaniello), 02/13/2018 05:06 PM

 
# 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) }
(2-2/3)