Skip to main content

Redis Cache Driver

Redis is a high-performance, in-memory data store. It is ideal for distributed, production-grade caching.

When to Use

  • Production environments
  • Multi-server or distributed systems
  • Need for persistence, high availability, or large cache sizes

Best For

  • Caching API/database responses
  • Session storage
  • Shared cache between multiple app instances

Limitations

  • Requires Redis server setup
  • Slightly more operational overhead than memory/file

Alternatives

Configuration

Configure the Redis driver in your cache configurations:

src/config/cache.ts
import { env } from "@mongez/dotenv";
import { CacheConfigurations, RedisCacheDriver } from "@warlock.js/cache";

const cacheConfigurations: CacheConfigurations = {
drivers: {
redis: RedisCacheDriver,
},
default: env("CACHE_DRIVER", "redis"),
options: {
redis: {
host: env("REDIS_HOST", "localhost"),
port: env("REDIS_PORT", 6379),
username: env("REDIS_USERNAME"),
password: env("REDIS_PASSWORD"),
globalPrefix: "prod-app",
ttl: 60 * 60 * 24, // 24 hours
},
},
};

export default cacheConfigurations;
tip

Make sure Redis is installed on your local machine and on the server otherwise it will throw an error.

Options

OptionTypeDefaultDescription
hoststring"localhost"Redis server host
portnumber6379Redis server port
usernamestringundefinedRedis username for authentication
passwordstringundefinedRedis password for authentication
urlstringundefinedRedis connection URL (overrides host/port)
globalPrefixstring | FunctionundefinedGlobal prefix for all cache keys
ttlnumberInfinityDefault TTL in seconds
clientOptionsobject{}Additional Redis client options

Connection Options

{
host: "localhost", // Redis host
port: 6379, // Redis port
username: "default", // Redis username
password: "your-password", // Redis password
url: "redis://user:pass@host:port", // Alternative: use URL
}

Global Prefix

{
globalPrefix: "myapp", // Static prefix
// OR
globalPrefix: () => `app-${Date.now()}`, // Dynamic prefix
}

TTL Configuration

{
ttl: 3600, // 1 hour default TTL
// OR
ttl: 60 * 60 * 24, // 24 hours
// OR
ttl: Infinity, // Never expire (default)
}

Advanced Client Options

{
clientOptions: {
retry_strategy: (options) => {
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Retry time exhausted');
}
if (options.attempt > 10) {
return undefined;
}
return Math.min(options.attempt * 100, 3000);
},
max_attempts: 3,
connect_timeout: 10000,
}
}

Example Usage

Storing and Retrieving Data

import { cache, CACHE_FOR } from "@warlock.js/cache";

await cache.set("user.2", { name: "Bob" }, CACHE_FOR.ONE_HOUR);
const user = await cache.get("user.2");

Atomic Operations

The Redis driver provides native atomic operations using Redis commands. These are race-condition free even in distributed systems.

Atomic Increment and Decrement

import { cache } from "@warlock.js/cache";

// Atomic increment using Redis INCRBY command
const views = await cache.increment("page.views", 1);

// Atomic decrement using Redis DECRBY command
const remaining = await cache.decrement("api.quota", 10);

Under the hood:

  • increment() uses Redis INCRBY command
  • decrement() uses Redis DECRBY command
  • Both are atomic at Redis level - guaranteed race-free

Set If Not Exists (Distributed Locking)

The setNX() method uses Redis SET NX command for atomic "set if not exists" operations. This is perfect for distributed locking:

import { cache } from "@warlock.js/cache";

// Try to acquire a lock
const acquired = await cache.setNX("lock:critical-operation", "locked", 30);

if (acquired) {
try {
// Lock acquired, perform operation
await performCriticalOperation();
} finally {
// Always release lock
await cache.remove("lock:critical-operation");
}
} else {
// Lock already exists
throw new Error("Operation already in progress");
}

Use cases:

  • Distributed locking across multiple servers
  • Idempotency keys (ensure operation runs only once)
  • Leader election
  • Preventing duplicate processing

Why Atomic Operations Matter

In distributed systems, multiple servers might try to modify the same cache key simultaneously:

// ❌ WITHOUT ATOMIC OPERATIONS (Race condition)
const views = await cache.get("page.views") || 0;
await cache.set("page.views", views + 1);
// Two servers both read 100, both write 101 → Wrong!

// ✅ WITH ATOMIC OPERATIONS (Race-free)
await cache.increment("page.views", 1);
// Redis INCRBY is atomic → Always correct!

See Atomic Operations for comprehensive documentation and examples.

Accessing the Redis Client

For advanced Redis operations, you can access the underlying Redis client:

import { cache } from "@warlock.js/cache";

const redisClient = cache.client;

// Use native Redis commands if needed
await redisClient.zAdd("leaderboard", {
score: 100,
value: "player1"
});

Troubleshooting

  • Connection errors? Check your Redis server is running and credentials are correct.
  • Data not shared between servers? Make sure all instances use the same Redis server.
  • setNX() throws error? setNX() is only available on Redis driver - make sure you're using Redis, not memory/file drivers.
  • Race conditions? Use atomic operations (increment(), decrement(), setNX()) instead of manual get/set for counters and locks.
note

Please note that the Redis Cache Drier implements all methods in Cache Driver Interface so you can use it directly as a cache driver.