@fjell/cache 4.6.22 → 4.7.2
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/CACHE_EVENTS.md +306 -0
- package/CACHE_IMPLEMENTATIONS.md +315 -0
- package/CONFIGURATION_GUIDE.md +167 -0
- package/CRITICAL_FIXES.md +68 -0
- package/MEMORY_LEAK_FIXES.md +270 -0
- package/README.md +513 -2
- package/dist/Aggregator.d.ts +27 -16
- package/dist/Cache.d.ts +59 -1
- package/dist/CacheContext.d.ts +35 -0
- package/dist/CacheMap.d.ts +132 -14
- package/dist/CacheStats.d.ts +51 -0
- package/dist/Instance.d.ts +4 -2
- package/dist/InstanceFactory.d.ts +3 -2
- package/dist/Operations.d.ts +21 -17
- package/dist/Options.d.ts +98 -0
- package/dist/browser/AsyncIndexDBCacheMap.d.ts +38 -0
- package/dist/browser/IndexDBCacheMap.d.ts +69 -0
- package/dist/browser/LocalStorageCacheMap.d.ts +59 -0
- package/dist/browser/SessionStorageCacheMap.d.ts +51 -0
- package/dist/events/CacheEventEmitter.d.ts +82 -0
- package/dist/events/CacheEventFactory.d.ts +121 -0
- package/dist/events/CacheEventTypes.d.ts +122 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/eviction/EvictionManager.d.ts +57 -0
- package/dist/eviction/EvictionStrategy.d.ts +142 -0
- package/dist/eviction/EvictionStrategyConfig.d.ts +97 -0
- package/dist/eviction/EvictionStrategyFactory.d.ts +12 -0
- package/dist/eviction/EvictionStrategyValidation.d.ts +36 -0
- package/dist/eviction/index.d.ts +10 -0
- package/dist/eviction/strategies/ARCEvictionStrategy.d.ts +73 -0
- package/dist/eviction/strategies/FIFOEvictionStrategy.d.ts +12 -0
- package/dist/eviction/strategies/LFUEvictionStrategy.d.ts +38 -0
- package/dist/eviction/strategies/LRUEvictionStrategy.d.ts +12 -0
- package/dist/eviction/strategies/MRUEvictionStrategy.d.ts +12 -0
- package/dist/eviction/strategies/RandomEvictionStrategy.d.ts +12 -0
- package/dist/eviction/strategies/TwoQueueEvictionStrategy.d.ts +54 -0
- package/dist/index.d.ts +29 -6
- package/dist/index.js +5764 -435
- package/dist/index.js.map +4 -4
- package/dist/memory/EnhancedMemoryCacheMap.d.ts +81 -0
- package/dist/memory/MemoryCacheMap.d.ts +48 -0
- package/dist/normalization.d.ts +19 -0
- package/dist/ops/action.d.ts +2 -3
- package/dist/ops/all.d.ts +2 -3
- package/dist/ops/allAction.d.ts +2 -3
- package/dist/ops/allFacet.d.ts +2 -3
- package/dist/ops/create.d.ts +2 -3
- package/dist/ops/facet.d.ts +2 -3
- package/dist/ops/find.d.ts +2 -3
- package/dist/ops/findOne.d.ts +2 -3
- package/dist/ops/get.d.ts +3 -3
- package/dist/ops/one.d.ts +2 -3
- package/dist/ops/remove.d.ts +2 -3
- package/dist/ops/reset.d.ts +2 -1
- package/dist/ops/retrieve.d.ts +2 -3
- package/dist/ops/set.d.ts +2 -2
- package/dist/ops/update.d.ts +2 -3
- package/dist/ttl/TTLManager.d.ts +100 -0
- package/dist/ttl/index.d.ts +2 -0
- package/dist/utils/CacheSize.d.ts +30 -0
- package/fix-async-tests.js +116 -0
- package/package.json +16 -13
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Cache Configuration Quick Reference
|
|
2
|
+
|
|
3
|
+
This guide provides quick reference configurations for the three most common cache types in fjell-cache.
|
|
4
|
+
|
|
5
|
+
## 1. Memory Cache Configuration
|
|
6
|
+
|
|
7
|
+
**Best for**: Fast access, temporary data, development environments
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createInstanceFactory, Options } from '@fjell/cache';
|
|
11
|
+
|
|
12
|
+
const memoryOptions: Partial<Options<User, 'user'>> = {
|
|
13
|
+
cacheType: 'memory',
|
|
14
|
+
memoryConfig: {
|
|
15
|
+
maxItems: 1000, // Store maximum 1000 items
|
|
16
|
+
ttl: 300000 // 5 minutes expiration
|
|
17
|
+
},
|
|
18
|
+
enableDebugLogging: true, // Enable detailed logging
|
|
19
|
+
autoSync: true, // Automatically sync with API
|
|
20
|
+
maxRetries: 3, // Retry failed operations 3 times
|
|
21
|
+
retryDelay: 1000, // Wait 1 second between retries
|
|
22
|
+
ttl: 600000 // 10 minutes default expiration
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const factory = createInstanceFactory(api, memoryOptions);
|
|
26
|
+
const cache = factory(coordinate, { registry });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Key Features**:
|
|
30
|
+
- No persistence (lost on app restart)
|
|
31
|
+
- Fast access times
|
|
32
|
+
- Configurable memory limits
|
|
33
|
+
- Automatic TTL expiration
|
|
34
|
+
|
|
35
|
+
## 2. IndexedDB Configuration
|
|
36
|
+
|
|
37
|
+
**Best for**: Large datasets, offline capability, persistent storage
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const indexedDBOptions: Partial<Options<User, 'user'>> = {
|
|
41
|
+
cacheType: 'indexedDB',
|
|
42
|
+
indexedDBConfig: {
|
|
43
|
+
dbName: 'UserAppCache', // Database name
|
|
44
|
+
version: 2, // Database version (increment for schema changes)
|
|
45
|
+
storeName: 'users' // Object store name
|
|
46
|
+
},
|
|
47
|
+
enableDebugLogging: false, // Disable debug logging in production
|
|
48
|
+
autoSync: true, // Keep data synchronized
|
|
49
|
+
maxRetries: 5, // More retries for async operations
|
|
50
|
+
retryDelay: 2000, // Longer delay for database operations
|
|
51
|
+
ttl: 1800000 // 30 minutes default expiration
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const factory = createInstanceFactory(api, indexedDBOptions);
|
|
55
|
+
const cache = factory(coordinate, { registry });
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Key Features**:
|
|
59
|
+
- Persistent storage (survives browser restart)
|
|
60
|
+
- Large storage capacity (hundreds of MB+)
|
|
61
|
+
- Asynchronous operations
|
|
62
|
+
- Structured database with versioning
|
|
63
|
+
|
|
64
|
+
## 3. localStorage Configuration
|
|
65
|
+
|
|
66
|
+
**Best for**: User preferences, settings, moderate-sized persistent data
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const localStorageOptions: Partial<Options<User, 'user'>> = {
|
|
70
|
+
cacheType: 'localStorage',
|
|
71
|
+
webStorageConfig: {
|
|
72
|
+
keyPrefix: 'myapp:users:', // Namespace to avoid key conflicts
|
|
73
|
+
compress: true // Enable compression to save space
|
|
74
|
+
},
|
|
75
|
+
enableDebugLogging: false, // Usually disabled in production
|
|
76
|
+
autoSync: false, // Manual sync for better control
|
|
77
|
+
maxRetries: 2, // Fewer retries for localStorage (usually fast)
|
|
78
|
+
retryDelay: 500, // Quick retry for localStorage operations
|
|
79
|
+
ttl: 7200000 // 2 hours default expiration
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const factory = createInstanceFactory(api, localStorageOptions);
|
|
83
|
+
const cache = factory(coordinate, { registry });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Key Features**:
|
|
87
|
+
- Persistent storage (survives browser restart)
|
|
88
|
+
- ~5-10MB storage limit
|
|
89
|
+
- Synchronous operations
|
|
90
|
+
- Optional compression
|
|
91
|
+
- Key namespacing for conflict avoidance
|
|
92
|
+
|
|
93
|
+
## Environment-Based Auto-Configuration
|
|
94
|
+
|
|
95
|
+
Automatically select the optimal cache type based on the runtime environment:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
function createOptimalCacheConfiguration(): Partial<Options<User, 'user'>> {
|
|
99
|
+
// Browser with IndexedDB support
|
|
100
|
+
if (typeof window !== 'undefined' && 'indexedDB' in window) {
|
|
101
|
+
return {
|
|
102
|
+
cacheType: 'indexedDB',
|
|
103
|
+
indexedDBConfig: {
|
|
104
|
+
dbName: 'OptimalCache',
|
|
105
|
+
version: 1,
|
|
106
|
+
storeName: 'items'
|
|
107
|
+
},
|
|
108
|
+
enableDebugLogging: false,
|
|
109
|
+
maxRetries: 5,
|
|
110
|
+
retryDelay: 2000
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Browser with localStorage
|
|
115
|
+
if (typeof window !== 'undefined' && 'localStorage' in window) {
|
|
116
|
+
return {
|
|
117
|
+
cacheType: 'localStorage',
|
|
118
|
+
webStorageConfig: {
|
|
119
|
+
keyPrefix: 'optimal:',
|
|
120
|
+
compress: true
|
|
121
|
+
},
|
|
122
|
+
enableDebugLogging: false,
|
|
123
|
+
maxRetries: 3
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Node.js or limited browser - use memory cache
|
|
128
|
+
return {
|
|
129
|
+
cacheType: 'memory',
|
|
130
|
+
memoryConfig: {
|
|
131
|
+
maxItems: 5000,
|
|
132
|
+
ttl: 300000
|
|
133
|
+
},
|
|
134
|
+
enableDebugLogging: true,
|
|
135
|
+
maxRetries: 3
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const optimalOptions = createOptimalCacheConfiguration();
|
|
140
|
+
const factory = createInstanceFactory(api, optimalOptions);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration Comparison
|
|
144
|
+
|
|
145
|
+
| Cache Type | Persistence | Size Limit | Speed | Use Case |
|
|
146
|
+
|------------|-------------|-------------|-------|----------|
|
|
147
|
+
| Memory | None | RAM dependent | Fastest | Temporary data, development |
|
|
148
|
+
| IndexedDB | Permanent | Hundreds of MB+ | Fast | Large datasets, offline apps |
|
|
149
|
+
| localStorage | Permanent | ~5-10MB | Fast | User preferences, settings |
|
|
150
|
+
|
|
151
|
+
## Quick Setup Commands
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Install fjell-cache
|
|
155
|
+
npm install @fjell/cache
|
|
156
|
+
|
|
157
|
+
# Run the configuration example
|
|
158
|
+
npx ts-node examples/cache-type-configurations-example.ts
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## See Also
|
|
162
|
+
|
|
163
|
+
- [Complete Examples](./examples/) - Comprehensive examples directory
|
|
164
|
+
- [README.md](./README.md) - Full documentation
|
|
165
|
+
- [API Documentation](https://getfjell.github.io/fjell-cache/) - Detailed API reference
|
|
166
|
+
|
|
167
|
+
For more detailed examples and use cases, see the [cache-type-configurations-example.ts](./examples/cache-type-configurations-example.ts) file.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Critical Bug fixes Applied
|
|
2
|
+
|
|
3
|
+
This document summarizes the critical bugs that were identified and fixed in the fjell-cache codebase.
|
|
4
|
+
|
|
5
|
+
## Fixed Issues
|
|
6
|
+
|
|
7
|
+
### 1. IndexedDB Race Condition in Initialization
|
|
8
|
+
**File:** `src/browser/IndexDBCacheMap.ts`
|
|
9
|
+
**Problem:** Initialization used fire-and-forget setTimeout with no proper synchronization
|
|
10
|
+
**Fix:** Added proper Promise-based initialization with mutex-like behavior
|
|
11
|
+
|
|
12
|
+
### 2. TwoQueue Eviction Order Bug
|
|
13
|
+
**File:** `src/eviction/strategies/TwoQueueEvictionStrategy.ts`
|
|
14
|
+
**Problem:** Recent queue eviction was selecting newest items instead of oldest (violating FIFO)
|
|
15
|
+
**Fix:** Reversed iteration order to properly evict oldest items from recent queue
|
|
16
|
+
|
|
17
|
+
### 3. ARC Decay Timing Bug
|
|
18
|
+
**File:** `src/eviction/strategies/ARCEvictionStrategy.ts`
|
|
19
|
+
**Problem:** Timer-based decay was using Date.now() for comparisons with config intervals
|
|
20
|
+
**Fix:** Added proper decay timing calculations with multiple intervals per decay period
|
|
21
|
+
|
|
22
|
+
### 4. JSON Normalization Key Ordering
|
|
23
|
+
**File:** `src/normalization.ts`
|
|
24
|
+
**Problem:** JSON.stringify produces non-deterministic key ordering
|
|
25
|
+
**Fix:** Implemented deterministic stringify function with sorted keys
|
|
26
|
+
|
|
27
|
+
### 5. Count-Min Sketch Hash Function
|
|
28
|
+
**File:** `src/eviction/strategies/LFUEvictionStrategy.ts`
|
|
29
|
+
**Problem:** Poor hash distribution causing collisions and -0/+0 issues
|
|
30
|
+
**Fix:** Replaced with FNV-1a hash algorithm for better distribution
|
|
31
|
+
|
|
32
|
+
### 6. Enhanced Memory Cache Metadata Sync
|
|
33
|
+
**File:** `src/memory/EnhancedMemoryCacheMap.ts`
|
|
34
|
+
**Problem:** Updates didn't distinguish between new entries and existing entry modifications
|
|
35
|
+
**Fix:** Added proper old value tracking and selective eviction strategy notifications
|
|
36
|
+
|
|
37
|
+
### 7. ARC Ghost List Memory Leak
|
|
38
|
+
**File:** `src/eviction/strategies/ARCEvictionStrategy.ts`
|
|
39
|
+
**Problem:** Ghost lists could grow unbounded with repeated additions
|
|
40
|
+
**Fix:** Added proper FIFO eviction from ghost lists with max size enforcement
|
|
41
|
+
|
|
42
|
+
### 8. IndexedDB Sync Data Loss
|
|
43
|
+
**File:** `src/browser/IndexDBCacheMap.ts`
|
|
44
|
+
**Problem:** Pending operations could be lost if individual sync attempts failed
|
|
45
|
+
**Fix:** Implemented proper operation queue with retry mechanisms
|
|
46
|
+
|
|
47
|
+
### 9. TTL Race Conditions
|
|
48
|
+
**File:** `src/memory/EnhancedMemoryCacheMap.ts`
|
|
49
|
+
**Problem:** Concurrent TTL checks could cause race conditions
|
|
50
|
+
**Fix:** Added operation tracking to prevent concurrent TTL operations on same keys
|
|
51
|
+
|
|
52
|
+
## Test Results
|
|
53
|
+
|
|
54
|
+
All fixes have been validated with comprehensive tests:
|
|
55
|
+
- **Test Files:** 50 passed
|
|
56
|
+
- **Test Cases:** 1,270 passed
|
|
57
|
+
- **Code Coverage:** 92.81% statements, 86.56% branches
|
|
58
|
+
|
|
59
|
+
## Impact
|
|
60
|
+
|
|
61
|
+
These fixes address critical issues that could have caused:
|
|
62
|
+
- Data corruption and race conditions
|
|
63
|
+
- Memory leaks and performance degradation
|
|
64
|
+
- Inconsistent eviction behavior
|
|
65
|
+
- Silent data loss
|
|
66
|
+
- Cache integrity violations
|
|
67
|
+
|
|
68
|
+
All fixes maintain backward compatibility while significantly improving reliability and performance.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Memory Leak Prevention Fixes in @fjell/cache
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document outlines the comprehensive memory leak prevention features implemented in the fjell-cache event system. These fixes address critical memory management issues that could cause applications to accumulate memory over time without proper cleanup.
|
|
6
|
+
|
|
7
|
+
## Fixed Memory Leak Issues
|
|
8
|
+
|
|
9
|
+
### 1. Static Timestamp State in CacheEventFactory ✅
|
|
10
|
+
|
|
11
|
+
**Problem**: The `CacheEventFactory` class used a static `lastTimestamp` variable that accumulated without cleanup, potentially causing memory issues in long-running applications.
|
|
12
|
+
|
|
13
|
+
**Solution**:
|
|
14
|
+
- Added automatic cleanup mechanisms with instance counting
|
|
15
|
+
- Implemented periodic cleanup that resets stale timestamp state
|
|
16
|
+
- Added `destroyInstance()` method to properly manage static state lifecycle
|
|
17
|
+
- Cleanup timer runs every 60 seconds and doesn't keep the process alive
|
|
18
|
+
|
|
19
|
+
**Files Modified**:
|
|
20
|
+
- `src/events/CacheEventFactory.ts`
|
|
21
|
+
|
|
22
|
+
**Key Features**:
|
|
23
|
+
```typescript
|
|
24
|
+
// Automatic cleanup when instances are destroyed
|
|
25
|
+
CacheEventFactory.destroyInstance();
|
|
26
|
+
|
|
27
|
+
// Periodic cleanup of stale state
|
|
28
|
+
private static performCleanup(): void {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
if (now - this.lastTimestamp > this.MAX_TIMESTAMP_AGE_MS) {
|
|
31
|
+
this.lastTimestamp = 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Weak References for Event Handlers ✅
|
|
37
|
+
|
|
38
|
+
**Problem**: Event handlers held strong references to listener functions, preventing garbage collection even when the listeners were no longer needed.
|
|
39
|
+
|
|
40
|
+
**Solution**:
|
|
41
|
+
- Implemented optional weak references for event listeners
|
|
42
|
+
- Automatic detection and cleanup of garbage-collected listeners
|
|
43
|
+
- Backwards compatible with fallback to strong references when WeakRef is not available
|
|
44
|
+
|
|
45
|
+
**Files Modified**:
|
|
46
|
+
- `src/events/CacheEventEmitter.ts`
|
|
47
|
+
- `src/events/CacheEventTypes.ts`
|
|
48
|
+
|
|
49
|
+
**Key Features**:
|
|
50
|
+
```typescript
|
|
51
|
+
// Weak reference support
|
|
52
|
+
listenerRef?: WeakRef<CacheEventListener<V, S, L1, L2, L3, L4, L5>>;
|
|
53
|
+
|
|
54
|
+
// Automatic cleanup
|
|
55
|
+
if (this.WEAK_REF_ENABLED && subscription.listenerRef) {
|
|
56
|
+
const listener = subscription.listenerRef.deref();
|
|
57
|
+
if (!listener) {
|
|
58
|
+
subscription.isActive = false;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Configuration option
|
|
64
|
+
{ useWeakRef: true }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Automatic Subscription Cleanup with Timeouts ✅
|
|
68
|
+
|
|
69
|
+
**Problem**: Event subscriptions could remain active indefinitely without access, accumulating memory over time.
|
|
70
|
+
|
|
71
|
+
**Solution**:
|
|
72
|
+
- Added periodic cleanup of inactive subscriptions
|
|
73
|
+
- Track last access time for each subscription
|
|
74
|
+
- Automatic removal of subscriptions inactive for more than 5 minutes
|
|
75
|
+
- Cleanup runs every 30 seconds
|
|
76
|
+
|
|
77
|
+
**Files Modified**:
|
|
78
|
+
- `src/events/CacheEventEmitter.ts`
|
|
79
|
+
|
|
80
|
+
**Key Features**:
|
|
81
|
+
```typescript
|
|
82
|
+
// Track access times
|
|
83
|
+
createdAt: number;
|
|
84
|
+
lastAccessTime: number;
|
|
85
|
+
|
|
86
|
+
// Periodic cleanup
|
|
87
|
+
private performPeriodicCleanup(): void {
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
if (now - subscription.lastAccessTime > this.MAX_INACTIVE_TIME_MS) {
|
|
90
|
+
toRemove.push(id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. Cache Destruction Mechanisms ✅
|
|
96
|
+
|
|
97
|
+
**Problem**: Cache instances lacked proper destruction methods, making it difficult to clean up resources when caches were no longer needed.
|
|
98
|
+
|
|
99
|
+
**Solution**:
|
|
100
|
+
- Added `destroy()` method to Cache interface and implementation
|
|
101
|
+
- Comprehensive cleanup of all associated resources
|
|
102
|
+
- Proper timer cleanup to prevent resource leaks
|
|
103
|
+
- Integration with CacheEventFactory instance counting
|
|
104
|
+
|
|
105
|
+
**Files Modified**:
|
|
106
|
+
- `src/Cache.ts`
|
|
107
|
+
- `src/Aggregator.ts`
|
|
108
|
+
|
|
109
|
+
**Key Features**:
|
|
110
|
+
```typescript
|
|
111
|
+
destroy(): void {
|
|
112
|
+
// Clean up event emitter
|
|
113
|
+
eventEmitter.destroy();
|
|
114
|
+
|
|
115
|
+
// Clean up TTL manager
|
|
116
|
+
if (ttlManager && typeof ttlManager.destroy === 'function') {
|
|
117
|
+
ttlManager.destroy();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Clean up cache map
|
|
121
|
+
if (cacheMap && typeof (cacheMap as any).destroy === 'function') {
|
|
122
|
+
(cacheMap as any).destroy();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Notify CacheEventFactory
|
|
126
|
+
CacheEventFactory.destroyInstance();
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 5. Enhanced Timer Management ✅
|
|
131
|
+
|
|
132
|
+
**Problem**: Debounce timers and cleanup intervals could accumulate without proper cleanup.
|
|
133
|
+
|
|
134
|
+
**Solution**:
|
|
135
|
+
- Comprehensive timer cleanup in all destruction paths
|
|
136
|
+
- Use of `timer.unref()` to prevent keeping Node.js process alive
|
|
137
|
+
- Proper cleanup of debounce timers when subscriptions are removed
|
|
138
|
+
|
|
139
|
+
**Files Modified**:
|
|
140
|
+
- `src/events/CacheEventEmitter.ts`
|
|
141
|
+
- `src/events/CacheEventFactory.ts`
|
|
142
|
+
|
|
143
|
+
**Key Features**:
|
|
144
|
+
```typescript
|
|
145
|
+
// Timer cleanup
|
|
146
|
+
if (subscription.debounceTimer) {
|
|
147
|
+
clearTimeout(subscription.debounceTimer);
|
|
148
|
+
subscription.debounceTimer = null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Non-blocking timers
|
|
152
|
+
if (this.cleanupInterval.unref) {
|
|
153
|
+
this.cleanupInterval.unref();
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Configuration Options
|
|
158
|
+
|
|
159
|
+
### Event Subscription Options
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
interface CacheSubscriptionOptions {
|
|
163
|
+
/** Use weak references for the listener (default: true if WeakRef is available) */
|
|
164
|
+
useWeakRef?: boolean;
|
|
165
|
+
|
|
166
|
+
/** Debounce events by this many milliseconds */
|
|
167
|
+
debounceMs?: number;
|
|
168
|
+
|
|
169
|
+
/** Filter by event types */
|
|
170
|
+
eventTypes?: CacheEventType[];
|
|
171
|
+
|
|
172
|
+
/** Optional error handler for listener errors */
|
|
173
|
+
onError?: (error: Error, event: any) => void;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Usage Examples
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Create cache with automatic cleanup
|
|
181
|
+
const cache = createCache(api, coordinate, registry, {
|
|
182
|
+
cacheType: 'memory'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Subscribe with weak references (default)
|
|
186
|
+
const subscription = cache.subscribe(
|
|
187
|
+
(event) => console.log(event.type),
|
|
188
|
+
{
|
|
189
|
+
useWeakRef: true, // Optional: defaults to true
|
|
190
|
+
eventTypes: ['item_created', 'item_updated']
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Proper cleanup when done
|
|
195
|
+
cache.destroy();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Testing
|
|
199
|
+
|
|
200
|
+
Comprehensive integration tests have been added to verify the memory leak prevention features:
|
|
201
|
+
|
|
202
|
+
- `tests/examples/memory-leak-prevention.integration.test.ts`
|
|
203
|
+
- `examples/memory-leak-prevention-example.ts`
|
|
204
|
+
|
|
205
|
+
The tests verify:
|
|
206
|
+
- Static state cleanup in CacheEventFactory
|
|
207
|
+
- Weak reference functionality
|
|
208
|
+
- Subscription timeout cleanup
|
|
209
|
+
- Cache destruction mechanisms
|
|
210
|
+
- Timer cleanup
|
|
211
|
+
|
|
212
|
+
## Monitoring and Debugging
|
|
213
|
+
|
|
214
|
+
### Memory Usage Monitoring
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Check active subscriptions
|
|
218
|
+
console.log(`Active subscriptions: ${cache.eventEmitter.getSubscriptionCount()}`);
|
|
219
|
+
|
|
220
|
+
// Get subscription details
|
|
221
|
+
const subscriptions = cache.eventEmitter.getSubscriptions();
|
|
222
|
+
console.log('Subscription details:', subscriptions);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Performance Impact
|
|
226
|
+
|
|
227
|
+
- Cleanup operations run in the background and don't block application logic
|
|
228
|
+
- Timers are configured to not keep the Node.js process alive
|
|
229
|
+
- Weak references provide automatic memory management with zero performance overhead
|
|
230
|
+
- Periodic cleanup intervals are optimized for balance between memory usage and performance
|
|
231
|
+
|
|
232
|
+
## Migration Guide
|
|
233
|
+
|
|
234
|
+
### For Existing Applications
|
|
235
|
+
|
|
236
|
+
1. **No Breaking Changes**: All existing code will continue to work without modifications
|
|
237
|
+
2. **Opt-in Features**: Weak references and enhanced cleanup are enabled by default but can be disabled
|
|
238
|
+
3. **Recommended Actions**:
|
|
239
|
+
- Add `cache.destroy()` calls when caches are no longer needed
|
|
240
|
+
- Consider enabling weak references for better memory management
|
|
241
|
+
- Monitor subscription counts in long-running applications
|
|
242
|
+
|
|
243
|
+
### Best Practices
|
|
244
|
+
|
|
245
|
+
1. **Always destroy caches**: Call `cache.destroy()` when done with a cache instance
|
|
246
|
+
2. **Use weak references**: Enable weak references for better automatic cleanup
|
|
247
|
+
3. **Monitor subscriptions**: Regularly check subscription counts in production
|
|
248
|
+
4. **Handle errors**: Provide error handlers for event listeners
|
|
249
|
+
5. **Avoid long-lived subscriptions**: Clean up subscriptions that are no longer needed
|
|
250
|
+
|
|
251
|
+
## Future Considerations
|
|
252
|
+
|
|
253
|
+
- Memory usage metrics and monitoring hooks
|
|
254
|
+
- Configurable cleanup intervals
|
|
255
|
+
- Enhanced debugging tools for memory leak detection
|
|
256
|
+
- Integration with application performance monitoring (APM) tools
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Summary
|
|
261
|
+
|
|
262
|
+
The memory leak prevention features provide comprehensive protection against common memory issues in event-driven cache systems:
|
|
263
|
+
|
|
264
|
+
- ✅ **Static state cleanup** prevents accumulation of factory state
|
|
265
|
+
- ✅ **Weak references** enable automatic garbage collection of listeners
|
|
266
|
+
- ✅ **Periodic cleanup** removes inactive subscriptions automatically
|
|
267
|
+
- ✅ **Resource destruction** provides comprehensive cleanup mechanisms
|
|
268
|
+
- ✅ **Timer management** prevents resource leaks from background timers
|
|
269
|
+
|
|
270
|
+
These features ensure that fjell-cache applications can run for extended periods without memory degradation, making the library suitable for production environments with high availability requirements.
|