Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] client side caching #2908

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ on:
- v4.0
- v5
paths-ignore:
- '**/*.md'
- "**/*.md"
pull_request:
branches:
- master
- v4.0
- v5
paths-ignore:
- '**/*.md'
- "**/*.md"
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [ '18', '20', '22' ]
redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M04-pre' ]
node-version: ["18", "20", "22"]
redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0-M05-pre"]
steps:
- uses: actions/checkout@v4
with:
Expand Down
2 changes: 2 additions & 0 deletions docs/v4-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ In v5, any unwritten commands (in the same pipeline) will be discarded.

- `FT.SUGDEL`: [^boolean-to-number]
- `FT.CURSOR READ`: `cursor` type changed from `number` to `string` (in and out) to avoid issues when the number is bigger than `Number.MAX_SAFE_INTEGER`. See [here](https://github.com/redis/node-redis/issues/2561).
- `FT.PROFILE`: `profile` type is now `ReplyUnion`, which preserves the server's original response format. This change helps prevent issues with future updates to the debug response structure.
- `FT.SUGGET`: Since Redis 8, the server returns `[]` instead of `null` when there are no suggestions

### Time Series

Expand Down
165 changes: 155 additions & 10 deletions docs/v5.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,171 @@
# RESP3 Support

TODO
Redis Serialization Protocol version 3 (RESP3) is the newer protocol used for communication between Redis servers and clients. It offers more data types and richer semantics compared to RESP2.

## Enabling RESP3

To use RESP3, simply set the `RESP` option to `3` when creating a client:

```javascript
const client = createClient({
RESP: 3
});
```

```javascript
// by default
await client.hGetAll('key'); // Record<string, string>
## Type Mapping

await client.withTypeMapping({
[TYPES.MAP]: Map
}).hGetAll('key'); // Map<string, string>
Some [RESP types](./RESP.md) can be mapped to more than one JavaScript type. For example, "Blob String" can be mapped to `string` or `Buffer`. You can override the default type mapping using the `withTypeMapping` function:

```javascript
await client.get('key'); // `string | null`

await client.withTypeMapping({
[TYPES.MAP]: Map,
const proxyClient = client.withTypeMapping({
[TYPES.BLOB_STRING]: Buffer
}).hGetAll('key'); // Map<string, Buffer>
});

await proxyClient.get('key'); // `Buffer | null`
```

## Unstable RESP3 Support

Some Redis modules (particularly the Search module) have responses that might change in future RESP3 implementations. These commands are marked with `unstableResp3: true` in the codebase.

To use these commands with RESP3, you must explicitly enable unstable RESP3 support:

```javascript
const client = createClient({
RESP: 3,
unstableResp3: true
});
```

If you attempt to use these commands with RESP3 without enabling the `unstableResp3` flag, the client will throw an error with a message like:

```
Some RESP3 results for Redis Query Engine responses may change. Refer to the readme for guidance
```

### Commands Using Unstable RESP3

The following Redis commands and modules use the `unstableResp3` flag:
- Many Search module commands (FT.*)
- Stream commands like XREAD, XREADGROUP
- Other modules with complex response structures

If you're working with these commands and want to use RESP3, make sure to enable unstable RESP3 support in your client configuration.

# Client-Side Caching

Redis 6.0 introduced client-side caching, which allows clients to locally cache command results and receive invalidation notifications from the server. This significantly reduces network roundtrips and latency for frequently accessed data.

### How It Works

1. When a cacheable command is executed, the client checks if the result is already in the cache
2. If found and valid, it returns the cached result (no Redis server roundtrip)
3. If not found, it executes the command and caches the result
4. When Redis modifies keys, it sends invalidation messages to clients
5. The client removes the invalidated entries from its cache

This mechanism ensures data consistency while providing significant performance benefits for read-heavy workloads.

## Requirements

Client-side caching in node-redis:
- Requires RESP3 protocol (`RESP: 3` in client configuration)
- Uses Redis server's invalidation mechanism to keep the cache in sync
- Is completely disabled when using RESP2

## Limitations of Client-Side Caching

Currently, node-redis implements client-side caching only in "default" tracking mode. The implementation does not yet support the following Redis client-side caching modes:

- **Opt-In Mode**: Where clients explicitly indicate which specific keys they want to cache using the `CLIENT CACHING YES` command before each cacheable command.

- **Opt-Out Mode**: Where all keys are cached by default, and clients specify exceptions for keys they don't want to cache with `CLIENT UNTRACKING`.

- **Broadcasting Mode**: Where clients subscribe to invalidation messages for specific key prefixes without the server tracking individual client-key relationships.

These advanced caching modes offer more fine-grained control over caching behavior and may be supported in future node-redis releases. While node-redis doesn't implement these modes natively yet, the underlying Redis commands (`CLIENT TRACKING`, `CLIENT CACHING`, `CLIENT UNTRACKING`) are available if you need to implement these advanced tracking modes yourself.


## Basic Configuration

To enable client-side caching with default settings:

```javascript
const client = createClient({
RESP: 3,
clientSideCache: {
// Cache configuration options
maxEntries: 1000, // Maximum number of entries in the cache (0 = unlimited)
ttl: 60000, // Time-to-live in milliseconds (0 = never expire)
evictPolicy: "LRU" // Eviction policy (LRU or FIFO)
}
});
```

### Cache Control

You can also create and control the cache instance directly:

```javascript
// Import the cache provider
const { BasicClientSideCache } = require('redis');

// Create a configurable cache instance
const cache = new BasicClientSideCache({
maxEntries: 5000,
ttl: 30000
});

// Create client with this cache
const client = createClient({
RESP: 3,
clientSideCache: cache
});

// Later you can:
// Get cache statistics
const hits = cache.cacheHits();
const misses = cache.cacheMisses();

// Manually invalidate specific keys
cache.invalidate('my-key');

// Clear the entire cache
cache.clear();

// Listen for cache events
cache.on('invalidate', (key) => {
console.log(`Cache key invalidated: ${key}`);
});
```

### Working with Connection Pools

Client-side caching also works with connection pools:

```javascript
const pool = createClientPool({
RESP: 3
}, {
clientSideCache: {
maxEntries: 10000,
ttl: 60000
},
minimum: 5
});
```

For pools, you can use specialized cache providers like `BasicPooledClientSideCache` or `PooledNoRedirectClientSideCache` that handle connection events appropriately.

### Performance Considerations

- Configure appropriate `maxEntries` and `ttl` values based on your application needs
- Monitor cache hit/miss rates to optimize settings
- Consider memory usage on the client side when using large caches
- Client-side caching works best for frequently accessed, relatively static data


# Sentinel Support

Expand Down
Loading
Loading