Brewless Brewless Docs

Running your application

Cache

Where an environment keeps its cache, what each store can and cannot do, and why Object Storage is the first choice.

On this page

Nothing stays running on a serverless container, and an environment runs more than one of them. Where the cache goes decides whether a value one container worked out is there for the next, and whether it survives a container that goes away. Brewless names a store for every environment, so the framework's own default — a database that your environment need not have — never decides it for you.

What your environment gets

At its first deploy an environment gets CACHE_STORE set to the first of these it has:

It has CACHE_STORE Shared between containers Survives a container
A cache (Managed Redis) redis Yes Yes
A database database Yes Yes
A bucket storage Yes Yes
None of these file No No

Most environments have a bucket and no cache, so for most of them the answer is storage: the cache goes in the Object Storage bucket the environment already has, under framework/cache/data. The variables get CACHE_STORAGE_DISK=s3 with it.

SESSION_DRIVER follows the same choice, except for storage and file: a session is read and written on every request, which Object Storage would make two calls of and a container's own disk would lose. Those get cookie, which the next container reads as well.

Note

These are the variables of a new environment. An environment that has variables already keeps every value it has, so a store you chose yourself is never replaced. Change it on the Variables tab or with brewless env:push.

Why Object Storage first

A bucket is the only shared, lasting place an environment has without paying for something that runs. Scaleway bills Object Storage for what you store and the requests you make; Managed Redis is billed by the size of its nodes for every hour it exists, awake or not. For a cache that is read a few hundred times an hour, the bucket costs cents and the cache costs tens of euros a month.

It also beats the alternatives an environment without Redis would otherwise fall to:

  • Against the container's own disk (file), which is where the framework would land without a database: what one container cached, another does not have, and a value cached forever is gone the moment that container goes away. A page that caches an expensive query would work it out again in every container, and Cache::forever() means "until this container dies". With a bucket, every container of the environment reads the same cache.
  • Against the database, which is shared and lasting too, but every cache hit is then a query on the database your application also serves pages from — and a serverless database has to wake up for it. If you have a database and a busy cache, database is still a reasonable choice; the point is that a bucket does not make you buy one.

What it costs you is latency: a cache hit is a request to Object Storage, so it is milliseconds rather than microseconds. That is a good trade for values that are expensive to work out and read now and then, and a poor one for a value read fifty times in a single request. Cache those in memory for the length of the request (Cache::driver('array')), or give the environment a Redis cache.

What the storage store cannot do

Laravel's storage store writes through a disk, which is an ordinary file store at the other end. Three things follow, and they are the reason it is not the answer for every application:

  • No locks. It is the only store Laravel ships that is not a LockProvider, so Cache::lock() raises an exception on it. Laravel's own scheduler notices and falls back, so withoutOverlapping() keeps working, but code of yours that takes a lock directly does not.
  • add() and increment() are not atomic. Both read and then write, so two containers doing it at the same moment can overwrite one another. A rate limiter or a counter on this store counts low under load.
  • No tags. Cache::tags() needs a taggable store; only redis, memcached and array are. The same is true of file, database and dynamodb, so this is nothing you lose by moving from them.

If your application needs any of these, give the environment a cache (see A cache) or point CACHE_STORE at its database.

The stores, and when to use which

Store Shared Lasting Locks Tags Use it when
storage Yes Yes No No The default without Redis: a shared cache for nothing but a bucket
redis Yes Yes Yes Yes A busy cache, locks, tags, sessions in a store; worth its hourly bill
database Yes Yes Yes No You have a database and the cache is quiet enough to share it
file No No Per container No Nothing else is there, or a container-local scratch
array No No Per request Yes Within one request, and in tests
null — — — — Switching the cache off, usually in tests
octane No No Per worker No Laravel Octane only; it lives in the worker's memory
memcached, dynamodb, apc — — — — Not offered by Brewless; set them yourself if you run them

failover is not a place to keep things but a list of stores tried in order, which is worth knowing if you want a Redis cache that falls back to the bucket when it is unreachable.

Checking what an environment uses

brewless command production "config:show cache.default"

The store has to exist in your application's config/cache.php. The framework's own stores are merged into yours, so storage is there even when your file does not list it; a store of your own making has to be in the file.