Skip to main content

Stores

A store is the persistence layer truschery/idem uses to save idempotency records and manage locks. It determines how responses are cached and how concurrent requests are handled.

Cache vs Database​

Cache StoreDatabase Store
ConcurrencyAtomic locks (Redis)SELECT FOR UPDATE
RequirementsRedis recommendedMigrations required
PerformanceFasterSlower
PersistenceUntil TTL expiresUntil TTL expires
Best forMost applicationsExisting DB infrastructure

Cache Store​

Uses Laravel's cache with atomic locks to handle concurrency. On the first request, a lock is acquired for the duration of execution. Concurrent requests either wait or throw depending on your lock_wait.strategy.

'stores' => [
'cache' => [
'driver' => 'default', // uses Laravel default cache driver
'ttl' => 86400, // seconds — 24 hours
],
],
tip

Redis is strongly recommended The Cache store requires a cache driver that supports atomic locks. Redis satisfies this out of the box. The default file/array drivers do not support locks and will not work correctly under concurrent load.

When to choose Cache store:

  • You already use Redis in your stack
  • You want the fastest possible response replay
  • You don't want to manage an extra database table

Database Store​

Uses a dedicated database table with pessimistic locking (SELECT FOR UPDATE inside a transaction) to guarantee that only one request processes a given key at a time.

Publish and run migrations before using this store — see Installation.

'stores' => [
'database' => [
'driver' => 'default', // uses Laravel default DB driver
'ttl' => 86400, // seconds — 24 hours
'table_name' => 'idempotency',
],
],

When to choose Database store:

  • You don't have Redis available
  • You prefer all persistent data in one place
  • You need idempotency records visible and queryable via SQL

Pruning Expired Records​

Over time the idempotency table accumulates expired records. Clean them up with the artisan command:

php artisan idempotency:prune

This removes all records where expires_at is in the past

tip

Add this command to your Laravel scheduled tasks to keep the table clean:

// routes/console.php
Schedule::command('idempotency:prune')->daily();

Switching Stores​

Globally — affects all operations (HTTP, Jobs, Facade) unless overridden:

'default' => 'database',

HTTP only — overrides the default store specifically for HTTP middleware:

'request' => [
'store' => 'cache',
],

This lets you mix stores — for example, use Cache for HTTP requests and Database for jobs.