@wfp99/async-lru-cache 1.1.0 → 1.1.1
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/README.md +38 -55
- package/README.zh-TW.md +63 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,46 +16,17 @@ An asynchronous LRU (Least Recently Used) memory cache that supports asynchronou
|
|
|
16
16
|
- 🛡️ **Error Handling**: Comprehensive error handling with automatic cache cleanup on failures
|
|
17
17
|
- 🧹 **Complete Clearing**: Support for clearing all cache entries at once
|
|
18
18
|
|
|
19
|
-
## Why
|
|
20
|
-
|
|
21
|
-
### The Problem
|
|
22
|
-
Traditional caching solutions often struggle with asynchronous operations:
|
|
23
|
-
- **Race Conditions**: Multiple concurrent requests for the same resource can trigger duplicate expensive operations
|
|
24
|
-
- **Memory Leaks**: Failed operations may leave stale entries in cache without proper cleanup
|
|
25
|
-
- **Complex State Management**: Coordinating cache updates with external persistence layers becomes error-prone
|
|
26
|
-
- **External Dependencies**: Many caching solutions require external services (Redis, Memcached) adding infrastructure complexity, network latency, and deployment overhead
|
|
27
|
-
|
|
28
|
-
### The Solution
|
|
29
|
-
AsyncLRUCache addresses these challenges by providing:
|
|
30
|
-
- **Smart Request Merging**: Multiple concurrent GET calls for the same key automatically share a single loader execution
|
|
31
|
-
- **Automatic Error Recovery**: Failed operations are automatically cleaned up, preventing memory leaks and stale data
|
|
32
|
-
- **Operation Serialization**: PUT operations for the same key are queued and executed sequentially, ensuring data consistency
|
|
33
|
-
- **Zero External Dependencies**: Pure in-memory solution eliminates the need for external caching services, reducing infrastructure complexity, setup overhead, and network latency while increasing reliability and performance
|
|
34
|
-
|
|
35
|
-
### When to Use
|
|
36
|
-
AsyncLRUCache is perfect for scenarios involving:
|
|
37
|
-
- **API Response Caching**: Cache expensive HTTP requests with automatic deduplication
|
|
38
|
-
- **Database Query Results**: Reduce database load while maintaining data consistency
|
|
39
|
-
- **Computed Values**: Cache expensive calculations with built-in invalidation
|
|
40
|
-
- **File System Operations**: Cache file reads/writes with persistence hooks
|
|
41
|
-
- **Microservices Communication**: Reduce inter-service calls with intelligent caching
|
|
42
|
-
|
|
43
|
-
### Why Choose In-Memory Over External Cache Services?
|
|
19
|
+
## Why Choose In-Memory Over External Cache Services?
|
|
44
20
|
|
|
45
21
|
**Simplicity & Performance**
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
22
|
+
- No setup required - install and use immediately
|
|
23
|
+
- Zero network latency - direct memory access
|
|
24
|
+
- Reduced infrastructure complexity
|
|
49
25
|
|
|
50
|
-
**Reliability &
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
**Development Efficiency**
|
|
56
|
-
- **Instant Development**: Start coding immediately without setting up Redis, Memcached, etc.
|
|
57
|
-
- **Easy Testing**: Unit tests run without external service dependencies
|
|
58
|
-
- **Simplified Debugging**: Cache behavior is predictable and traceable within your application process
|
|
26
|
+
**Reliability & Development**
|
|
27
|
+
- No external dependencies or network failures
|
|
28
|
+
- Simplified testing and debugging
|
|
29
|
+
- Works without Redis, Memcached, or other cache servers
|
|
59
30
|
|
|
60
31
|
## Installation
|
|
61
32
|
|
|
@@ -201,24 +172,28 @@ constructor(option: AsyncLRUCacheOption)
|
|
|
201
172
|
| Property | Type | Required | Description |
|
|
202
173
|
|----------|------|----------|-------------|
|
|
203
174
|
| `capacity` | `number` | Yes | Maximum number of items allowed in cache. Must be a positive number. |
|
|
175
|
+
| `defaultTtlMs` | `number` | No | Default TTL for all cache entries (optional) |
|
|
176
|
+
| `cleanupIntervalMs` | `number` | No | Automatic cleanup interval for expired entries (optional) |
|
|
204
177
|
|
|
205
178
|
#### Methods
|
|
206
179
|
|
|
207
|
-
##### `get(key: K, loader: () => Promise<V
|
|
180
|
+
##### `get(key: K, loader: () => Promise<V>, ttlMs?: number): Promise<V>`
|
|
208
181
|
|
|
209
182
|
Retrieves data from cache. If not found, uses the loader to load data.
|
|
210
183
|
|
|
211
184
|
- **key**: Cache key
|
|
212
185
|
- **loader**: Asynchronous loader function executed on cache miss
|
|
186
|
+
- **ttlMs**: Optional TTL override for this entry
|
|
213
187
|
- **Returns**: Promise that resolves to the required data
|
|
214
188
|
|
|
215
|
-
##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void
|
|
189
|
+
##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void>, ttlMs?: number): Promise<V>`
|
|
216
190
|
|
|
217
191
|
Puts a value into cache and optionally executes a saver function for persistence.
|
|
218
192
|
|
|
219
193
|
- **key**: Cache key
|
|
220
194
|
- **value**: Value to cache
|
|
221
195
|
- **saver**: Optional asynchronous saver function
|
|
196
|
+
- **ttlMs**: Optional TTL override for this entry
|
|
222
197
|
- **Returns**: Promise that resolves to the latest value after saver operation completes
|
|
223
198
|
|
|
224
199
|
##### `invalidate(key: K): void`
|
|
@@ -231,11 +206,33 @@ Invalidates and removes the cache entry for the specified key.
|
|
|
231
206
|
|
|
232
207
|
Clears all cache entries. This method removes all items from cache and manually clears node links to prevent potential memory leaks.
|
|
233
208
|
|
|
209
|
+
##### `has(key: K): boolean`
|
|
210
|
+
|
|
211
|
+
Checks if a key exists in cache and is not expired.
|
|
212
|
+
|
|
213
|
+
- **key**: Cache key to check
|
|
214
|
+
- **Returns**: True if key exists and is valid
|
|
215
|
+
|
|
216
|
+
##### `size(): number`
|
|
217
|
+
|
|
218
|
+
Returns the current number of items in cache.
|
|
219
|
+
|
|
220
|
+
- **Returns**: Number of cached items
|
|
221
|
+
|
|
222
|
+
##### `cleanupExpired(): void`
|
|
223
|
+
|
|
224
|
+
Manually removes expired entries from cache.
|
|
225
|
+
|
|
226
|
+
##### `destroy(): void`
|
|
227
|
+
|
|
228
|
+
Destroys the cache, stops cleanup timers and clears all data.
|
|
229
|
+
|
|
234
230
|
## Concurrency Handling
|
|
235
231
|
|
|
236
|
-
|
|
232
|
+
AsyncLRUCache automatically handles concurrent operations:
|
|
237
233
|
|
|
238
|
-
|
|
234
|
+
- **GET Request Merging**: Multiple concurrent requests for the same key share a single loader execution
|
|
235
|
+
- **PUT Operation Serialization**: Multiple PUT operations for the same key are serialized to ensure they execute in order
|
|
239
236
|
|
|
240
237
|
```typescript
|
|
241
238
|
// These three concurrent requests will share the same loader execution
|
|
@@ -244,13 +241,7 @@ const [data1, data2, data3] = await Promise.all([
|
|
|
244
241
|
cache.get('shared-key', loader),
|
|
245
242
|
cache.get('shared-key', loader)
|
|
246
243
|
]);
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### PUT Operation Serialization
|
|
250
|
-
|
|
251
|
-
Multiple PUT operations for the same key are serialized to ensure they execute in order:
|
|
252
244
|
|
|
253
|
-
```typescript
|
|
254
245
|
// These operations will execute sequentially, even if started concurrently
|
|
255
246
|
cache.put('key', 'value1', saver1);
|
|
256
247
|
cache.put('key', 'value2', saver2);
|
|
@@ -285,14 +276,6 @@ const user: User = await userCache.get('user:123', async () => {
|
|
|
285
276
|
- **Operation Chain Errors**: PUT operations ignore previous operation errors, allowing new operations to proceed
|
|
286
277
|
- **Error Logging**: All errors are automatically logged to console for debugging
|
|
287
278
|
|
|
288
|
-
## Memory Management
|
|
289
|
-
|
|
290
|
-
AsyncLRUCache provides comprehensive memory management:
|
|
291
|
-
|
|
292
|
-
- **Automatic Eviction**: Automatically removes least recently used items based on capacity
|
|
293
|
-
- **Manual Cleanup**: `clear()` method thoroughly cleans all node links to prevent memory leaks
|
|
294
|
-
- **Error Cleanup**: Automatically cleans related cache entries when operations fail
|
|
295
|
-
|
|
296
279
|
## License
|
|
297
280
|
|
|
298
281
|
MIT
|
package/README.zh-TW.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Async LRU Cache
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/@wfp99%2Fasync-lru-cache)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
|
|
7
|
+
一個支援非同步載入和保存操作的 LRU(Least Recently Used)記憶體快取,自動處理並發請求。
|
|
4
8
|
|
|
5
9
|
## 特色
|
|
6
10
|
|
|
@@ -9,49 +13,20 @@
|
|
|
9
13
|
- 📝 **序列化寫入**:序列化同一 key 的 PUT 操作,確保順序執行
|
|
10
14
|
- 🗑️ **LRU 淘汰策略**:實現 LRU 演算法,自動移除最久未使用的項目
|
|
11
15
|
- ⏰ **TTL 支援**:支援可配置的生存時間(Time To Live),自動過期快取項目
|
|
12
|
-
- 🛡️
|
|
16
|
+
- 🛡️ **錯誤處理**:完善的錯誤處理機制,失敗時自動清理快取
|
|
13
17
|
- 🧹 **完整清除**:支援一次性清除所有快取項目
|
|
14
18
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
### 問題所在
|
|
18
|
-
傳統的快取解決方案在處理非同步操作時經常遇到困難:
|
|
19
|
-
- **競態條件**:對同一資源的多個並發請求可能觸發重複的昂貴操作
|
|
20
|
-
- **記憶體洩漏**:失敗的操作可能在快取中留下過期項目而沒有適當的清理
|
|
21
|
-
- **複雜的狀態管理**:協調快取更新與外部持久化層變得容易出錯
|
|
22
|
-
- **外部依賴**:許多快取解決方案需要外部服務(Redis、Memcached),增加基礎架構複雜性、網路延遲和部署成本
|
|
23
|
-
|
|
24
|
-
### 解決方案
|
|
25
|
-
AsyncLRUCache 透過以下方式解決這些挑戰:
|
|
26
|
-
- **智慧請求合併**:對同一 key 的多個並發 GET 呼叫自動共享單一載入器執行
|
|
27
|
-
- **自動錯誤恢復**:失敗的操作會自動清理,防止記憶體洩漏和過期資料
|
|
28
|
-
- **操作序列化**:同一 key 的 PUT 操作會排隊並依序執行,確保資料一致性
|
|
29
|
-
- **零外部依賴**:純記憶體內解決方案,無需外部快取服務,減少基礎架構複雜性、設定成本和網路延遲,同時提高可靠性和效能
|
|
30
|
-
|
|
31
|
-
### 適用場景
|
|
32
|
-
AsyncLRUCache 非常適合以下情境:
|
|
33
|
-
- **API 回應快取**:快取昂貴的 HTTP 請求並自動去重複
|
|
34
|
-
- **資料庫查詢結果**:減少資料庫負載同時維持資料一致性
|
|
35
|
-
- **計算值**:快取昂貴的計算結果並內建失效機制
|
|
36
|
-
- **檔案系統操作**:快取檔案讀寫並提供持久化串接
|
|
37
|
-
- **微服務通訊**:透過快取減少服務間呼叫
|
|
38
|
-
|
|
39
|
-
### 為什麼選擇記憶體內快取而非外部快取服務?
|
|
19
|
+
## 為什麼選擇記憶體內快取而非外部快取服務?
|
|
40
20
|
|
|
41
21
|
**簡單性與效能**
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
**可靠性與可預測性**
|
|
47
|
-
- **無網路依賴**:快取操作不會因網路問題而失敗
|
|
48
|
-
- **一致性效能**:沒有可變的網路延遲影響快取效能
|
|
49
|
-
- **簡化部署**:作為應用程式的一部分部署,無需管理獨立的快取基礎架構
|
|
22
|
+
- 無需設定 - 安裝後立即使用
|
|
23
|
+
- 零網路延遲 - 直接記憶體存取
|
|
24
|
+
- 減少基礎架構複雜性
|
|
50
25
|
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
26
|
+
**可靠性與開發**
|
|
27
|
+
- 無外部依賴或網路故障風險
|
|
28
|
+
- 簡化測試和除錯
|
|
29
|
+
- 無需 Redis、Memcached 或其他快取伺服器
|
|
55
30
|
|
|
56
31
|
## 安裝
|
|
57
32
|
|
|
@@ -102,23 +77,6 @@ try {
|
|
|
102
77
|
}
|
|
103
78
|
```
|
|
104
79
|
|
|
105
|
-
### 進階使用
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
// 與資料庫配合使用
|
|
109
|
-
const cache = new AsyncLRUCache<string, UserData>({ capacity: 500 });
|
|
110
|
-
|
|
111
|
-
// 從資料庫載入並自動快取
|
|
112
|
-
const user = await cache.get(`user:${userId}`, async () => {
|
|
113
|
-
return await database.users.findById(userId);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// 同時儲存到快取和資料庫
|
|
117
|
-
await cache.put(`user:${userId}`, updatedUser, async (key, value) => {
|
|
118
|
-
await database.users.update(userId, value);
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
80
|
### TTL(生存時間)支援
|
|
123
81
|
|
|
124
82
|
AsyncLRUCache 支援使用 TTL(Time To Live)自動過期快取項目:
|
|
@@ -172,6 +130,23 @@ cache.destroy();
|
|
|
172
130
|
- 呼叫 `cleanupExpired()` 進行手動清理
|
|
173
131
|
- 呼叫 `destroy()` 停止計時器並防止記憶體洩漏
|
|
174
132
|
|
|
133
|
+
### 進階使用
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// 與資料庫配合使用
|
|
137
|
+
const cache = new AsyncLRUCache<string, UserData>({ capacity: 500 });
|
|
138
|
+
|
|
139
|
+
// 從資料庫載入並自動快取
|
|
140
|
+
const user = await cache.get(`user:${userId}`, async () => {
|
|
141
|
+
return await database.users.findById(userId);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 原子性地保存到快取和資料庫
|
|
145
|
+
await cache.put(`user:${userId}`, updatedUser, async (key, value) => {
|
|
146
|
+
await database.users.update(userId, value);
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
175
150
|
### 快取管理
|
|
176
151
|
|
|
177
152
|
```typescript
|
|
@@ -197,24 +172,28 @@ constructor(option: AsyncLRUCacheOption)
|
|
|
197
172
|
| 屬性 | 類型 | 必需 | 描述 |
|
|
198
173
|
|-----|------|------|------|
|
|
199
174
|
| `capacity` | `number` | 是 | 快取中允許的最大項目數量,必須為正數 |
|
|
175
|
+
| `defaultTtlMs` | `number` | 否 | 所有快取項目的預設 TTL(可選) |
|
|
176
|
+
| `cleanupIntervalMs` | `number` | 否 | 過期項目的自動清理間隔(可選) |
|
|
200
177
|
|
|
201
178
|
#### 方法
|
|
202
179
|
|
|
203
|
-
##### `get(key: K, loader: () => Promise<V
|
|
180
|
+
##### `get(key: K, loader: () => Promise<V>, ttlMs?: number): Promise<V>`
|
|
204
181
|
|
|
205
182
|
從快取中獲取資料。如果不存在,使用載入器載入資料。
|
|
206
183
|
|
|
207
184
|
- **key**: 快取鍵
|
|
208
185
|
- **loader**: 快取未命中時執行的非同步載入函數
|
|
186
|
+
- **ttlMs**: 此項目的可選 TTL 覆寫
|
|
209
187
|
- **回傳**: Promise,解析為所需的資料
|
|
210
188
|
|
|
211
|
-
##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void
|
|
189
|
+
##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void>, ttlMs?: number): Promise<V>`
|
|
212
190
|
|
|
213
191
|
將值放入快取中,並可選擇性地執行保存器函數進行持久化。
|
|
214
192
|
|
|
215
193
|
- **key**: 快取鍵
|
|
216
194
|
- **value**: 要快取的值
|
|
217
195
|
- **saver**: 可選的非同步保存函數
|
|
196
|
+
- **ttlMs**: 此項目的可選 TTL 覆寫
|
|
218
197
|
- **回傳**: Promise,解析為保存操作完成後的最新值
|
|
219
198
|
|
|
220
199
|
##### `invalidate(key: K): void`
|
|
@@ -227,11 +206,33 @@ constructor(option: AsyncLRUCacheOption)
|
|
|
227
206
|
|
|
228
207
|
清除所有快取項目。此方法會移除快取中的所有項目並手動清理節點連結以避免潛在的記憶體洩漏。
|
|
229
208
|
|
|
209
|
+
##### `has(key: K): boolean`
|
|
210
|
+
|
|
211
|
+
檢查快取中是否存在指定鍵且未過期。
|
|
212
|
+
|
|
213
|
+
- **key**: 要檢查的快取鍵
|
|
214
|
+
- **回傳**: 如果鍵存在且有效則回傳 true
|
|
215
|
+
|
|
216
|
+
##### `size(): number`
|
|
217
|
+
|
|
218
|
+
回傳快取中目前的項目數量。
|
|
219
|
+
|
|
220
|
+
- **回傳**: 快取項目的數量
|
|
221
|
+
|
|
222
|
+
##### `cleanupExpired(): void`
|
|
223
|
+
|
|
224
|
+
手動從快取中移除過期項目。
|
|
225
|
+
|
|
226
|
+
##### `destroy(): void`
|
|
227
|
+
|
|
228
|
+
銷毀快取,停止清理計時器並清除所有資料。
|
|
229
|
+
|
|
230
230
|
## 並發處理
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
AsyncLRUCache 會自動處理並發操作:
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
- **GET 請求合併**:對同一 key 的多個並發請求會共享單一載入器執行
|
|
235
|
+
- **PUT 操作序列化**:對同一 key 的多個 PUT 操作會序列化執行以確保順序
|
|
235
236
|
|
|
236
237
|
```typescript
|
|
237
238
|
// 這三個並發請求會共享同一個載入器執行
|
|
@@ -240,13 +241,7 @@ const [data1, data2, data3] = await Promise.all([
|
|
|
240
241
|
cache.get('shared-key', loader),
|
|
241
242
|
cache.get('shared-key', loader)
|
|
242
243
|
]);
|
|
243
|
-
```
|
|
244
244
|
|
|
245
|
-
### PUT 操作序列化
|
|
246
|
-
|
|
247
|
-
對同一 key 的多個 PUT 操作會被序列化,確保它們按順序執行:
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
245
|
// 這些操作會按順序執行,即使它們是並發啟動的
|
|
251
246
|
cache.put('key', 'value1', saver1);
|
|
252
247
|
cache.put('key', 'value2', saver2);
|
|
@@ -274,21 +269,13 @@ const user: User = await userCache.get('user:123', async () => {
|
|
|
274
269
|
});
|
|
275
270
|
```
|
|
276
271
|
|
|
277
|
-
##
|
|
272
|
+
## 錯誤處理
|
|
278
273
|
|
|
279
274
|
- **載入器失敗**:如果載入器函數拋出異常,對應的快取項目會被自動移除
|
|
280
275
|
- **保存器失敗**:如果保存器函數失敗,快取項目也會被移除,確保資料一致性
|
|
281
|
-
- **操作鏈錯誤**:PUT
|
|
276
|
+
- **操作鏈錯誤**:PUT 操作會忽略前一個操作的錯誤,允許新操作繼續進行
|
|
282
277
|
- **錯誤日誌**:所有錯誤都會自動記錄到控制台,便於除錯
|
|
283
278
|
|
|
284
|
-
## 記憶體管理
|
|
285
|
-
|
|
286
|
-
AsyncLRUCache 具備完善的記憶體管理機制:
|
|
287
|
-
|
|
288
|
-
- **自動淘汰**:當快取項目數量超過容量限制時,自動移除最久未使用的項目
|
|
289
|
-
- **手動清理**:`clear()` 方法會徹底清理所有節點連結,防止記憶體洩漏
|
|
290
|
-
- **錯誤清理**:操作失敗時自動清理相關快取項目
|
|
291
|
-
|
|
292
279
|
## 授權
|
|
293
280
|
|
|
294
281
|
MIT
|