cacheable 0.2.9 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +136 -141
- package/dist/index.cjs +448 -0
- package/dist/index.d.cts +104 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.js +421 -0
- package/package.json +37 -26
- package/.npmignore +0 -3
- package/.travis.yml +0 -4
- package/Makefile +0 -7
- package/index.js +0 -1
- package/lib/cacheable.js +0 -162
- package/lib/registry.js +0 -170
- package/test/cacheable.test.js +0 -195
- package/test/mocha.opts +0 -2
- package/test/support/user.js +0 -26
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
MIT License & © Jared Wray
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
|
5
|
+
deal in the Software without restriction, including without limitation the
|
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,189 +1,184 @@
|
|
|
1
|
+
[<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
|
|
2
|
+
|
|
1
3
|
# Cacheable
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
> Simple Caching Engine using Keyv
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
[](https://codecov.io/gh/jaredwray/cacheable)
|
|
8
|
+
[](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
|
|
9
|
+
[](https://www.npmjs.com/package/cacheable)
|
|
10
|
+
[](https://www.npmjs.com/package/cacheable)
|
|
7
11
|
|
|
12
|
+
`cacheable` is a high performance layer 1 / layer 2 caching engine that is focused on distributed caching with enterprise features such as `CacheSync`. It is built on top of the robust storage engine [Keyv](https://keyv.org) and provides a simple API to cache and retrieve data.
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
* Simple to use with robust API
|
|
15
|
+
* Not bloated with additional modules
|
|
16
|
+
* Extendable to your own caching engine
|
|
17
|
+
* Scalable and trusted storage engine by Keyv
|
|
18
|
+
* Resilient to failures with try/catch and offline
|
|
19
|
+
* Hooks and Events to extend functionality
|
|
20
|
+
* Comprehensive testing and code coverage
|
|
21
|
+
* Distributed Caching Sync via Pub/Sub (coming soon)
|
|
22
|
+
* Maintained and supported regularly
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
var Redis = require('redis');
|
|
13
|
-
var Cacheable = require('cacheable');
|
|
14
|
-
|
|
15
|
-
var client = Redis.createClient();
|
|
16
|
-
var cached = Cacheable({
|
|
17
|
-
client: client,
|
|
18
|
-
ttl: 60, // set a default max age of one minute for `cached.set`
|
|
19
|
-
prefix: 'myapp:' // the prefix for every cache key
|
|
20
|
-
});
|
|
24
|
+
## Getting Started
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
cached.set(key, value, 300, callback)
|
|
24
|
-
cached.get(key, callback)
|
|
25
|
-
cached.del(['abc', 'aba'], callback)
|
|
26
|
-
```
|
|
26
|
+
`cacheable` is primarily used as an extension to you caching engine with a robust storage backend [Keyv](https://keyv.org), Memonization, Hooks, Events, and Statistics.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```javascript
|
|
31
|
-
// Get remote content that expires in 3600 seconds
|
|
32
|
-
var getUrlContent = cached.wrap(function(url, callback) {
|
|
33
|
-
request(url, function() {
|
|
34
|
-
// ...
|
|
35
|
-
})
|
|
36
|
-
}, 'url-{0}', 3600)
|
|
28
|
+
```bash
|
|
29
|
+
npm install cacheable
|
|
37
30
|
```
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
## Basic Usage
|
|
40
33
|
|
|
41
34
|
```javascript
|
|
42
|
-
|
|
43
|
-
this.attributes = data
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
User.prototype.toJSON = function() {
|
|
47
|
-
return this.attributes
|
|
48
|
-
}
|
|
35
|
+
import { Cacheable } from 'cacheable';
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// ...
|
|
54
|
-
var user = new User(data)
|
|
55
|
-
|
|
56
|
-
callback(err, user);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
User.prototype.getPostIds = function(start, limit, callback) {
|
|
60
|
-
callback(null, [1,2,3...])
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// register the constructor first
|
|
64
|
-
cached.register(User)
|
|
65
|
-
|
|
66
|
-
// enable cache for `User.get` method
|
|
67
|
-
// So when you call `User.get(some_id)`, it will fetch data
|
|
68
|
-
// from cache first, when cache not found, then the original function will be called.
|
|
69
|
-
User.enableCache('get', '{_model_}:{0}') // '{0}' means the `arguments[0]`
|
|
70
|
-
|
|
71
|
-
// You can also enable cache for an instance method
|
|
72
|
-
User.enableCache('.getPostIds', '{_model_}:posts-{0}-{1}')
|
|
37
|
+
const cacheable = new Cacheable();
|
|
38
|
+
await cacheable.set('key', 'value', 1000);
|
|
39
|
+
const value = await cacheable.get('key');
|
|
73
40
|
```
|
|
74
41
|
|
|
75
|
-
|
|
42
|
+
This is a basic example where you are only using the in-memory storage engine. To enable layer 1 and layer 2 caching you can use the `secondary` property in the options:
|
|
76
43
|
|
|
77
|
-
|
|
44
|
+
```javascript
|
|
45
|
+
import { Cacheable } from 'cacheable';
|
|
46
|
+
import KeyvRedis from '@keyv/redis';
|
|
78
47
|
|
|
79
|
-
|
|
80
|
-
|
|
48
|
+
const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
|
|
49
|
+
const cache = new Cacheable({secondary});
|
|
50
|
+
```
|
|
81
51
|
|
|
82
|
-
|
|
83
|
-
then cached will use this name.
|
|
52
|
+
In this example, the primary store we will use `lru-cache` and the secondary store is Redis. You can also set multiple stores in the options:
|
|
84
53
|
|
|
85
54
|
```javascript
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
55
|
+
import { Cacheable } from 'cacheable';
|
|
56
|
+
import { Keyv } from 'keyv';
|
|
57
|
+
import KeyvRedis from '@keyv/redis';
|
|
58
|
+
import { LRUCache } from 'lru-cache'
|
|
59
|
+
|
|
60
|
+
const primary = new Keyv({store: new LRUCache()});
|
|
61
|
+
const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
|
|
62
|
+
const cache = new Cacheable({primary, secondary});
|
|
89
63
|
```
|
|
90
64
|
|
|
91
|
-
|
|
92
|
-
The `.toJSON` will be extended by `cache.register`, the output object will always have a property `__cachedname`,
|
|
93
|
-
as is the constructor's modelName. You can add a `.toObject = .toJSON`, and use `.toObject` whenever you need a clean object.
|
|
65
|
+
This is a more advanced example and not needed for most use cases.
|
|
94
66
|
|
|
67
|
+
## Hooks and Events
|
|
95
68
|
|
|
96
|
-
|
|
69
|
+
The following hooks are available for you to extend the functionality of `cacheable` via `CacheableHooks` enum:
|
|
97
70
|
|
|
98
|
-
|
|
71
|
+
* `BEFORE_SET`: This is called before the `set()` method is called.
|
|
72
|
+
* `AFTER_SET`: This is called after the `set()` method is called.
|
|
73
|
+
* `BEFORE_SET_MANY`: This is called before the `setMany()` method is called.
|
|
74
|
+
* `AFTER_SET_MANY`: This is called after the `setMany()` method is called.
|
|
75
|
+
* `BEFORE_GET`: This is called before the `get()` method is called.
|
|
76
|
+
* `AFTER_GET`: This is called after the `get()` method is called.
|
|
77
|
+
* `BEFORE_GET_MANY`: This is called before the `getMany()` method is called.
|
|
78
|
+
* `AFTER_GET_MANY`: This is called after the `getMany()` method is called.
|
|
99
79
|
|
|
100
|
-
|
|
101
|
-
var item = new User(json)
|
|
102
|
-
item._unpickle()
|
|
103
|
-
return item
|
|
104
|
-
```
|
|
105
|
-
Note that it would be impossible to unpickle a cache if the constructor's name was changed.
|
|
106
|
-
|
|
107
|
-
When registered, the class will have a property `._cacheKeys` and an instance would have
|
|
108
|
-
a method `._clearCache()`.
|
|
80
|
+
An example of how to use these hooks:
|
|
109
81
|
|
|
110
82
|
```javascript
|
|
111
|
-
|
|
112
|
-
var self = this
|
|
113
|
-
// destroy the item from database
|
|
114
|
-
db.destroy(..., function() {
|
|
115
|
-
// then clear the cache
|
|
116
|
-
self._clearCache(callback)
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
```
|
|
83
|
+
import { Cacheable, CacheableHooks } from 'cacheable';
|
|
120
84
|
|
|
121
|
-
|
|
85
|
+
const cacheable = new Cacheable();
|
|
86
|
+
cacheable.onHook(CacheableHooks.BEFORE_SET, (data) => {
|
|
87
|
+
console.log(`before set: ${data.key} ${data.value}`);
|
|
88
|
+
});
|
|
89
|
+
```
|
|
122
90
|
|
|
123
|
-
|
|
124
|
-
The `ttl` is in seconds. If no `ttl` set, the cache will never automatically expire, even it an `options.ttl`
|
|
125
|
-
is passed when you do `new Cached()`.
|
|
91
|
+
## Storage Tiering and Caching
|
|
126
92
|
|
|
127
|
-
|
|
93
|
+
`cacheable` is built as a layer 1 and layer 2 caching engine by default. The purpose is to have your layer 1 be fast and your layer 2 be more persistent. The primary store is the layer 1 cache and the secondary store is the layer 2 cache. By adding the secondary store you are enabling layer 2 caching. By default the operations are blocking but fault tolerant:
|
|
128
94
|
|
|
129
|
-
|
|
95
|
+
* `Setting Data`: Sets the value in the primary store and then the secondary store.
|
|
96
|
+
* `Getting Data`: Gets the value from the primary if the value does not exist it will get it from the secondary store and set it in the primary store.
|
|
97
|
+
* `Deleting Data`: Deletes the value from the primary store and secondary store at the same time waiting for both to respond.
|
|
98
|
+
* `Clearing Data`: Clears the primary store and secondary store at the same time waiting for both to respond.
|
|
130
99
|
|
|
131
|
-
|
|
100
|
+
## Non-Blocking Operations
|
|
132
101
|
|
|
133
|
-
`
|
|
134
|
-
So you'd better alway name your functions, like this:
|
|
102
|
+
If you want your layer 2 (secondary) store to be non-blocking you can set the `nonBlocking` property to `true` in the options. This will make the secondary store non-blocking and will not wait for the secondary store to respond on `setting data`, `deleting data`, or `clearing data`. This is useful if you want to have a faster response time and not wait for the secondary store to respond.
|
|
135
103
|
|
|
136
104
|
```javascript
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
105
|
+
import { Cacheable } from 'cacheable';
|
|
106
|
+
import {KeyvRedis} from '@keyv/redis';
|
|
107
|
+
|
|
108
|
+
const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
|
|
109
|
+
const cache = new Cacheable({secondary, nonBlocking: true});
|
|
140
110
|
```
|
|
141
111
|
|
|
142
|
-
|
|
143
|
-
For a class method, this usually means the name of a constructor.
|
|
112
|
+
## CacheSync - Distributed Updates
|
|
144
113
|
|
|
145
|
-
|
|
146
|
-
`%j{0}` mean the first argument value will be converted to json.
|
|
114
|
+
`cacheable` has a feature called `CacheSync` that is coming soon. This feature will allow you to have distributed caching with Pub/Sub. This will allow you to have multiple instances of `cacheable` running and when a value is set, deleted, or cleared it will update all instances of `cacheable` with the same value. Current plan is to support the following:
|
|
147
115
|
|
|
116
|
+
* [Google Pub/Sub](https://cloud.google.com/pubsub)
|
|
117
|
+
* [AWS SQS](https://aws.amazon.com/sqs)
|
|
118
|
+
* [RabbitMQ](https://www.rabbitmq.com)
|
|
119
|
+
* [Nats](https://nats.io)
|
|
120
|
+
* [Azure Service Bus](https://azure.microsoft.com/en-us/services/service-bus)
|
|
121
|
+
* [Redis Pub/Sub](https://redis.io/topics/pubsub)
|
|
148
122
|
|
|
149
|
-
|
|
123
|
+
This feature should be live by end of year.
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
## Cacheable Options
|
|
152
126
|
|
|
153
|
-
|
|
154
|
-
it's a class method.
|
|
127
|
+
The following options are available for you to configure `cacheable`:
|
|
155
128
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
*
|
|
163
|
-
* `limit`: limit per page
|
|
164
|
-
* `offset`: offset
|
|
165
|
-
*
|
|
166
|
-
*/
|
|
167
|
-
User.getAllIds = function(options, callback) {
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
User.prototype.getPostIds = function(start, limit, callback) {
|
|
171
|
-
// get user's posts
|
|
172
|
-
callback(null, [1,2,3...])
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
User.enableCache('getAllIds', 'ids-{0.limit}-{0.offset}')
|
|
176
|
-
|
|
177
|
-
User.enableCache('.getPostIds', 'posts-{0}-{1}')
|
|
178
|
-
|
|
179
|
-
// You can omit the key, cacheable will automatically use the method name
|
|
180
|
-
User.enableCache('.getPostIds', 3600)
|
|
181
|
-
// KEY: '{_model_}:{id}:getTagsIds', expires in: 3600 seconds
|
|
182
|
-
```
|
|
129
|
+
* `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
|
|
130
|
+
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
|
|
131
|
+
* `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
|
|
132
|
+
* `enableStats`: If you want to enable statistics for this instance. Default is `false`.
|
|
133
|
+
|
|
134
|
+
## Cacheable Statistics (Instance Only)
|
|
183
135
|
|
|
184
|
-
|
|
136
|
+
If you want to enable statistics for your instance you can set the `enableStats` property to `true` in the options. This will enable statistics for your instance and you can get the statistics by calling the `stats` property. Here are the following property statistics:
|
|
185
137
|
|
|
138
|
+
* `hits`: The number of hits in the cache.
|
|
139
|
+
* `misses`: The number of misses in the cache.
|
|
140
|
+
* `sets`: The number of sets in the cache.
|
|
141
|
+
* `deletes`: The number of deletes in the cache.
|
|
142
|
+
* `clears`: The number of clears in the cache.
|
|
143
|
+
* `errors`: The number of errors in the cache.
|
|
144
|
+
* `count`: The number of keys in the cache.
|
|
145
|
+
* `vsize`: The estimated byte size of the values in the cache.
|
|
146
|
+
* `ksize`: The estimated byte size of the keys in the cache.
|
|
186
147
|
|
|
187
|
-
|
|
148
|
+
You can clear the stats by calling the `clearStats()` method.
|
|
149
|
+
|
|
150
|
+
_This does not enable statistics for your layer 2 cache as that is a distributed cache_.
|
|
151
|
+
|
|
152
|
+
## API
|
|
188
153
|
|
|
189
|
-
the
|
|
154
|
+
* `set(key, value, ttl? | [{string, string, ttl?}])`: Sets a value in the cache.
|
|
155
|
+
* `setMany([{key, value, ttl?}])`: Sets multiple values in the cache.
|
|
156
|
+
* `get(key | [keys])`: Gets a value from the cache.
|
|
157
|
+
* `getMany([keys])`: Gets multiple values from the cache.
|
|
158
|
+
* `has(key | [key])`: Checks if a value exists in the cache.
|
|
159
|
+
* `hasMany([keys])`: Checks if multiple values exist in the cache.
|
|
160
|
+
* `take(key)`: Takes a value from the cache and deletes it. (coming soon)
|
|
161
|
+
* `takeMany([keys])`: Takes multiple values from the cache and deletes them. (coming soon)
|
|
162
|
+
* `delete(key | [key])`: Deletes a value from the cache.
|
|
163
|
+
* `deleteMany([keys])`: Deletes multiple values from the cache.
|
|
164
|
+
* `clear()`: Clears the cache stores. Be careful with this as it will clear both layer 1 and layer 2.
|
|
165
|
+
* `clearPrimary()`: Clears the primary store. (coming soon)
|
|
166
|
+
* `clearSecondary()`: Clears the secondary store. (coming soon)
|
|
167
|
+
* `wrap(function, options)`: Wraps a function in a cache. (coming soon)
|
|
168
|
+
* `disconnect()`: Disconnects from the cache stores.
|
|
169
|
+
* `onHook(hook, callback)`: Sets a hook.
|
|
170
|
+
* `removeHook(hook)`: Removes a hook.
|
|
171
|
+
* `on(event, callback)`: Listens for an event.
|
|
172
|
+
* `removeListener(event, callback)`: Removes a listener.
|
|
173
|
+
* `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
|
|
174
|
+
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
|
|
175
|
+
* `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
|
|
176
|
+
* `stats`: The statistics for this instance which includes `hits`, `misses`, `sets`, `deletes`, `clears`, `errors`, `count`, `vsize`, `ksize`.
|
|
177
|
+
* `clearStats()`: Clears the statistics for this instance.
|
|
178
|
+
|
|
179
|
+
## How to Contribute
|
|
180
|
+
|
|
181
|
+
You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
|
|
182
|
+
|
|
183
|
+
## License and Copyright
|
|
184
|
+
[MIT © Jared Wray](./LICENSE)
|