Kobako: From Ruby to mruby
This article is translated by AI, if have any corrections please let me know.
Choosing mruby as the sandbox language came out of ruby.wasm’s limitations, but sharing the same language standard (ISO/IEC 30170:2012) with CRuby doesn’t mean the goal comes easily. Compared to CRuby, mruby comes with plenty of restrictions.
Those are the trade-offs mruby has to make to run in environments like embedded systems, and the lightweight nature that comes with them happens to be an advantage for an Embedded Sandbox. It turns the idea of embedding a Ruby sandbox into any language into a viable option.
Marshal
dRuby can implement RPC (Remote Procedure Call) in very little code because it makes good use of Ruby’s language features. As I mentioned in the previous post, the method_missing mechanism lets a call go through normally even when the method isn’t defined on the remote side.
When it comes to transferring data, Ruby’s Marshal mechanism takes over. When we need to send an object, calling .dump is enough to turn it into binary data.
1args = [1, 2, 3]
2bin = Marshal.dump(args) # => \x00 ...With that, the arguments a remote method call needs can be passed across to the other side.
1class Proxy
2 def method_missing(method, *args, **kwargs)
3 raise if @target.respond_to(method)
4
5 # @server.send(method, *args, **kwargs)
6 bin = Marshal.dump([method, args, kwargs])
7 @server.write(bin)
8 res = Marshal.load(@server.read)
9 end
10endThat takes care of the whole transport. Switching over to communication between Ruby and mruby, the intuition is that it should work the same way, that Marshal alone would be enough to solve it. Reality isn’t that smooth.
mruby has no native Marshal, and the mechanism might not be directly usable anyway, since CRuby and mruby differ in how memory is structured at the C level.
Most languages have a similar standard library, such as gob in Golang or pickle in Python
JSON
Since Ruby’s native Marshal is off the table, we just need something similar to replace it, and JSON is probably the most intuitive choice.
JSON handling is mature in most languages, and mruby has a gem like mruby-json available too. In theory it’s as simple as calling #to_json in Ruby and JSON.parse in mruby to get the value back.
There’s precedent for applying JSON to RPC as well, JSON-RPC being one example. Support across languages is strong, so going cross-language later wouldn’t be a problem either.
JSON has its limits, though. In recent years people have started looking for other formats to make token usage more efficient for AI, precisely because JSON’s readability advantage is paid for in space. Compared to similar tools, it usually takes up more room (wrapping strings in ", and so on).
That structure also makes it slower to process. You have to locate one " and then the next to tell whether something is a string. A binary representation usually only needs [type][len][data], two markers alongside the data, so there’s no extra flag to track for an opened " and no need to backtrack to confirm.
For an early design, or when generality matters, JSON is a fine choice. But Kobako sits at a slightly lower level, and there are other options.
MessagePack
MessagePack describes itself outright as a format “like JSON, but smaller and faster.” It uses the [type][len][data] structure I mentioned above, and just from dropping all those { and " symbols, we save a fair amount of time on transport, encoding, and decoding.
That said, once JSON is out, MessagePack is just about the only option left for Kobako. The faster Protobuf gets its speed from an agreed-upon Schema, which means it doesn’t even need [type]: once you know what kind of data it is, it’s always read a specific way, and that makes it faster.
Both Ruby and mruby are interpreted, so our programs can change data structures freely at runtime. Designing around Protobuf would mean asking users to compile the Ruby gem and the matching mruby themselves every time, just to establish how the two sides talk. For the strengths of languages like Ruby or JavaScript, that turns straight into a drawback.
So between “speed” and “flexibility,” MessagePack covers both, and it comes out a bit ahead of JSON. There’s really nothing to complain about.
There were a few extra reasons Kobako landed on MessagePack, mainly that Fluent Bit and mruby-engine, two Ruby projects led by large companies, both chose MessagePack as their transport format. At the very least it’s an approach that has been proven to work.
Next up is the problem that was proven alongside MessagePack: how Ruby exchanges data with the mruby isolated inside a WebAssembly VM.
Enjoyed this article? Buy me a milk tea 🧋