koatty_store 1.7.0 → 1.9.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/.rollup.config.js +59 -59
- package/CHANGELOG.md +94 -27
- package/LICENSE +29 -29
- package/README.md +373 -2
- package/dist/LICENSE +29 -29
- package/dist/README.md +373 -2
- package/dist/index.d.ts +318 -99
- package/dist/index.js +902 -251
- package/dist/index.mjs +902 -251
- package/dist/package.json +107 -103
- package/package.json +107 -103
package/dist/LICENSE
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
BSD 3-Clause License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020, Koatty
|
|
4
|
-
All rights reserved.
|
|
5
|
-
|
|
6
|
-
Redistribution and use in source and binary forms, with or without
|
|
7
|
-
modification, are permitted provided that the following conditions are met:
|
|
8
|
-
|
|
9
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
-
list of conditions and the following disclaimer.
|
|
11
|
-
|
|
12
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
-
this list of conditions and the following disclaimer in the documentation
|
|
14
|
-
and/or other materials provided with the distribution.
|
|
15
|
-
|
|
16
|
-
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
-
contributors may be used to endorse or promote products derived from
|
|
18
|
-
this software without specific prior written permission.
|
|
19
|
-
|
|
20
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020, Koatty
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/dist/README.md
CHANGED
|
@@ -1,2 +1,373 @@
|
|
|
1
|
-
# koatty_store
|
|
2
|
-
|
|
1
|
+
# koatty_store
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/koatty_store)
|
|
4
|
+
[](https://npmjs.org/package/koatty_store)
|
|
5
|
+
|
|
6
|
+
Cache store (memory or redis) for Koatty framework.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🚀 **Dual Storage**: Support both in-memory and Redis storage
|
|
11
|
+
- 💾 **LRU Cache**: Built-in LRU cache with configurable size
|
|
12
|
+
- ⏰ **TTL Support**: Field-level TTL for hash operations
|
|
13
|
+
- 🔒 **Concurrency Safe**: Atomic operations with lock protection
|
|
14
|
+
- 📊 **Rich Data Types**: String, Hash, List, Set, Sorted Set
|
|
15
|
+
- 🔄 **Auto Reconnect**: Redis connection with retry mechanism
|
|
16
|
+
- 🎯 **Type Safe**: Full TypeScript support
|
|
17
|
+
- 📦 **Lightweight**: Minimal dependencies
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install koatty_store
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Memory Store
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { CacheStore } from 'koatty_store';
|
|
31
|
+
|
|
32
|
+
// Create memory store
|
|
33
|
+
const store = new CacheStore({
|
|
34
|
+
type: 'memory',
|
|
35
|
+
keyPrefix: 'myapp:',
|
|
36
|
+
maxKeys: 1000
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// String operations
|
|
40
|
+
await store.set('user:1', 'John', 60); // Expires in 60 seconds
|
|
41
|
+
const value = await store.get('user:1');
|
|
42
|
+
|
|
43
|
+
// Hash operations
|
|
44
|
+
await store.hset('user:info', 'name', 'John');
|
|
45
|
+
await store.hset('user:info', 'age', '25');
|
|
46
|
+
const name = await store.hget('user:info', 'name');
|
|
47
|
+
|
|
48
|
+
// List operations
|
|
49
|
+
await store.rpush('queue', 'task1');
|
|
50
|
+
await store.rpush('queue', 'task2');
|
|
51
|
+
const task = await store.lpop('queue');
|
|
52
|
+
|
|
53
|
+
// Close store
|
|
54
|
+
await store.close();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Redis Store
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { CacheStore } from 'koatty_store';
|
|
61
|
+
|
|
62
|
+
// Create redis store
|
|
63
|
+
const store = new CacheStore({
|
|
64
|
+
type: 'redis',
|
|
65
|
+
host: '127.0.0.1',
|
|
66
|
+
port: 6379,
|
|
67
|
+
password: 'your-password',
|
|
68
|
+
db: 0,
|
|
69
|
+
keyPrefix: 'myapp:',
|
|
70
|
+
poolSize: 10
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Use same API as memory store
|
|
74
|
+
await store.set('key', 'value');
|
|
75
|
+
await store.close();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
### Memory Store Options
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface MemoryStoreOpt {
|
|
84
|
+
type: 'memory';
|
|
85
|
+
keyPrefix?: string; // Key prefix, default: 'Koatty'
|
|
86
|
+
db?: number; // Database index, default: 0
|
|
87
|
+
timeout?: number; // Default TTL in seconds, default: 600
|
|
88
|
+
maxKeys?: number; // LRU max keys, default: 1000
|
|
89
|
+
maxMemory?: number; // Max memory in bytes
|
|
90
|
+
evictionPolicy?: 'lru' | 'lfu' | 'random'; // Eviction policy, default: 'lru'
|
|
91
|
+
ttlCheckInterval?: number; // TTL check interval in ms, default: 60000
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Redis Store Options
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface RedisStoreOpt {
|
|
99
|
+
type: 'redis';
|
|
100
|
+
host: string; // Redis host
|
|
101
|
+
port: number; // Redis port
|
|
102
|
+
password?: string; // Redis password
|
|
103
|
+
db?: number; // Database index, default: 0
|
|
104
|
+
keyPrefix?: string; // Key prefix, default: 'Koatty'
|
|
105
|
+
timeout?: number; // Default TTL in seconds, default: 600
|
|
106
|
+
poolSize?: number; // Connection pool size, default: 10
|
|
107
|
+
connectTimeout?: number; // Connection timeout in ms, default: 500
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## API Reference
|
|
112
|
+
|
|
113
|
+
### String Operations
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Set a string value with optional TTL
|
|
117
|
+
await store.set(key: string, value: string | number, timeout?: number): Promise<string>
|
|
118
|
+
|
|
119
|
+
// Get a string value
|
|
120
|
+
await store.get(key: string): Promise<string | null>
|
|
121
|
+
|
|
122
|
+
// Delete a key
|
|
123
|
+
await store.del(key: string): Promise<number>
|
|
124
|
+
|
|
125
|
+
// Check if key exists
|
|
126
|
+
await store.exists(key: string): Promise<number>
|
|
127
|
+
|
|
128
|
+
// Get TTL of a key
|
|
129
|
+
await store.ttl(key: string): Promise<number>
|
|
130
|
+
|
|
131
|
+
// Set TTL for a key
|
|
132
|
+
await store.expire(key: string, timeout: number): Promise<number>
|
|
133
|
+
|
|
134
|
+
// Increment
|
|
135
|
+
await store.incr(key: string): Promise<number>
|
|
136
|
+
await store.incrby(key: string, increment: number): Promise<number>
|
|
137
|
+
|
|
138
|
+
// Decrement
|
|
139
|
+
await store.decr(key: string): Promise<number>
|
|
140
|
+
await store.decrby(key: string, decrement: number): Promise<number>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Hash Operations
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Set hash field with optional TTL (field-level)
|
|
147
|
+
await store.hset(name: string, key: string, value: string | number, timeout?: number): Promise<number>
|
|
148
|
+
|
|
149
|
+
// Get hash field
|
|
150
|
+
await store.hget(name: string, key: string): Promise<string | null>
|
|
151
|
+
|
|
152
|
+
// Delete hash field
|
|
153
|
+
await store.hdel(name: string, key: string): Promise<number>
|
|
154
|
+
|
|
155
|
+
// Check if hash field exists
|
|
156
|
+
await store.hexists(name: string, key: string): Promise<number>
|
|
157
|
+
|
|
158
|
+
// Get all fields and values
|
|
159
|
+
await store.hgetall(name: string): Promise<any>
|
|
160
|
+
|
|
161
|
+
// Get all field names
|
|
162
|
+
await store.hkeys(name: string): Promise<string[]>
|
|
163
|
+
|
|
164
|
+
// Get all values
|
|
165
|
+
await store.hvals(name: string): Promise<any[]>
|
|
166
|
+
|
|
167
|
+
// Get hash length
|
|
168
|
+
await store.hlen(name: string): Promise<number>
|
|
169
|
+
|
|
170
|
+
// Increment hash field
|
|
171
|
+
await store.hincrby(name: string, key: string, increment: number): Promise<number>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### List Operations
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Push to right
|
|
178
|
+
await store.rpush(name: string, value: string | number): Promise<number>
|
|
179
|
+
|
|
180
|
+
// Push to left
|
|
181
|
+
await store.lpush(name: string, value: string | number): Promise<number>
|
|
182
|
+
|
|
183
|
+
// Pop from left
|
|
184
|
+
await store.lpop(name: string): Promise<string | null>
|
|
185
|
+
|
|
186
|
+
// Pop from right
|
|
187
|
+
await store.rpop(name: string): Promise<string | null>
|
|
188
|
+
|
|
189
|
+
// Get list length
|
|
190
|
+
await store.llen(name: string): Promise<number>
|
|
191
|
+
|
|
192
|
+
// Get range
|
|
193
|
+
await store.lrange(name: string, start: number, stop: number): Promise<any[]>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Set Operations
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Add member with optional TTL
|
|
200
|
+
await store.sadd(name: string, value: string | number, timeout?: number): Promise<number>
|
|
201
|
+
|
|
202
|
+
// Remove member
|
|
203
|
+
await store.srem(name: string, key: string): Promise<number>
|
|
204
|
+
|
|
205
|
+
// Get set size
|
|
206
|
+
await store.scard(name: string): Promise<number>
|
|
207
|
+
|
|
208
|
+
// Check if member exists
|
|
209
|
+
await store.sismember(name: string, key: string): Promise<number>
|
|
210
|
+
|
|
211
|
+
// Get all members
|
|
212
|
+
await store.smembers(name: string): Promise<any[]>
|
|
213
|
+
|
|
214
|
+
// Pop random member
|
|
215
|
+
await store.spop(name: string): Promise<any>
|
|
216
|
+
|
|
217
|
+
// Move member between sets
|
|
218
|
+
await store.smove(source: string, destination: string, member: string): Promise<number>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Advanced Features
|
|
222
|
+
|
|
223
|
+
### Field-Level TTL for Hash
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Set hash field with TTL (expires in 60 seconds)
|
|
227
|
+
await store.hset('user:session', 'token', 'abc123', 60);
|
|
228
|
+
|
|
229
|
+
// Field will be automatically deleted after TTL expires
|
|
230
|
+
setTimeout(async () => {
|
|
231
|
+
const token = await store.hget('user:session', 'token');
|
|
232
|
+
console.log(token); // null
|
|
233
|
+
}, 61000);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Singleton Pattern
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Get singleton instance
|
|
240
|
+
const store1 = CacheStore.getInstance({
|
|
241
|
+
type: 'memory',
|
|
242
|
+
keyPrefix: 'app:'
|
|
243
|
+
}, 'cache1');
|
|
244
|
+
|
|
245
|
+
const store2 = CacheStore.getInstance({
|
|
246
|
+
type: 'memory',
|
|
247
|
+
keyPrefix: 'app:'
|
|
248
|
+
}, 'cache1');
|
|
249
|
+
|
|
250
|
+
console.log(store1 === store2); // true
|
|
251
|
+
|
|
252
|
+
// Clear specific instance
|
|
253
|
+
await CacheStore.clearInstance('cache1');
|
|
254
|
+
|
|
255
|
+
// Clear all instances
|
|
256
|
+
await CacheStore.clearAllInstances();
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Concurrency Safe Operations
|
|
260
|
+
|
|
261
|
+
All atomic operations (incr, decr, incrby, decrby, hincrby) are protected with locks to ensure data consistency in concurrent scenarios.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// Safe concurrent increments
|
|
265
|
+
await Promise.all([
|
|
266
|
+
store.incr('counter'),
|
|
267
|
+
store.incr('counter'),
|
|
268
|
+
store.incr('counter')
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
const count = await store.get('counter');
|
|
272
|
+
console.log(count); // "3" - guaranteed consistency
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Best Practices
|
|
276
|
+
|
|
277
|
+
### 1. Use Key Prefix
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const store = new CacheStore({
|
|
281
|
+
type: 'memory',
|
|
282
|
+
keyPrefix: 'myapp:' // Prefix all keys
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 2. Set Appropriate TTL
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// Short-lived data
|
|
290
|
+
await store.set('session:token', token, 3600); // 1 hour
|
|
291
|
+
|
|
292
|
+
// Long-lived data
|
|
293
|
+
await store.set('user:profile', profile, 86400); // 24 hours
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 3. Handle Errors Gracefully
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
try {
|
|
300
|
+
await store.set('key', 'value');
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('Cache operation failed:', error.message);
|
|
303
|
+
// Fallback to database or other source
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 4. Close Store on Exit
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
process.on('SIGINT', async () => {
|
|
311
|
+
await store.close();
|
|
312
|
+
process.exit(0);
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 5. Use Connection Pool for Redis
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
const store = new CacheStore({
|
|
320
|
+
type: 'redis',
|
|
321
|
+
host: '127.0.0.1',
|
|
322
|
+
port: 6379,
|
|
323
|
+
poolSize: 20 // Adjust based on load
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Testing
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Run tests
|
|
331
|
+
npm test
|
|
332
|
+
|
|
333
|
+
# Run tests with coverage
|
|
334
|
+
npm test -- --coverage
|
|
335
|
+
|
|
336
|
+
# Run specific test
|
|
337
|
+
npm test -- test/memory.test.ts
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Performance
|
|
341
|
+
|
|
342
|
+
### Memory Store
|
|
343
|
+
- **Operations**: ~1,000,000 ops/sec
|
|
344
|
+
- **LRU Eviction**: O(1)
|
|
345
|
+
- **TTL Check**: Background task, configurable interval
|
|
346
|
+
|
|
347
|
+
### Redis Store
|
|
348
|
+
- **Operations**: Depends on Redis server
|
|
349
|
+
- **Connection Pool**: Configurable size
|
|
350
|
+
- **Auto Reconnect**: Exponential backoff strategy
|
|
351
|
+
|
|
352
|
+
## Changelog
|
|
353
|
+
|
|
354
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
BSD-3-Clause
|
|
359
|
+
|
|
360
|
+
## Contributing
|
|
361
|
+
|
|
362
|
+
Contributions are welcome! Please read the [contributing guidelines](https://github.com/koatty/koatty_store/blob/master/CONTRIBUTING.md) first.
|
|
363
|
+
|
|
364
|
+
## Support
|
|
365
|
+
|
|
366
|
+
- [GitHub Issues](https://github.com/koatty/koatty_store/issues)
|
|
367
|
+
- [Documentation](https://koatty.com)
|
|
368
|
+
|
|
369
|
+
## Related Projects
|
|
370
|
+
|
|
371
|
+
- [koatty](https://github.com/koatty/koatty) - The Koatty framework
|
|
372
|
+
- [koatty_lib](https://github.com/koatty/koatty_lib) - Koatty utilities
|
|
373
|
+
- [koatty_logger](https://github.com/koatty/koatty_logger) - Koatty logger
|