Ruby and Erlang, Talking to Each Other

A presentation for Ruby Users of Minnesota, March 2008.

OSS Packages Currently Available for Ruby/Erlang Communication

Erlang<->C Communication

  • Via an Erlang "driver" port
    1. External process, communicate via pipes, all comm is serialized
      • Safe (can't crash Erlang VM), slow (pipe data xfer, OS context switching, etc.)
    2. Shared library loaded into Erlang VM's memory space
      • Fast, unsafe, must still use driver port API to serialize data in both directions (mostly true)
  • "C-node", a process that implements the Erlang message passing protocol
    • C-nodes look exactly like Erlang nodes
  • Via TCP, UDP, SCTP, files, named pipes, ....

RBridge, Simple Communication

  • TCP client-server design, no chicken-and-egg problem
  • Basic design (if it were glue to Ruby instead): TCP server for "irb"
    • Data conversion to Erlang: ASCII
    • Data conversion from Erlang: ASCII S-Expression formatting -> Ruby types
  • Data type conversion is limited, several Erlang types unsupported

RBridge Example

% /usr/local/lib/ruby/gems/1.8/gems/rbridge-0.1.2/rulang_server/rulang 9900 &
[RBridge Server Started]

% irb
irb> require 'rbridge'
=> true

irb> a = RBridge.new(nil, "localhost", 9900)
=> #<RBridge:0xb7f0ac14 @erlang=#<ErlangAdapter:0xb7f0ab38 @host="localhost", @port=9900>, @mod="erlang", @async=false>

irb> a.erl("[70, 71, 72].")
=> "FGH"

irb> a.erl("[70, 71, 172].")
=> "FG\254"

irb> a.erl("[70, 71, 272].")
=> [70, 71, 272]

irb> a.erl("length(erlang:processes()).")
=> 22

irb> a.erl("erlang:processes().")
RuntimeError: [Error]=>Error: {function_clause,[{local,to_ruby,[<0.0.0>]}]}

        from ./rbridge.rb:77:in `erl'
        from (irb):3

irb> a.erl("spawn(fun() -> ets:new(slf, [set, named_table, public]), receive die -> ok end end).")
RuntimeError: [Error]=>Error: {function_clause,[{local,to_ruby,[<0.68.0>]}]}
        from ./rbridge.rb:77:in `erl'
        from (irb):25

irb> a.erl("ets:insert(slf, {\"scott\", 39, \"louise\"}).")
=> "true"

irb> a.erl("ets:lookup(slf, \"scott\").")
=> [["scott", 39, "louise"]]

RBridge Data Conversion To Ruby

Erlang Type Ruby Type
atom string
list of 0 <= integer <= 255 string
list of anything else array
tuple array
integer integer
float float
any other sorry, no mapping

Critique of RBridge

  • Any Ruby -> Erlang data must be flattened to ASCII, using Erlang syntax
  • Easy to call arbitrary Erlang functions
  • Impedence mismatch: somewhat high

Erlectricity

  1. Start an Erlang virtual machine
  2. Open a driver port to a new Ruby OS process
  3. Send (nearly) arbitrary Erlang term to port
  4. Read Erlang term from port (optional)
  • The Chicken and Egg:
    • The Erlang VM runs first, Ruby's is a child OS process.
    • Ruby's STDIN and STDOUT is used for communication with Erlang

Simple Example

See code in http://www.snookles.com/erlang/ruby.mn-Mar2008/.

% erl
Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.5  (abort with ^G)
1> c(slf1).
{ok, slf1}

7> Port = slf1:start().
#Port<0.111>
Before top of loop

8> slf1:msg(Port, {hello, world}).
you_betcha
At top of loop
Before when 1
After when 1
After when 1b
After when 2
got :hello :world

9> slf1:msg(Port, {hello, <<"world">>}).
you_betcha
got :hello "world"      

11> slf1:msg(Port, {hello, "world!"}).                         
you_betcha
got :hello [119, 111, 114, 108, 100, 33]

13> slf1:msg(Port, {hello, [1, 2, 3, {foo, [], bar}]}).
you_betcha
got :hello [1, 2, 3, [:foo, [], :bar]]

15> slf1:msg(Port, {hello, this, is, a, 6, tuple}).   
you_betcha
./slf1.rb:46: warning: multiple values for a block parameter (6 for 1)
   from /usr/local/lib/ruby/gems/1.8/gems/erlectricity-0.2.0/lib/erlectricity/matcher.rb:14
catchall: [:hello, :this, :is, :a, 6, :tuple]

Reading a Reply Term From Erlang

16> slf1:get(Port).
no_data_in_mailbox

18> slf1:msg(Port, {sleep, 1}).
you_betcha
Going to sleep for 1 seconds ... done

19> slf1:get(Port).            
{done_sleeping,1}

20> slf1:get(Port).
no_data_in_mailbox

32> [ slf1:msg(Port, {sleep, 0.5}) , slf1:get(Port), timer:sleep(1000), slf1:get(Port) ].
Going to sleep for 0.5 seconds ... done
[you_betcha,no_data_in_mailbox,ok,{done_sleeping,0.500000}]

Critique of Erlectricity

  • Ruby code is creating Erlang terms, communication is fairly efficient.
  • Mostly-straightforward mapping of types, back and forth
  • Lists and tuples are the exception
    • Ruby Array -> Erlang tuple
    • Ruby Erlectricity::List -> Erlang list
  • Not so easy call arbitrary Erlang functions
    • But easy enough to fix on app-by-app basis
  • Impedence mismatch: fairly low
  • Wouldn't be too much work to create an independent node
    • Use TCP/IP instead of local pipes
    • Remote nodes can't tell the difference: Erlang, Ruby, Folger's Crystals, ...

Fuzed, Not Mongrel

  • Yes, it works.
  • As of 30-March-2008, don't use the gem. Fetch the code via "git".
  • Packaging is targeted at interactive/development environment right now.
  • Maintains a dynamic pool of client Erlang nodes for processing Rails requests.
    • Dispatch from client pool is LIFO.
    • Each client pool member runs two Ruby OS processes.

-- Main.fritchie - 30 Mar 2008

Topic attachments
I Attachment Action Size Date Who Comment
pngpng fuzed.png manage 8.3 K 31 Mar 2008 - 20:05 Main.fritchie Simple diagram of yaws/fuzed communication
This topic: Main > WebHome > TalksForRUM > RUMPresentationMarch2008
History: r3 - 31 Mar 2008 - 20:05:44 - Main.fritchie
 
This site is powered by the TWiki collaboration platformCopyright by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback