@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.
Files changed (3) hide show
  1. package/README.md +38 -55
  2. package/README.zh-TW.md +63 -76
  3. 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 AsyncLRUCache?
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
- - **No Setup Required**: Install and use immediately without configuring external services
47
- - **Zero Network Latency**: Direct memory access provides microsecond response times
48
- - **Reduced Infrastructure**: Eliminate cache servers, reducing operational complexity and costs
22
+ - No setup required - install and use immediately
23
+ - Zero network latency - direct memory access
24
+ - Reduced infrastructure complexity
49
25
 
50
- **Reliability & Predictability**
51
- - **No Network Dependencies**: Cache operations never fail due to network issues
52
- - **Consistent Performance**: No variable network latency affecting cache performance
53
- - **Simplified Deployment**: Deploy as part of your application without managing separate cache infrastructure
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>): 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>): Promise<V>`
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
- ### GET Request Merging
232
+ AsyncLRUCache automatically handles concurrent operations:
237
233
 
238
- When multiple concurrent requests fetch the same key, AsyncLRUCache automatically merges these requests, ensuring the loader function executes only once:
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
- 一個支援非同步載入和保存操作的 LRU(Least Recently Used)記憶體快取類別,自動處理並發請求。
3
+ [![npm version](https://badge.fury.io/js/@wfp99%2Fasync-lru-cache.svg)](https://badge.fury.io/js/@wfp99%2Fasync-lru-cache)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](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
- ## 為什麼選擇 AsyncLRUCache?
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
- - **即時開發**:無需設定 Redis、Memcached 等即可開始編碼
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>): 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>): Promise<V>`
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
- ### GET 請求合併
232
+ AsyncLRUCache 會自動處理並發操作:
233
233
 
234
- 當多個並發請求獲取相同的 key 時,AsyncLRUCache 會自動合併這些請求,確保載入器函數只執行一次:
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wfp99/async-lru-cache",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A simple memory cache using the LRU algorithm and supporting asynchronous data access.",
5
5
  "keywords": [
6
6
  "cache",