Comparing SQLite based Rails cache stores

Written in

by

Introduction

If you are running a production Rails application using SQLite then you are probably using SQLite as a cache store as well. Compared to something like Redis or Memcached, SQLite offers a potentially much larger caching space and a significantly higher read performance, which is what matters most when we are dealing with caching. One other important advantage of SQLite based caching is that it also benefits from the features provided by the filesystem or volume manager underneath it, like for example transparent compression.

In this article we will be looking at two SQLite based cache stores for Rails, Litecache, which is a part of the Litestack library of SQLite based data components and SolidCache, which is the new ActiveRecord Based cache store for Rails and one that can be configured to use SQLite.

Litecache

Litecache builds on top of SQLite via Litestack provided abstractions, as a result it enjoys a fine tuned, raw, SQLite connection, automatic thread/fiber environment detection and fork resilience.

To configure Litecache for you Rails application you need to add the following line to your Gemfile

gem "litestack"

Then run

bundle update

Then in your environments/production file

config.cache_store = :litecache

Optionally, you could also run the installer to configure other litestack components

rails generate litestack:install

With that in place, you can now use the Rails cache and it will be using Litecache which utilizes SQLite.

Solid Cache

Solid Cache provides a Rails cache store that builds on top of ActiveRecord, hence it comes with all the bells and whistles that ActiveRecord has, like having adapters for many database engines, including PostgreSQL, SQLite, MySQL and MongoDB. It also provides the infrastructure needed for using sharded databases and utilizing primary/secondary database. These features can be used directly in Solid Cache and are potentially useful for really large setups.

To use Solid Cache you need to add the gem to your Gemfile

gem "solid_cache"

And then run

bundle update

You are expected to already have SQLite configured for your database (either via the sqlite3 adapter or Litestack’s Litedb adapter)

production:
  adapter: sqlite3

Then generate the migrations (for the correct environment)

RAILS_ENV=production rails solid_cache:install:migrations 

Then run the migrations

RAILS_ENV=production rails db:migrate

And in config/environments/production

config.cache_store = :solid_cache_store

At this point, you can use solid cache right away. Please refer to the Solid Cache gem page on github for more information, and more configuration options regarding Solid Cache.

Not all SQLite caches are created equal

Even though both Litestack and Solid Cache can be using SQLite, they have vastly different feature sets and performance characteristics. This is largely due to how each of them abstracts the underlying physical cache store.

Litecache, a bare metal approach

Litecache uses raw connections in the most efficient way possible, it avoids creating explicit transactions when it can and it tries to rely solely on prepared statements to improve latency. As a result, Litecache performance is really fast, actually even faster in reading cache entries than Redis and any other network based solution.

Solid Cache, standing on the soldier of giants

As mentioned earlier, Solid Cache builds heavily on the foundation built by ActiveRecord. Making it a very versatile solution with advanced for mapping cache operations to a potentially complex setup of sharded, distributed and replicated databases. Though this comes at the price of having all these layers of abstraction between the cache operation and the physical store.

It can be argued that, in the case of an application utilizing SQLite in production, that application will generally be confined to a single box. The chances that such an application needs a very elaborate setup for caching, that utilizes multiple SQLite databases, such that it will sacrifice latency for scale are very slim of not nonexistent.

Performance benchmarks

We will look at how both solutions, Litecache and Solid Cache with SQLite perform various read/write operations with different payload sizes.

All benchmarks were ran using the default configuration for each library, though the SQLite connection for Solid Cache was tuned to increase read/write performance. The following pragmas were configures:

PRAGMA journal_mode = WAL;
PRAGMA synchronous = 1;
PRGAMA mmap_size = 134,217,728;

The tests were ran on an AMD Ryzen 8740HS machine.

Write throughput (writes/second)

Payload Size10100100010000
Litecache14,76315,19416,16512,169
Solid Cache3,0733,0082,5262,826

As can be seen, Litecache writes are much faster than Solid Cache writes, up to 5X faster at times. Of course in a more concurrent test both solutions will suffer a little, but Litecache’s faster operations should lead to even less write contention vs Solid Cache which will be holding write locks for much longer.

Read throughput (reads/second)

Payload Size10100100010000
Litecache56,30253,07751,37825,658
Solid Cache18,16517,86118,17212,674

During reads, Litecache retains a solid advantage over Solid Cache, though it is not as profound as writes it is still quite remarkable as it reaches over 3X at times. The difference dips to 2X as the payload size increases, which is understandable, as the ActiveRecord overheads start to become less pronounced compared to the overhead of moving the large data records around.

Conclusion

Both Litecache and Solid Cache provide a SQLite based caching option that you can use for your Rails applications that utilize SQLite in production. Though due to their different design choices, Litecache delivers much higher cache performance over Solid Cache. And while Solid Cache comes with a lot of goodies from its ActiveRecord foundation, those are hardly useful in the context of a Rails app utilizing SQLite in production.

One response to “Comparing SQLite based Rails cache stores”

  1. Turbo charge your Ruby Litestack apps using the Fiber Scheduler & YJIT – Oldmoe's blog Avatar

    […] our latest article we saw how Litestack provides a high performance caching solution for Ruby/Rails SQLite based […]

Leave a comment