Cache with Rails

Mateo mojica
6 min readSep 13, 2024

--

Photo by Steve Johnson on Unsplash

Application performance is one of the most important things to consider when you are building an application because it will directly impact the experience your users will have. That is why a good amount of modern applications use a system component called cache, which will help with storing data so it will be accessed faster and with that improve the performance of the app. In this article, we are going to dive into what is cache, its advantages, and disadvantages, and how to implement it in a Rails application.

Let’s start with some definitions. Cache is a hardware or software component embedded in an application or device’s memory that automatically and temporarily stores data consumed by the user to reduce the data retrieval time and effort the next time the application or device is accessed. Cached data is saved on a storage medium exclusive to the cache user and separate from the main storage. Cache minimizes data access times, lowers latency, and enhances data fetching (internal or external).

But, how does it work? Well, the data in a cache is typically stored in faster access memory, like RAM, and may be controlled by the application itself. The fundamental objective of a cache is to improve data retrieval speed by eliminating the need to contact the slower storage layer behind it. When the client tries to retrieve data, it checks the cache first. If the data is in there, it is called a cache hit. Data not located in the cache is taken from the main memory and put into the cache. This is known as a cache miss. The caching algorithms, cache mechanisms, and system regulations determine how this is accomplished and what information is expelled from the cache to create space for new data, two of the most common ones include Least Recently Used (LRU), which discards the data that has remained unused for the longest duration, and Most Recently Used (MRU), which prioritizes the removal of the most recently accessed data. Usually, you have to set a time limit to the data that is stored, that limit depends on the application and the type of data it handles, for frequently changing data the time should be small, and for slower changing data the cache time can be longer.

The application has to handle the cache bust when it knows the data in the cache is no longer valid even though its time to live is not over yet. Generally, for an application cache, the information is stored using a hash data structure where you specify a unique key (easy to identify) according to your guidelines, and the value is the data that is related to the key.

Photo by Anna Dziubinska on Unsplash

For many applications, caching is the only way to make things quicker. When the application has to retrieve costly data like slow network calls or large data that is frequently accessed, the default behavior is to cache it, so the next time someone looks it up, it will be less expensive to get the data, thus improving performance. Some applications like browsers cache the web pages when you visit them the first time to make them load faster when you visit them again, but it also will give you access to it while offline too. All of this showcases the importance of having a cache in your applications, it will make it faster with a better experience for your users and will make it more efficient since the data is fetched once, especially for expensive operations like calculations or network calls.

But it is not all advantages when working with cache, ithas many advantages for performance but it can also have some disadvantages that can break the user experience instead of improving it. The most common problem is that the data lives for too long in the cache and it becomes outdated and the users don’t have a way to get the fresh data. Another problem can be that the information between requests is not consistent, this problem presents itself when you have distributed systems with different cache servers, so depending on which server serves your request you may get different information. These problems are the reason that you have to be very careful as a developer on how you manage the cache in your application because all those problems are caused by a bad managing structure if the cache. You have to be very careful of stale data, even though the cache service will manage this with the times you specify for the data, sometimes you know that the data has changed and you have to bust the cache (delete the data from the cache) so the next time someone requests it will fetch the fresh data for everyone. You also have to ensure that if you have several cache servers you have to keep them synced so all the users get the same data.

There are different types of cache:

  • L1: the smallest and fastest type of cache. It is embedded in the CPU so it can operate at the same speed and that is why is used for storing instructions. Its size varies from 2KB to 64KB.
  • L2: It is larger and slower than L1, it can be inside or outside (but not far with a dedicated bus) the CPU depending on the architecture, and it can be shared among the cores or dedicated to one core. Its size goes from 256 KB to 512 KB.
  • L3: it is known as Last Level Cache (LLC), it is larger than the previous two and it is a shared memory by all cores and it serves as a communication between cores. Its size ranges from 1 MB to 8 MB.
  • Application Cache: It is the cache system that an application uses to store frequently used data, this data can be stored in RAM memory (faster) or disk (slower) depending on the type and the mapping techniques that are used for caching.

Now, if you want to implement cache in your application there are many open-source options used by companies in their applications, here are the most relevant:

  • Redis: It is an open-source, fast, high-performance, and flexible distributed in-memory data structure store that works as a caching engine, in-memory persistent on-disk database, and message broker.
  • Elastic Search: is a distributed search and analytics engine, scalable data store, and vector database built on Apache Lucene. It’s optimized for speed and relevance on production-scale workloads. Use Elasticsearch to search, index, store, and analyze data of all shapes and sizes in near real-time.
  • Memcache: is an open-source, simple yet powerful, distributed memory object caching system. It is an in-memory key-value store for small chunks of data such as results of database calls, API calls, or page rendering, and supports strings as the only data type. Although it is a distributed caching system, the servers are disconnected from each other, which means there is no replication support like in Redis.
  • Apache ignite: is a free and open-source, horizontally scalable distributed in-memory key-value store, cache, and multi-model database system that provides powerful processing APIs for computing distributed data. It is also an in-memory data grid that can be used either in memory or with Ignite native persistence. It features multi-tier storage, complete SQL support, and ACID (Atomicity, Consistency, Isolation, Durability) transactions (supported only at key-value API level) across multiple cluster nodes, co-located processing, and machine learning. It supports automatic integration with any third-party databases, including any RDBMS (such as MySQL, PostgreSQL, Oracle Database, and so on) or NoSQL stores.

It is time to see how to implement cache into your Rails application. First, you must configure the cache server in the configuration files and install the respective gem to communicate with it.

# Example of an environment configuration file
# Path: config/environments/environment.rb
Rails.application.configure do
....
config.cache_store = :redis_cache_store, {
url: ENV['REDIS_URL'],
namespace: 'cache',
password: ENV.fetch('REDIS_PASSWORD', nil)
}
....
end

The operations that you can do to manipulate the cache data are: read/write which tries to find the data under the provided key, if it is a hit it returns the cached data, if it is a miss then it executes the provided block and stores the result in the cache under the key. the other operation is delete, which deletes the data under the provided key, effectively busting the cache.

# Read / Write
Rails.cache.fetch("/user/#{user_id}", expires_in: <Time to live, eg 1.day>) do
# <Data the you want to store>
User.find(user_id)
end

# Delete
Rails.cache.delete("/user/#{user_id}")

That is all I wanted to share about cache, thank you for reading all the way through and I hope you learned something new from this article. If you liked it give it a clap and check out my other articles.

--

--

Mateo mojica
Mateo mojica

Written by Mateo mojica

Electronic engineer and software developer

No responses yet