You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*`client`: a redis client instance, mandatory. Should be an `ioredis` client or compatible.
75
+
*`invalidation`: enable invalidation, see [invalidation](#invalidation). Default is disabled.
76
+
*`invalidation.referencesTTL`: references TTLin seconds, it means how long the references are alive; it should be set at the maximum of all the caches ttl.
77
+
*`log`: logger instance `pino` compatible, default is disabled.
Define a newfunction to cache of the given `name`.
59
88
89
+
The `define` method adds a `cache[name]`function that will call the `original` function if the result is not present
90
+
in the cache. The cache key for `arg` is computed using [`safe-stable-stringify`](https://www.npmjs.com/package/safe-stable-stringify) and it is passed as the `cacheKey` argument to the original function.
91
+
60
92
Options:
61
93
62
-
*`tll`: the maximum time a cache entry can live, default as defined in the cache.
63
-
*`cacheSize`: the maximum amount of entries to fit in the cache for each defined method, default as defined in the cache.
94
+
*`ttl`:themaximumtimeacacheentrycanlive, defaultasdefinedinthecache; defaultiszero, socacheisdisabled, thefunction will be only the deduped.
64
95
* `serialize`: a function to convert the given argument into a serializable object (orstring).
96
+
* `onDedupe`: a function that is called every time there is defined is deduped.
65
97
* `onHit`: a function that is called every time there is a hit in the cache.
98
+
* `onMiss`: a function that is called every time the result is not in the cache.
99
+
* `storage`: the storage to use, same as above. It's possible to specify different storages for each defined function for fine-tuning.
100
+
* `references`: sync or async function to generate references, it receives `(args, key, result)` from the defined function call and must return an array of strings or falsy; see [invalidation](#invalidation) to know how to use them.
101
+
Example
66
102
67
-
The `define` method adds a `cache[name]` function that will call the `original` function if the result is not present
68
-
in the cache. The cache key for `arg` is computed using [`safe-stable-stringify`](https://www.npmjs.com/package/safe-stable-stringify)
69
-
and it is passed as the `cacheKey` argument to the original function.
103
+
```js
104
+
const cache = createCache({ ttl:60 })
105
+
106
+
cache.define('fetchUser', {
107
+
references: (args, key, result) => result ? [`user~${result.id}`] :null
108
+
},
109
+
(id) =>database.find({ table:'users', where: { id }}))
110
+
111
+
awaitcache.fetchUser(1)
112
+
```
70
113
71
114
### `cache.clear([name], [arg])`
72
115
73
116
Clear the cache. If `name` is specified, all the cache entries from the function defined with that name are cleared.
74
117
If `arg` is specified, only the elements cached with the given `name` and `arg` are cleared.
75
118
119
+
## Invalidation
120
+
121
+
Along with `time to live` invalidation of the cache entries, we can use invalidation by keys.
122
+
The concept behind invalidation by keys is that entries have an auxiliary key set that explicitly links requests along with their own result. These auxiliary keys are called here `references`.
123
+
A scenario. Let's say we have an entry _user_`{id: 1, name: "Alice"}`, it may change often or rarely, the `ttl` system is not accurate:
124
+
125
+
* it can be updated before `ttl` expiration, in this case the old value is shown until expiration by `ttl`.
126
+
* it's not been updated during `ttl` expiration, so in this case, we don't need to reload the value, because it's not changed
127
+
128
+
To solve this common problem, we can use `references`.
129
+
We can say that the result of defined function `getUser(id: 1)` has reference `user~1`, and the result of defined function `findUsers`, containing `{id: 1, name: "Alice"},{id: 2, name: "Bob"}` has references `[user~1,user~2]`.
130
+
So we can find the results in the cache by their `references`, independently of the request that generated them, and we can invalidate by `references`.
131
+
132
+
So, when a writing event involving `user {id: 1}` happens (usually an update), we can remove all the entries in the cache that have references to `user~1`, so the result of `getUser(id: 1)` and `findUsers`, and they will be reloaded at the next request with the new data - but not the result of `getUser(id: 2)`.
133
+
134
+
Explicit invalidation is `disabled` by default, you have to enable it in `storage` settings.
135
+
136
+
See [mercurius-cache-example](https://github.com/mercurius/mercurius-cache-example) for a complete example.
137
+
138
+
### Redis
139
+
140
+
Using a `redis` storage is the best choice for a shared and/or large cache.
141
+
All the `references` entries in redis have `referencesTTL`, so they are all cleaned at some time.
142
+
`referencesTTL` value should be set at the maximum of all the `ttl`s, to let them be available for every cache entry, but at the same time, they expire, avoiding data leaking.
143
+
Anyway, we should keep `references` up-to-date to be more efficient on writes and invalidation, using the `garbage collector` function, that prunes the expired references: while expired references do not compromise the cache integrity, they slow down the I/O operations.
144
+
Storage `memory` doesn't have `gc`.
145
+
146
+
### Redis garbage collector
147
+
148
+
As said, While the garbage collector is optional, is highly recommended to keep references up to date and improve performances on setting cache entries and invalidation of them.
149
+
150
+
### `storage.gc([mode], [options])`
151
+
152
+
*`mode`: `lazy` (default) or `strict`.
153
+
In `lazy` mode, only a chunk of the `references` are randomly checked, and probably freed; running `lazy` jobs tend to eventually clear all the expired `references`.
154
+
In `strict` mode, all the `references` are checked and freed, and after that, `references` and entries are perfectly clean.
155
+
`lazy` mode is the light heuristic way to ensure cached entries and `references` are cleared without stressing too much `redis`, `strict` mode at the opposite stress more `redis` to get a perfect result.
156
+
The best strategy is to combine them both, running often `lazy` jobs along with some `strict` ones, depending on the size of the cache.
157
+
158
+
Options:
159
+
160
+
*`chunk`: the chunk size of references analyzed per loops, default `64`
161
+
*`lazy~chunk`: the chunk size of references analyzed per loops in `lazy` mode, default `64`; if both `chunk` and `lazy.chunk` is set, the maximum one is taken
162
+
*`lazy~cursor`: the cursor offset, default zero; cursor should be set at `report.cursor` to continue scanning from the previous operation
0 commit comments