Skip to content

Commit c6238a2

Browse files
committed
CSC POC ontop of Parser
1 parent c39f377 commit c6238a2

17 files changed

+1051
-47
lines changed

packages/client/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import RedisSentinel from './lib/sentinel';
2020
export { RedisSentinelOptions, RedisSentinelType } from './lib/sentinel/types';
2121
export const createSentinel = RedisSentinel.create;
2222

23+
import { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache';
24+
export { BasicClientSideCache, BasicPooledClientSideCache };
25+
2326
// export { GeoReplyWith } from './lib/commands/generic-transformers';
2427

2528
// export { SetOptions } from './lib/commands/SET';
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Client Side Caching Support
2+
3+
Client Side Caching enables Redis Servers and Clients to work together to enable a client to cache results from command sent to a server and be informed by the server when the cached result is no longer valid.
4+
5+
## Usage
6+
7+
node-redis supports two ways of instantiating client side caching support
8+
9+
Note: Client Side Caching is only supported with RESP3.
10+
11+
### Anonymous Cache
12+
13+
```javascript
14+
const client = createClient({RESP: 3, clientSideCache: {ttl: 0, maxEntries: 0, lru: false}})
15+
```
16+
17+
In this instance, the cache is opaque to the user, and they have no control over it.
18+
19+
### Controllable Cache
20+
21+
```javascript
22+
const ttl = 0, maxEntries = 0, lru = false;
23+
const cache = new BasicClientSideCache(ttl, maxEntries, lru);
24+
const client = createClient({RESP: 3, clientSideCache: cache});
25+
```
26+
27+
In this instance, the user has full control over the cache, as they have access to the cache object.
28+
29+
They can manually invalidate keys
30+
31+
```javascript
32+
cache.invalidate(key);
33+
```
34+
35+
they can clear the entire cache
36+
g
37+
```javascript
38+
cache.clear();
39+
```
40+
41+
as well as get cache metrics
42+
43+
```typescript
44+
const hits: number = cache.cacheHits();
45+
const misses: number = cache.cacheMisses();
46+
```
47+
48+
## Pooled Caching
49+
50+
Similar to individual clients, node-redis also supports caching for its pooled client object, with the cache being able to be instantiated in an anonymous manner or a controllable manner.
51+
52+
### Anonymous Cache
53+
54+
```javascript
55+
const client = createClientPool({RESP: 3}, {clientSideCache: {ttl: 0, maxEntries: 0, lru: false}, minimum: 8});
56+
```
57+
58+
### Controllable Cache
59+
60+
```javascript
61+
const ttl = 0, maxEntries = 0, lru = false;
62+
const cache = new BasicPooledClientSideCache(ttl, maxEntries, lru);
63+
const client = createClientPool({RESP: 3}, {clientSideCache: cache, minimum: 8});
64+
```
+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import assert from "assert";
2+
import testUtils, { GLOBAL } from "../test-utils"
3+
import { BasicClientSideCache, BasicPooledClientSideCache } from "./cache"
4+
import { REDIS_FLUSH_MODES } from "../commands/FLUSHALL";
5+
6+
describe("Client Side Cache", () => {
7+
describe('Basic Cache', () => {
8+
const csc = new BasicClientSideCache();
9+
10+
testUtils.testWithClient('Basic Cache Miss', async client => {
11+
csc.clear();
12+
13+
await client.set("x", 1);
14+
await client.get("x");
15+
16+
assert.equal(1, csc.cacheMisses(), "Cache Misses");
17+
assert.equal(0, csc.cacheHits(), "Cache Hits");
18+
}, {
19+
...GLOBAL.SERVERS.OPEN,
20+
clientOptions: {
21+
RESP: 3,
22+
clientSideCache: csc
23+
}
24+
});
25+
26+
testUtils.testWithClient('Basic Cache Hit', async client => {
27+
csc.clear();
28+
29+
await client.set("x", 1);
30+
await client.get("x");
31+
await client.get("x");
32+
33+
assert.equal(1, csc.cacheMisses(), "Cache Misses");
34+
assert.equal(1, csc.cacheHits(), "Cache Hits");
35+
}, {
36+
...GLOBAL.SERVERS.OPEN,
37+
clientOptions: {
38+
RESP: 3,
39+
clientSideCache: csc
40+
}
41+
});
42+
43+
testUtils.testWithClient('Basic Cache Clear', async client => {
44+
csc.clear();
45+
46+
await client.set("x", 1);
47+
await client.get("x");
48+
csc.clear();
49+
await client.get("x");
50+
51+
assert.equal(1, csc.cacheMisses(), "Cache Misses");
52+
assert.equal(0, csc.cacheHits(), "Cache Hits");
53+
}, {
54+
...GLOBAL.SERVERS.OPEN,
55+
clientOptions: {
56+
RESP: 3,
57+
clientSideCache: csc
58+
}
59+
});
60+
61+
testUtils.testWithClient('Null Invalidate acts as clear', async client => {
62+
csc.clear();
63+
64+
await client.set("x", 1);
65+
await client.get("x");
66+
csc.invalidate(null);
67+
await client.get("x");
68+
69+
assert.equal(2, csc.cacheMisses(), "Cache Misses");
70+
assert.equal(0, csc.cacheHits(), "Cache Hits");
71+
}, {
72+
...GLOBAL.SERVERS.OPEN,
73+
clientOptions: {
74+
RESP: 3,
75+
clientSideCache: csc
76+
}
77+
});
78+
79+
testUtils.testWithClient('flushdb causes an invalidate null', async client => {
80+
csc.clear();
81+
82+
await client.set("x", 1);
83+
await client.get("x");
84+
await client.flushDb(REDIS_FLUSH_MODES.SYNC);
85+
await client.get("x");
86+
87+
assert.equal(2, csc.cacheMisses(), "Cache Misses");
88+
assert.equal(0, csc.cacheHits(), "Cache Hits");
89+
}, {
90+
...GLOBAL.SERVERS.OPEN,
91+
clientOptions: {
92+
RESP: 3,
93+
clientSideCache: csc
94+
}
95+
});
96+
97+
testUtils.testWithClient('Basic Cache Invalidate', async client => {
98+
csc.clear();
99+
100+
await client.set("x", 1);
101+
await client.get("x");
102+
await client.set("x", 2);
103+
await client.get("x");
104+
105+
assert.equal(2, csc.cacheMisses(), "Cache Misses");
106+
assert.equal(0, csc.cacheHits(), "Cache Hits");
107+
}, {
108+
...GLOBAL.SERVERS.OPEN,
109+
clientOptions: {
110+
RESP: 3,
111+
clientSideCache: csc
112+
}
113+
});
114+
});
115+
116+
describe("Pooled Cache", () => {
117+
const csc = new BasicPooledClientSideCache();
118+
119+
testUtils.testWithClientPool('Basic Cache Miss and Clear', async client => {
120+
csc.clear();
121+
122+
await client.set("x", 1);
123+
await client.get("x");
124+
125+
assert.equal(1, csc.cacheMisses(), "Cache Misses");
126+
assert.equal(0, csc.cacheHits(), "Cache Hits");
127+
}, {
128+
...GLOBAL.SERVERS.OPEN,
129+
poolOptions: {
130+
minimum: 5,
131+
maximum: 5,
132+
acquireTimeout: 0,
133+
cleanupDelay: 1,
134+
clientSideCache: csc
135+
}
136+
})
137+
138+
testUtils.testWithClientPool('Basic Cache Hit', async client => {
139+
csc.clear();
140+
141+
await client.set("x", 1);
142+
await client.get("x");
143+
await client.get("x");
144+
await client.get("x");
145+
146+
assert.equal(1, csc.cacheMisses(), "Cache Misses");
147+
assert.equal(2, csc.cacheHits(), "Cache Hits");
148+
}, {
149+
...GLOBAL.SERVERS.OPEN,
150+
poolOptions: {
151+
minimum: 5,
152+
maximum: 5,
153+
acquireTimeout: 0,
154+
cleanupDelay: 1,
155+
clientSideCache: csc
156+
}
157+
})
158+
159+
testUtils.testWithClientPool('Basic Cache Invalidate', async client => {
160+
csc.clear();
161+
162+
await client.set("x", 1);
163+
await client.get("x");
164+
csc.invalidate("x");
165+
await client.get("x");
166+
csc.invalidate("x");
167+
await client.get("x");
168+
csc.invalidate("x");
169+
await client.get("x");
170+
171+
assert.equal(4, csc.cacheMisses(), "Cache Misses");
172+
assert.equal(0, csc.cacheHits(), "Cache Hits");
173+
}, {
174+
...GLOBAL.SERVERS.OPEN,
175+
poolOptions: {
176+
minimum: 5,
177+
maximum: 5,
178+
acquireTimeout: 0,
179+
cleanupDelay: 1,
180+
clientSideCache: csc
181+
}
182+
})
183+
});
184+
});

0 commit comments

Comments
 (0)