WarmBlanket is a Ruby gem for warming up web services on boot. Its main target are JRuby-powered web services or even TruffleRuby based services, although it is not specific to those runtimes in any way.

How the magic happens

Why do we need to warm up web services?

When the Java Virtual Machine (JVM) starts, it starts by interpreting Java bytecode. As it starts to detect code that runs often, it just-in-time compiles that code into native machine code, improving performance.

This is a known challenge for most JVMs, and the same applies to JRuby applications, which also run on the JVM.

A widely-documented solution to this problem is to perform a warm-up step when starting a service:

What does WarmBlanket do?

WarmBlanket warms services by performing repeated web requests for a configurable number of seconds. After that time, it closes shop and you’ll never hear about it until the next service restart or deploy.

How does WarmBlanket work?

WarmBlanket spawns a configurable number of background threads that run inside the service process, and then uses an http client to perform local requests to the web server, simulating load.

As it simulates requests, the JVM is warmed up and thus when real requests come in, no performance degradation is observed.

Limitations/caveats

We strongly recommend that any services using WarmBlanket, if deployed on Heroku, use Preboot. Preboot allows a service instance to be warmed up for 3 minutes before Heroku starts sending live traffic its way, which is preferable to doing it live.

On kubernetes, you can make use of readiness probes to delay service startup while warm-blanket is working.

Important for TruffleRuby as well!

TruffleRuby can be run either on a standalone mode, or with a JVM. In either configuration, like JRuby, it needs to warm up until it gets to peak performance, so WarmBlanket is great there too!

How can I make use of it?

To make use of WarmBlanket, you’ll need to follow the next sections, which will guide you through installing, configuring and enabling the gem.

1. Installation

To install using Bundler, add the following to your Gemfile:

gem 'warm-blanket', '~> 1.0'

WarmBlanket uses semantic versioning.

2. Configuration settings

This gem can be configured via the following environment variables:

  • PORT: Local webserver port (automatically set on Heroku)

  • WARMBLANKET_ENABLED: Enable warm blanket (defaults to false; true or 1 enables)

  • WARMBLANKET_WARMUP_THREADS: Number of warm up threads to use (defaults to 2)

  • WARMBLANKET_WARMUP_TIME_SECONDS: Time, in seconds, during which to warm up the service (defaults to 150)

Configuring endpoints to be called

Configure endpoints to be called as follows (on a config/warm_blanket.rb:

Example GET requests
require 'warm-blanket'

WarmBlanket.configure do |config|
  common_headers = {
    'X-Api-Key': ENV.fetch('API_KEYS').split(',').first,
  }

  config.endpoints = [
    {get: '/foo', headers: common_headers},
    {get: '/', headers: common_headers},
  ]
end

Other HTTP verbs are supported (and you can pass in a body key if needed), but be careful about side effects from such verbs. And if there’s no side effect from a POST or PUT, do consider if it shouldn’t be a GET instead ;)

Example POST request with body
# Notice that you need to both:
# * set the Content-Type manually (if needed)
# * JSON-encode the body  (if needed)

WarmBlanket.configure do |config|
  common_headers = {
    'X-Api-Key': ENV.fetch('API_KEY').split(',').first,
    'Content-Type': 'application/json',
  }

  post_body = MultiJson.dump(
    account_id: 'dummy_account',
    user_id: 'dummy_user_id',
  )

  config.endpoints = [
    {post: '/some_endoint', headers: common_headers, body: post_body},
  ]
end

3. Trigger warmup

Add the following to the end of your config.ru file:

Development

After checking out the repo, run bundle install to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributors

Open-sourced with ❤️ by Talkdesk!

Maintained by Ivo Anjo and the Talkdesk Engineering team.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Talkdesk/warm-blanket.