= What?
zozo is a tool that makes it easy to reduce the memory footprint of your
applications by having them not load rubygems/bundler at runtime:
$ unicorn -c unicorn.conf -D
$ ps ux
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME
COMMAND
jeremy 18226 0.0 0.5 17196 4496 ?? S 1:34PM 0:00.01
ruby: unicorn master -c unicorn.conf -D (ruby)
jeremy 8473 31.3 3.3 27180 30172 ?? S 1:34PM 0:00.62
ruby: unicorn worker[0] -c unicorn.conf -D (ruby)
$ zozo -R config.ru unicorn
$ ruby -I lib bin/unicorn -c unicorn.conf -D
$ ps ux
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME
COMMAND
jeremy 17561 0.0 0.4 5548 3908 ?? S 1:35PM 0:00.01
ruby: unicorn master -c unicorn.conf -D (ruby)
jeremy 22626 4.2 2.0 15016 17904 ?? S 1:35PM 0:00.25
ruby: unicorn worker[0] -c unicorn.conf -D (ruby)
As you can see, the memory footprint is reduced dramatically:
master process:
VSZ: 5548/17196 => 68% reduction
RSS: 3908/4496 => 13% reduction
worker process:
VSZ: 15016/27180 => 45% reduction
RSS: 17904/30172 => 41% reduction
That’s a major difference, as a 41% reduction in memory footprint means
you can host 68% more workers in the same amount of memory. It also
makes your applications faster. They will start faster because zozo
loads all necessary library files into a single directory tree. They
will run faster as there will be fewer ruby objects to check every time
the garbage collector is run.
= Why?
Rubygems is a fine package distribution system, but it is not very
efficient from a runtime memory standpoint. If your application uses
rubygems in production, every time it starts, rubygems needs to figure
out which packages to load. zozo makes it so that this is calculation
is only done once, and the result is cached into a local directory.
= How?
zozo works by starting ruby and checking the current load path. It then
requires all of the command line arguments given, and checks the load
path again. Any new entries in the load path are checked and their
contents are loaded into a local directory (lib by default). By
default, zozo uses symlinks, but it can use hard links (-H) or make
copies (-c) via a command line option.
In addition, new entries in the load path that end in /bin are loaded
into a separate local directory (bin by default). This allows you to
run them with loading rubygems via:
ruby -I lib bin/$program
zozo adds replacement rubygems.rb, ubygems.rb, and bundler.rb files to
the lib directory it creates, so it works transparently if your program
requires rubygems and/or bundler. If you run your program without
adding the lib directory zozo creates to the load path, rubygems/bundler
will be used as it was. If you run your program with the lib directory
zozo creates in the load path, then rubygems/bundler will not be loaded,
and it won’t need to be because all other libraries your program uses
will already be in the load path.
= Where?
= Who?
Jeremy E. / [email protected]
= When?
Now:
sudo gem install zozo
= Does not work with Rails!
The replacement rubygems.rb and bundler.rb files only do the bare
minimum. The rubygems.rb file adds Kernel#gem, and the bundler.rb file
adds Bundler.setup, both of which are defined to do nothing and return
nil. No other features are mocked out. This means that frameworks that
rely on introspecting the running Gem/Bundler configuration (notably
Rails) will not work.
This is probably fixable, and I’ll accept patches that allow zozo to
work with Rails, but I don’t plan on working on the issue myself. As
Rails uses a substantial amount of memory by itself, it benefits less
from zozo than more memory friendly frameworks such as Sinatra.