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 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
- Cache manager that doesn't suck.
5
+ > Simple Caching Engine using Keyv
4
6
 
5
- Make the result of you async functions cacheable, automatically pickle and unpickle the data.
6
- Manage all cache keys in one place, use a simple `._clearCache()` to purge cache.
7
+ [![codecov](https://codecov.io/gh/jaredwray/cacheable/graph/badge.svg?token=lWZ9OBQ7GM)](https://codecov.io/gh/jaredwray/cacheable)
8
+ [![tests](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
9
+ [![npm](https://img.shields.io/npm/dm/cacheable.svg)](https://www.npmjs.com/package/cacheable)
10
+ [![npm](https://img.shields.io/npm/v/cacheable)](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
- ## Usage
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
- ```javascript
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
- cached.set(key, value, callback)
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
- Wraping an async function:
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
- Manage cache for your models:
32
+ ## Basic Usage
40
33
 
41
34
  ```javascript
42
- function User(data) {
43
- this.attributes = data
44
- }
45
-
46
- User.prototype.toJSON = function() {
47
- return this.attributes
48
- }
35
+ import { Cacheable } from 'cacheable';
49
36
 
50
- // get user by id
51
- User.get = function(user_id, callback) {
52
- // get the user from data base
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
- ## API
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
- ### cached.register(cls, name)
44
+ ```javascript
45
+ import { Cacheable } from 'cacheable';
46
+ import KeyvRedis from '@keyv/redis';
78
47
 
79
- You have to `register` all model constructors, so when cache is hit, the cached manager would know
80
- how to restore the data as a proper JavaScript Object.
48
+ const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
49
+ const cache = new Cacheable({secondary});
50
+ ```
81
51
 
82
- If your model constructor doesn't have a name, you can give a name as the second parameter,
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
- var Book = function() {
87
- }
88
- cached.register(Book, 'Book')
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
- Your class.prototype must have a `.toJSON` method, so the cache wrapper could know how to save it to cache.
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
- If an `._unpickle` method is also defined, it will be called each time the object is restored from cache.
69
+ The following hooks are available for you to extend the functionality of `cacheable` via `CacheableHooks` enum:
97
70
 
98
- That is:
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
- ```javascript
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
- User.prototype.destroy = function(callback) {
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
- ### cached.wrap(fn, [key], [ttl])
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
- Wrap an standard nodejs async function(which should have a `callback(err, result)` as the last parameter).
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
- The parameter `key` is a pattern for formatting real cache keys.
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
- The default `key` is:
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
- {_model_}:{_fn_}
100
+ ## Non-Blocking Operations
132
101
 
133
- `{_fn_}` is the name of the function `fn`. If not found, an error will throw.
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
- User.get = function get(id) {
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
- `{_model_}` equals to `{this.name}`, which is `this.modelName || this.name` in the scope when the function is called.
143
- For a class method, this usually means the name of a constructor.
112
+ ## CacheSync - Distributed Updates
144
113
 
145
- Numbers like `{0}` is indexes of arguments when the function is called.
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
- ### cls.enableCache(methodName, [key], [ttl])
123
+ This feature should be live by end of year.
150
124
 
151
- When a `cls` is registered, you can use `cls.enableCache` to enable cache for class/instance methods.
125
+ ## Cacheable Options
152
126
 
153
- If `methodName` starts with a dot `(.)`, it will be considered as an instance method, otherwise,
154
- it's a class method.
127
+ The following options are available for you to configure `cacheable`:
155
128
 
156
- ```javascript
157
- /**
158
- *
159
- * List all ids
160
- *
161
- * Options:
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
- It is strongly recommended to use this approach to add cache, instead of directly call `cached.wrap`.
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
- ## License
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 MIT licence.
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)