Skip to main content

Teaching vacancies - Replace Redis with Solid Cache for the Rails cache

Date: 2026-05-27

Status

Decided

Context and Problem Statement

The Rails cache (Rails.cache) in production uses :redis_cache_store, backed by an Azure Cache for Redis instance dedicated to caching (redis-cache in terraform/app/modules/paas/aks_redis.tf). In production it runs on the Premium tier.

Besides general caching, this cache also holds the request counters that Rack::Attack uses for throttling and blocking abusive clients, so it must be shared across all application instances.

The same retirement that led us to replace Sidekiq (ADR 0014) applies here. Microsoft has announced the retirement of Azure Cache for Redis in favour of Azure Managed Redis, and DfE has shared a direction with its service teams to move away from existing Azure Cache for Redis instances, and to avoid the cost of Azure Managed Redis where a service does not need Redis.

Since Rails 8, the framework default cache store is Solid Cache, which stores cache entries in a relational database rather than in memory. We already run PostgreSQL for the service, and it is already hosting our background jobs through Solid Queue.

Should we move our cache to Azure Managed Redis, or replace it with Solid Cache?

Decision Drivers

  • The retirement of Azure Cache for Redis, and DfE's direction to move away from it.
  • Reducing hosting costs: removing the cache instance, after the queue instance, means we no longer pay for any managed Redis service.
  • Simplifying our infrastructure: with Solid Queue and Solid Cache, PostgreSQL is the only data store the application needs, in every environment including local development.
  • Aligning with current Rails defaults.
  • Keeping a cache shared across application instances, so Rack::Attack throttling keeps working.

Considered Options

  • Keep :redis_cache_store, and move its Redis instance to Azure Managed Redis.
  • Replace the Redis cache with Solid Cache, backed by PostgreSQL.

Decision Outcome

We will replace the Redis cache with Solid Cache, backed by PostgreSQL, and remove Redis from the service entirely.

Removing Redis entirely also means moving everything else that talks to Redis directly. The main case is vacancy view tracking, which counts views per referrer in Redis (VacancyAnalyticsService, TrackVacancyViewJob, AggregateVacancyReferrerStatsJob). That approach relies on Redis-specific operations, such as scanning keys by pattern, so it will need to be reworked rather than simply pointed at Solid Cache.

Once nothing uses Redis, we remove the Redis gems, the REDIS_CACHE_URL and REDIS_QUEUE_URL configuration, the Redis instances in Terraform and the Redis service in the devcontainer.

Positive Consequences

  • No managed Redis service left to pay for, and no exposure to the Azure Cache for Redis retirement.
  • One data store to provision, back up, monitor and secure.
  • Simpler local development and test setup, with no Redis container.
  • A cache held on disk can be much larger than a memory-bound one for the same cost, and it is not lost when an instance restarts.
  • Follows the Rails default.

Negative Consequences

  • Reading from and writing to a database-backed cache is slower than an in-memory Redis cache. This is acceptable for our workload, but should be watched on hot paths.
  • Rack::Attack writes a counter on almost every request, which adds write load to PostgreSQL. We need to monitor this, and may need to host the cache in a separate database if it affects the application.
  • The database now carries application data, background jobs and the cache, making PostgreSQL an even more critical single dependency to size and monitor.
  • Vacancy view tracking has to be reworked before Redis can be removed.