Engine Yard, who employ the majority of the JRuby development team, asked me if I would write a short article for their newsletter about how my company uses JRuby in production. Naturally, I was delighted to oblige.
Here’s my article on how JRuby and the JVM allowed us to dramatically speed up our application:
ONEIS is a specialist ‘software as a service’ platform for building highly customized information management systems. We have a wide variety of customers, ranging from environmental consultancies to high-end medical practices. While these clients’ needs are specific, they’re united by the production of large amounts of documentation and a need for fast, precise, accurate search to find exactly the right piece of information at the right time.
We’re very fortunate to have a product which scales horizontally. Since there’s no interaction between each customer, we can scale simply by adding more servers. For administrative convenience, we use Solaris Zones for lightweight virtualization, splitting a single physical server into several instances of the application. Each instance is fully multi-tenant, where any process can respond to any customer.
The application was started using an early version of Rails. Because of the non-relational data model, per-customer customizations, and multi-tenancy, we continually found ourselves working around some of the core assumptions of Rails.
In the end, we only really used Rails for a small amount of the application and found our requirements drifting further away from what was provided easily by the Ruby language and interpreter. Many of our core services were best implemented using Java libraries, and because C Ruby can only run a single thread at a time, we used many independent processes. By summer 2009, our application looked like this:
While it worked very well, and Solaris’ SMF alleviated any problems with running a complex tangle of independent services in production, it was not as efficient as we would have liked. And critically, because Apache simply round-robined requests between the application servers, one customer could hog all the resources of the server.
It had also become intolerably cumbersome to develop. Functionality was split across an inter-process Ruby and Java language boundary, and a large number of processes had to be running for anything to work.
We decided that it was worth spending some development time to re-architect how the service was delivered. Since we had lots of Ruby code and a reliance on Java, it was natural to look to JRuby. Using the knowledge gained by running and developing the application for a few years, we designed a lightweight application server and framework, replacing Rails and the C Ruby stack.
The key JRuby features which allowed us to radically improve our architecture were true multi-threading and seamless access to Java libraries. Here’s how ONEIS looks today:
The number of processes has been dramatically reduced — even though the feature set has increased.
We’ve replaced Apache with Jetty, using it as an embedded web server within the JVM process and directly serving the HTTP requests from the users. An application framework, written in Java, processes requests and where necessary, hands them off for processing to a pool of threads which run the application code with JRuby. There’s an important addition between the framework and the request processors to throttle the number of in-flight requests for each customer. This ensures a single customer can’t hog the server.
The inter-process communication to coordinate all the processes and caches has been removed, vastly increasing efficiency. This has been replaced by a number of mutex-protected data structures, but since these are all per-customer data structures and there are zero locks until the customer is chosen, this is highly scalable.
We’ve found that JRuby is very pleasant to work with, and the JRuby core team is very responsive to issues. The differences between C Ruby and JRuby are minimal, and we haven’t had any problems moving a large code base between platforms. It’s also made development so much easier, as there’s just a single process to start and there are minimal differences between development and production.
Changing the underlying web application framework for a mature application is not a project to be undertaken lightly. To reduce the changes to the application code, our framework is quite similar to Rails. By using many of the patterns and some of the Rails components, notably ActiveRecord, we minimised the overall impact while being able to develop a custom framework to fit our needs exactly.
The performance improvements have exceeded our expectations. For starters, running our Ruby code is about two times faster. Overall, the system is five times faster with the new streamlined architecture, and it handles high load effectively and fairly to our customers.
JRuby has given us the best of both worlds. Java gives us a high performance, but safe, static programming language. Ruby allows us to write good code, quickly. Together they give us the means to develop a high quality and efficient application with a very small team.
COMMENTSblog comments powered by Disqus