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, andCache::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,
databaseis 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, soCache::lock()raises an exception on it. Laravel's own scheduler notices and falls back, sowithoutOverlapping()keeps working, but code of yours that takes a lock directly does not. add()andincrement()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; onlyredis,memcachedandarrayare. The same is true offile,databaseanddynamodb, 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.