Project

General

Profile

Bug #14471 ยป echo_client.rb

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

 
1
# This script demonstrates the issue with mixing DRb calls and
2
# forking child processes.
3
#
4
# If the parent process calls a remote DRb method prior to forking
5
# a child process it appears there is still a connection in the
6
# DRbConn global connection pool.  This seems to cause messages to
7
# be sent to the wrong client and typically leads to one of three
8
# symptoms:
9
#
10
# 1. A message is sent to the wrong client
11
#    Ex: 'Failure! expected BBBB but got AAAA'
12
#
13
# 2. A message is spliced and what should be the message size is another string
14
#    Ex: 'Failure! too large packet 1090927110'
15
#
16
# 3. A message is spliced and what should be the Marshal version is another string
17
#    Ex: 'Failure! incompatible marshal file format (can't be read)
18
#         format version 4.8 required; 14.4 given'
19
#
20
#    For this case we added some client-side logging to dump the hex_bytes of the string:
21
#    [TypeError]: incompatible marshal file format (can't be read)
22
#      format version 4.8 required; 58.12 given
23
#    0x00000000  3a 0c 56 69 6d 48 61 73 68 7b 09 49 22 0b 65 6e  :.VimHash{.I".en
24
#    0x00000010  74 69 74 79 06 3a 06 45 54 49 43 3a 0e 56 69 6d  tity.:.ETIC:.Vim
25
#    0x00000020  53 74 72 69 6e 67 22 0d 76 6d 2d 31 38 37 35 37  String".vm-18757
26
#
27
#    The first bytes here which should be the marshal version look like the middle of
28
#    another message.
29

    
30
require 'drb/drb'
31

    
32
def echo_client(str)
33
  echo_obj = DRbObject.new_with_uri('druby://localhost:9999')
34

    
35
  1_000.times do
36
    begin
37
      ret = echo_obj.echo(str)
38
      if ret != str
39
        puts "PID #{Process.pid}: Failure! expected #{str} but got #{ret}"
40
        exit 1
41
      end
42
    rescue TypeError, DRb::DRbConnError => err
43
      puts "PID #{Process.pid}: Failure! #{err}"
44
      exit 1
45
    end
46
  end
47
  puts "PID #{Process.pid}: Succeded."
48
end
49

    
50
# Execute a DRb call in the parent process prior to forking the children
51
echo_obj = DRbObject.new_with_uri('druby://localhost:9999')
52
echo_obj.echo("CCCC")  # Comment this out and the script passes
53

    
54
# Create 20 child processes to echo some string to the server and verify
55
# the same string was returned
56
child_pids = []
57
%w(AAAA BBBB).each do |str|
58
  10.times do
59
    child_pids << Kernel.fork { echo_client(str) }
60
  end
61
end
62

    
63
child_pids.each { |pid| Process.wait(pid) }