cacheable 0.3.0 → 1.1.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 CHANGED
@@ -1,4 +1,4 @@
1
- MIT License
1
+ MIT License & © Jared Wray
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
package/README.md CHANGED
@@ -1,23 +1,27 @@
1
- [<img align="center" src="https://jaredwray.com/images/cacheable_white.svg" alt="keyv">](https://github.com/jaredwray/cacheable)
1
+ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
2
2
 
3
- # cacheable
3
+ # Cacheable
4
4
 
5
5
  > Simple Caching Engine using Keyv
6
6
 
7
- [![codecov](https://codecov.io/gh/jaredwray/cacheable/branch/master/graph/badge.svg?token=LDLaqe4PsI)](https://codecov.io/gh/jaredwray/cacheable)
7
+ [![codecov](https://codecov.io/gh/jaredwray/cacheable/graph/badge.svg?token=lWZ9OBQ7GM)](https://codecov.io/gh/jaredwray/cacheable)
8
8
  [![tests](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
9
9
  [![npm](https://img.shields.io/npm/dm/cacheable.svg)](https://www.npmjs.com/package/cacheable)
10
10
  [![npm](https://img.shields.io/npm/v/cacheable)](https://www.npmjs.com/package/cacheable)
11
11
 
12
- `cacheable` is a simple caching engine that uses [Keyv](https://keyv.org) as the storage engine. It is designed to be simple to use and extend. Here are some of the features:
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.
13
+
13
14
  * Simple to use with robust API
14
15
  * Not bloated with additional modules
15
16
  * Extendable to your own caching engine
16
17
  * Scalable and trusted storage engine by Keyv
17
- * Statistics built in by default
18
+ * Memory Caching with LRU and Expiration `CacheableMemory`
19
+ * Resilient to failures with try/catch and offline
18
20
  * Hooks and Events to extend functionality
19
21
  * Comprehensive testing and code coverage
20
- * Maintained and supported
22
+ * Distributed Caching Sync via Pub/Sub (coming soon)
23
+ * ESM and CommonJS support with TypeScript
24
+ * Maintained and supported regularly
21
25
 
22
26
  ## Getting Started
23
27
 
@@ -33,186 +37,178 @@ npm install cacheable
33
37
  import { Cacheable } from 'cacheable';
34
38
 
35
39
  const cacheable = new Cacheable();
36
- cacheable.set('key', 'value', 1000);
37
- const value = cacheable.get('key');
40
+ await cacheable.set('key', 'value', 1000);
41
+ const value = await cacheable.get('key');
38
42
  ```
39
43
 
40
- ## Extending Your own Caching Engine
44
+ 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:
41
45
 
42
46
  ```javascript
43
47
  import { Cacheable } from 'cacheable';
48
+ import KeyvRedis from '@keyv/redis';
44
49
 
45
- export class MyCache extends Cacheable {
46
- constructor() {
47
- super();
48
- }
49
- }
50
+ const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
51
+ const cache = new Cacheable({secondary});
52
+ ```
53
+
54
+ 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:
55
+
56
+ ```javascript
57
+ import { Cacheable } from 'cacheable';
58
+ import { Keyv } from 'keyv';
59
+ import KeyvRedis from '@keyv/redis';
60
+ import { LRUCache } from 'lru-cache'
61
+
62
+ const primary = new Keyv({store: new LRUCache()});
63
+ const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
64
+ const cache = new Cacheable({primary, secondary});
50
65
  ```
51
66
 
52
- From here you now how the ability to use the `cacheable` API. You can also extend the API to add your own functionality.
67
+ This is a more advanced example and not needed for most use cases.
53
68
 
54
- ## Storage Adapters and Keyv
69
+ ## Hooks and Events
70
+
71
+ The following hooks are available for you to extend the functionality of `cacheable` via `CacheableHooks` enum:
72
+
73
+ * `BEFORE_SET`: This is called before the `set()` method is called.
74
+ * `AFTER_SET`: This is called after the `set()` method is called.
75
+ * `BEFORE_SET_MANY`: This is called before the `setMany()` method is called.
76
+ * `AFTER_SET_MANY`: This is called after the `setMany()` method is called.
77
+ * `BEFORE_GET`: This is called before the `get()` method is called.
78
+ * `AFTER_GET`: This is called after the `get()` method is called.
79
+ * `BEFORE_GET_MANY`: This is called before the `getMany()` method is called.
80
+ * `AFTER_GET_MANY`: This is called after the `getMany()` method is called.
55
81
 
56
- To set Keyv as the storage engine, you can do the following:
82
+ An example of how to use these hooks:
57
83
 
58
84
  ```javascript
59
- import { Cacheable } from 'cacheable';
60
- import Keyv from 'keyv';
85
+ import { Cacheable, CacheableHooks } from 'cacheable';
61
86
 
62
- export class MyCache extends Cacheable {
63
- constructor() {
64
- super(new Keyv('redis://user:pass@localhost:6379'));
65
- }
66
- }
87
+ const cacheable = new Cacheable();
88
+ cacheable.onHook(CacheableHooks.BEFORE_SET, (data) => {
89
+ console.log(`before set: ${data.key} ${data.value}`);
90
+ });
67
91
  ```
68
92
 
69
- or you can do it at the property level:
93
+ ## Storage Tiering and Caching
94
+
95
+ `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:
96
+
97
+ * `Setting Data`: Sets the value in the primary store and then the secondary store.
98
+ * `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.
99
+ * `Deleting Data`: Deletes the value from the primary store and secondary store at the same time waiting for both to respond.
100
+ * `Clearing Data`: Clears the primary store and secondary store at the same time waiting for both to respond.
101
+
102
+ ## Non-Blocking Operations
103
+
104
+ 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.
70
105
 
71
106
  ```javascript
72
107
  import { Cacheable } from 'cacheable';
73
- import Keyv from 'keyv';
108
+ import {KeyvRedis} from '@keyv/redis';
74
109
 
75
- export class MyCache extends Cacheable {
76
- constructor() {
77
- super();
78
-
79
- this.store = new Keyv('redis://user:pass@localhost:6379');
80
- }
81
- }
110
+ const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
111
+ const cache = new Cacheable({secondary, nonBlocking: true});
82
112
  ```
83
113
 
84
- ## Statistics
114
+ ## CacheSync - Distributed Updates
85
115
 
86
- To get statistics on your cache, you can do the following:
116
+ `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:
87
117
 
88
- ```javascript
89
- import { Cacheable } from 'cacheable';
118
+ * [Google Pub/Sub](https://cloud.google.com/pubsub)
119
+ * [AWS SQS](https://aws.amazon.com/sqs)
120
+ * [RabbitMQ](https://www.rabbitmq.com)
121
+ * [Nats](https://nats.io)
122
+ * [Azure Service Bus](https://azure.microsoft.com/en-us/services/service-bus)
123
+ * [Redis Pub/Sub](https://redis.io/topics/pubsub)
90
124
 
91
- export class MyCache extends Cacheable {
92
- constructor() {
93
- super();
94
- }
125
+ This feature should be live by end of year.
95
126
 
96
- async getStats() {
97
- return this.stats.getReport();
98
- }
99
- }
100
- ```
127
+ ## Cacheable Options
101
128
 
102
- This will generate the following json object:
103
-
104
- ```json
105
- {
106
- "cacheSize": 100,
107
- "currentSize": 80,
108
- "hits": 500,
109
- "misses": 200,
110
- "hitRate": 0.71,
111
- "evictions": 50,
112
- "averageLoadPenalty": 0.05,
113
- "loadSuccessCount": 700,
114
- "loadExceptionCount": 10,
115
- "totalLoadTime": 3500,
116
- "topHits": [
117
- {
118
- "key": "key1",
119
- "value": "value1",
120
- "lastAccessed": 1627593600000,
121
- "accessCount": 50
122
- },
123
- {
124
- "key": "key2",
125
- "value": "value2",
126
- "lastAccessed": 1627593600000,
127
- "accessCount": 45
128
- }
129
- // More items...
130
- ],
131
- "leastUsed": [
132
- {
133
- "key": "key3",
134
- "value": "value3",
135
- "lastAccessed": 1627593600000,
136
- "accessCount": 5
137
- },
138
- {
139
- "key": "key4",
140
- "value": "value4",
141
- "lastAccessed": 1627593600000,
142
- "accessCount": 4
143
- }
144
- // More items...
145
- ]
146
- }
147
- ```
129
+ The following options are available for you to configure `cacheable`:
148
130
 
149
- * `cacheSize`: The maximum number of items that can be stored in the cache.
150
- * `currentSize`: The current number of items in the cache.
151
- hits: The number of cache hits. A cache hit occurs when the requested data is found in the cache.
152
- * `misses`: The number of cache misses. A cache miss occurs when the requested data is not found in the cache and needs to be loaded.
153
- * `hitRate`: The ratio of cache hits to the total number of cache lookups. This is a measure of the cache's effectiveness.
154
- * `evictions`: The number of items that have been evicted from the cache, typically because the cache is full.
155
- * `averageLoadPenalty`: The average time spent loading new values into the cache, typically measured in milliseconds. This could be calculated as totalLoadTime / (hits + misses).
156
- * `loadSuccessCount`: The number of times cache loading has succeeded.
157
- * `loadExceptionCount`: The number of times cache loading has failed due to exceptions.
158
- * `totalLoadTime`: The total time spent loading new values into the cache, typically measured in milliseconds.
131
+ * `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
132
+ * `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
133
+ * `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
134
+ * `stats`: To enable statistics for this instance. Default is `false`.
159
135
 
160
- ## Hooks and Events
136
+ ## Cacheable Statistics (Instance Only)
161
137
 
162
- The following hooks are available for you to extend the functionality of `cacheable`:
138
+ If you want to enable statistics for your instance you can set the `.stats.enabled` 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:
163
139
 
164
- * `preSet`: This is called before the `set` method is called.
165
- * `postSet`: This is called after the `set` method is called.
166
- * `preSetMany`: This is called before the `setMany` method is called.
167
- * `postSetMany`: This is called after the `setMany` method is called.
168
- * `preGet`: This is called before the `get` method is called.
169
- * `postGet`: This is called after the `get` method is called.
170
- * `preGetMany`: This is called before the `getMany` method is called.
171
- * `postGetMany`: This is called after the `getMany` method is called.
140
+ * `hits`: The number of hits in the cache.
141
+ * `misses`: The number of misses in the cache.
142
+ * `sets`: The number of sets in the cache.
143
+ * `deletes`: The number of deletes in the cache.
144
+ * `clears`: The number of clears in the cache.
145
+ * `errors`: The number of errors in the cache.
146
+ * `count`: The number of keys in the cache.
147
+ * `vsize`: The estimated byte size of the values in the cache.
148
+ * `ksize`: The estimated byte size of the keys in the cache.
172
149
 
173
- An example of how to use these hooks:
150
+ You can clear / reset the stats by calling the `.stats.reset()` method.
174
151
 
175
- ```javascript
176
- import { Cacheable } from 'cacheable';
152
+ _This does not enable statistics for your layer 2 cache as that is a distributed cache_.
177
153
 
178
- const cacheable = new Cacheable();
179
- cacheable.hooks.setHook('preSet', (key, value) => {
180
- console.log(`preSet: ${key} ${value}`);
181
- });
154
+ ## API
155
+
156
+ * `set(key, value, ttl? | [{string, string, ttl?}])`: Sets a value in the cache.
157
+ * `setMany([{key, value, ttl?}])`: Sets multiple values in the cache.
158
+ * `get(key | [keys])`: Gets a value from the cache.
159
+ * `getMany([keys])`: Gets multiple values from the cache.
160
+ * `has(key | [key])`: Checks if a value exists in the cache.
161
+ * `hasMany([keys])`: Checks if multiple values exist in the cache.
162
+ * `take(key)`: Takes a value from the cache and deletes it.
163
+ * `takeMany([keys])`: Takes multiple values from the cache and deletes them.
164
+ * `delete(key | [key])`: Deletes a value from the cache.
165
+ * `deleteMany([keys])`: Deletes multiple values from the cache.
166
+ * `clear()`: Clears the cache stores. Be careful with this as it will clear both layer 1 and layer 2.
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
+
178
+ ## CacheableMemory - In-Memory Cache
179
+
180
+ `cacheable` comes with a built-in in-memory cache called `CacheableMemory`. This is a simple in-memory cache that is used as the primary store for `cacheable`. You can use this as a standalone cache or as a primary store for `cacheable`. Here is an example of how to use `CacheableMemory`:
181
+
182
+ ```javascript
183
+ import { CacheableMemory } from 'cacheable';
184
+ const options = {
185
+ ttl: 60 * 60 * 1000, // 1 hour
186
+ useClones: true, // use clones for the values (default is true)
187
+ lruSize: 1000, // the size of the LRU cache (default is 0 which is unlimited)
188
+ }
189
+ const cache = new CacheableMemory(options);
190
+ await cache.set('key', 'value');
191
+ const value = await cache.get('key'); // value
182
192
  ```
183
193
 
184
- The following events are available for you to extend the functionality of `cacheable`:
194
+ You can use `CacheableMemory` as a standalone cache or as a primary store for `cacheable`. You can also set the `useClones` property to `false` if you want to use the same reference for the values. This is useful if you are using large objects and want to save memory. The `lruSize` property is the size of the LRU cache and is set to `0` by default which is unlimited. When setting the `lruSize` property it will limit the number of keys in the cache.
185
195
 
186
- * `set`: This is called when the `set` method is called.
187
- * `setMany`: This is called when the `setMany` method is called.
188
- * `get`: This is called when the `get` method is called.
189
- * `getMany`: This is called when the `getMany` method is called.
190
- * `clear`: This is called when the `clear` method is called.
191
- * `has`: This is called when the `has` method is called.
192
- * `disconnect`: This is called when the `disconnect` method is called.
193
- * `error`: This is called when an error occurs.
196
+ This simple in-memory cache uses multiple Map objects and a with `expiration` and `lru` policies if set to manage the in memory cache at scale.
194
197
 
195
- ## API
198
+ ### CacheableMemory API
196
199
 
197
200
  * `set(key, value, ttl?)`: Sets a value in the cache.
198
- * `setMany([{key, value, ttl?}])`: Sets multiple values in the cache.
199
201
  * `get(key)`: Gets a value from the cache.
200
202
  * `has(key)`: Checks if a value exists in the cache.
201
- * `getMany([keys])`: Gets multiple values from the cache.
202
203
  * `delete(key)`: Deletes a value from the cache.
203
204
  * `clear()`: Clears the cache.
204
- * `disconnect()`: Disconnects from the cache.
205
- * `getStats()`: Gets statistics from the cache.
206
- * `setHook(hook, callback)`: Sets a hook.
207
- * `deleteHook(hook)`: Removes a hook.
208
- * `emitEvent(event, data)`: Emits an event.
209
- * `on(event, callback)`: Listens for an event.
210
- * `removeListener(event, callback)`: Removes a listener.
211
- * `store`: The [Keyv](https://keyv.org) storage engine.
205
+ * `size()`: The number of keys in the cache.
206
+ * `keys()`: The keys in the cache.
207
+
212
208
 
213
209
  ## How to Contribute
214
210
 
215
211
  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`.
216
212
 
217
213
  ## License and Copyright
218
- MIT © Jared Wray - [https://github.com/jaredwray/cacheable/blob/main/LICENSE](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
214
+ [MIT © Jared Wray](./LICENSE)