The past two months have been busy. In New Zealand, we are in lockdown, so I have been adapting my work schedule and family life. Feel free to contact me if you would like to do some peer programming.
Falcon virtual is now running in production, hosting this site and several others for the past month. Real internet traffic has provided useful data, allowing me to improve HTTP/2 flow control and general throughput.
As falcon virtual acts as a load balancer, it also terminates SSL. It gets an A rating out of the box. With the next release of the OpenSSL gem, we should be able to support TLS 1.3 natively which will reduce latency and improve security further.
In order to support the same amount of traffic as Nginx, which has its own internal cache, I implemented async-http-cache. It can be used both as a server-side and client-side cache, and can be easily added to falcon.
CVE-2020-10933: Heap exposure vulnerability in the socket library
It was also during this time, I found some strange issues with data buffering, and after two days of debugging narrowed it down to a heap exposure vulnerability in the Ruby socket library. It is strongly recommended that you upgrade to a fixed version of Ruby.
After spending time working on
falcon virtual, I came to the conclusion that the way we specify "environments" lacks consistency. In particular,
RACK_ENV is often misused and misunderstood (even in my own code). The relationship between
RAILS_ENV (and others) is poorly specified. I decided to create variant, a gem which documents and supports the
VARIANT environment variable.
In terms of
falcon, deployment into production is simpler, because setting
VARIANT=production is framework-agnostic. Of course, it will depend on individual frameworks and tools taking advantage of it, and I hope with time they will.
After repeatedly implementing the same patterns with rake over and over again, and still running into limitations, I decided to create Bake. It improves in several key areas, including on-demand loading of tasks, argument handling, task documentation and gem integration.
Bake already integrates with several of my other gems, including console, variant and async-http to name a few.
Releasing gems can now be done using bake-bundler which removes the need for rake in the vast majority of the gems I maintain. In addition to the standard installation and release tasks, it can also automaticlly bump the version number which is convenient.
One of my goals is to bring attention to thread safety issues that exist in Ruby code. As we look forward to improved parallelism and concurrency with Ruby 3, understanding where we currently lack thread safety will help inform and justify the decisions we are making for Ruby 3.
Lazy Mutex Initialization
One problem that is both fairly common and easily identifiable by automated analysis, is lazy mutex initialization. Using this gem source code "analysis" tool we identified many issues:
- BallAerospace/COSMOS – Lazy mutex initialization.
- soveran/ohm – Lazy mutex initialization.
- ruby-amqp/amqp – Lazy mutex initialization.
- sinatra/mustermann – Lazy mutex initialization?
- fog/fog-aws – Lazy mutex initialization and data race.
- castwide/backport – Lazy mutex initialization.
- castwide/solargraph – Lazy mutex initialization.
- cucumber/cucumber-ruby – Lazy mutex initialization.
- lostisland/faraday – Data race.
- famoseagle/carrot – Lazy mutex initialization.
- marcmo/cxxproject – Lazy mutex initialization.
Thanks to Olle Jonsson for finding many of these issues.
In addition, I've made a proposal to support a standardised documentation tag for reentrant behaviour. While this doesn't impact actual code, it could assist with code analysis.
Falcon introduces isolated multi-process and multi-thread execution models. Global mutable state bypasses these boundaries, sometimes with unpredictable results, so I created the thread-local gem which standardises the construction of thread-local instances.
Thread-local instances are completely isolated and thus don't attract the same thread-safety issues that might occur with globally shared state. In addition, falcon running in multi-thread mode may restart threads which suffer critical failures. By using thread-local state, any state that caused the application to fail will be discarded too.
GitHub Changelog Generator
I have been experimenting with taking existing code bases, and retrofitting async libraries, in order to improve concurrency. One recent example is PR #784 to GitHub Changelog Generator. We used Async::HTTP::Faraday to improve the concurrency of Octokit.
There are more improvements to be made, but the initial results show that improving concurrency can significantly reduce run-time:
After some remote "back-to-back" debugging, the ARMv7 coroutine implementation is finally working and enabled by default. It turned out to be a code alignment issue. This now means Ruby running on the Raspberry Pi and other 32-bit ARM processors uses native coroutines which improves performance. Thanks to Paul Jordan for bringing this across the finishing line.
If you are a company and you depend on Ruby, I invite you to support my work. I also provide commercial support agreements and contracting for businesses that want to improve their infrastructure using Falcon and Async. Feel free to get in touch.