@fjell/cache 4.7.35 → 4.7.37

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.
@@ -1,270 +0,0 @@
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.
package/MIGRATION_v3.md DELETED
@@ -1,249 +0,0 @@
1
- # Migration Guide: v2.x to v3.0
2
-
3
- ## Overview
4
-
5
- Version 3.0 of `@fjell/cache` adopts the centralized Operations interface from `@fjell/core`. This provides a consistent interface across all Fjell packages, better type safety, and shared validation logic.
6
-
7
- ## Breaking Changes
8
-
9
- ### 1. Operations Interface Now Extends Core
10
-
11
- The `Operations` interface now extends from `@fjell/core/Operations`. This change is mostly transparent to users, but provides better type checking and consistency across the Fjell ecosystem.
12
-
13
- ### 2. Updated `create` Method Signature
14
-
15
- The `create` operation now uses the `CreateOptions` type from `@fjell/core`:
16
-
17
- #### Before (v2.x)
18
- ```typescript
19
- // Create with locations
20
- await cache.operations.create(itemData, locations);
21
-
22
- // Create without locations
23
- await cache.operations.create(itemData, []);
24
- await cache.operations.create(itemData);
25
- ```
26
-
27
- #### After (v3.0)
28
- ```typescript
29
- // Create with locations
30
- await cache.operations.create(itemData, { locations });
31
-
32
- // Create without locations (unchanged)
33
- await cache.operations.create(itemData);
34
-
35
- // Create with specific key (new feature)
36
- await cache.operations.create(itemData, { key: { kt: 'user', pk: 'user-123' } });
37
- ```
38
-
39
- ### 3. New `upsert` Method
40
-
41
- A new `upsert` method has been added to the Operations interface:
42
-
43
- ```typescript
44
- // Update if exists, create if doesn't
45
- const item = await cache.operations.upsert(
46
- key,
47
- itemData,
48
- locations // Optional, used only for creation
49
- );
50
- ```
51
-
52
- ## Migration Steps
53
-
54
- ### Step 1: Update Dependencies
55
-
56
- Update to the latest versions:
57
-
58
- ```bash
59
- npm install @fjell/core@latest @fjell/cache@latest
60
- ```
61
-
62
- ### Step 2: Update `create` Calls
63
-
64
- Find all calls to `cache.operations.create` with a locations parameter and wrap the locations in an object:
65
-
66
- ```typescript
67
- // Search for patterns like:
68
- cache.operations.create(item, locations)
69
- cache.operations.create(item, loc)
70
- cache.operations.create(itemData, [{kt: 'parent', lk: '...'}])
71
-
72
- // Replace with:
73
- cache.operations.create(item, { locations })
74
- cache.operations.create(item, { locations: loc })
75
- cache.operations.create(itemData, { locations: [{kt: 'parent', lk: '...'}] })
76
- ```
77
-
78
- ### Step 3: Optional - Use New `upsert` Method
79
-
80
- If you have code that checks for existence before creating or updating:
81
-
82
- ```typescript
83
- // Before (v2.x)
84
- const existing = await cache.operations.get(key);
85
- if (existing) {
86
- await cache.operations.update(key, updates);
87
- } else {
88
- await cache.operations.create(data, locations);
89
- }
90
-
91
- // After (v3.0) - simpler!
92
- await cache.operations.upsert(key, data, locations);
93
- ```
94
-
95
- ## What Stays The Same
96
-
97
- - All other cache operations work identically (`get`, `all`, `one`, `update`, `remove`, `find`, `findOne`, `action`, `allAction`, `facet`, `allFacet`)
98
- - Cache-specific methods unchanged (`retrieve`, `set`, `reset`)
99
- - Cache events unchanged
100
- - TTL/eviction unchanged
101
- - Cache statistics unchanged
102
- - All cache implementations work the same way
103
- - Cache configuration options unchanged
104
-
105
- ## Type Imports
106
-
107
- You can now import core types from either package:
108
-
109
- ```typescript
110
- // From core (recommended)
111
- import { OperationParams, AffectedKeys } from '@fjell/core';
112
-
113
- // From cache (re-exported for convenience)
114
- import { OperationParams, AffectedKeys } from '@fjell/cache';
115
- ```
116
-
117
- ## Benefits
118
-
119
- ### Smaller Bundle Size
120
- Shared types between packages reduce overall bundle size.
121
-
122
- ### Better Type Safety
123
- Consistent types across packages provide better TypeScript support and catch more errors at compile time.
124
-
125
- ### Consistent Interface
126
- The same Operations interface is used across `@fjell/cache`, `@fjell/lib`, `@fjell/providers`, and other packages.
127
-
128
- ### Enhanced Functionality
129
- New `upsert` method simplifies common patterns.
130
-
131
- ## Examples
132
-
133
- ### Basic Migration Example
134
-
135
- ```typescript
136
- import { createCache } from '@fjell/cache';
137
- import { createCoordinate, createRegistry } from '@fjell/registry';
138
-
139
- // Setup (unchanged)
140
- const registry = createRegistry();
141
- const cache = await createCache(api, createCoordinate('user'), registry);
142
-
143
- // Operations (mostly unchanged)
144
- await cache.operations.all(); // ✓ No change
145
- await cache.operations.get(key); // ✓ No change
146
- await cache.operations.update(key, data); // ✓ No change
147
-
148
- // Create operation (requires update)
149
- await cache.operations.create(data, locations); // ✗ v2.x
150
- await cache.operations.create(data, { locations }); // ✓ v3.0
151
-
152
- // New upsert method
153
- await cache.operations.upsert(key, data); // ✓ New in v3.0
154
- ```
155
-
156
- ### Contained Items Migration
157
-
158
- ```typescript
159
- // Before (v2.x)
160
- const commentData = { text: 'Great post!' };
161
- const locations = [{ kt: 'post', lk: 'post-123' }];
162
- await commentCache.operations.create(commentData, locations);
163
-
164
- // After (v3.0)
165
- const commentData = { text: 'Great post!' };
166
- const locations = [{ kt: 'post', lk: 'post-123' }];
167
- await commentCache.operations.create(commentData, { locations });
168
- ```
169
-
170
- ### Upsert Pattern
171
-
172
- ```typescript
173
- // Before (v2.x) - manual check
174
- async function saveUser(key, userData) {
175
- const existing = await cache.operations.get(key);
176
- if (existing) {
177
- return await cache.operations.update(key, userData);
178
- } else {
179
- return await cache.operations.create(userData, []);
180
- }
181
- }
182
-
183
- // After (v3.0) - built-in upsert
184
- async function saveUser(key, userData) {
185
- return await cache.operations.upsert(key, userData);
186
- }
187
- ```
188
-
189
- ## Troubleshooting
190
-
191
- ### TypeScript Errors
192
-
193
- If you see TypeScript errors after upgrading:
194
-
195
- 1. **"Argument of type 'LocKeyArray' is not assignable to parameter"**
196
- - Wrap the locations array in `{ locations: ... }`
197
-
198
- 2. **"Type X is not assignable to type CreateOptions"**
199
- - Update create calls to use the object syntax
200
-
201
- 3. **"Property 'upsert' does not exist"**
202
- - Ensure you've updated `@fjell/core` and `@fjell/cache` to the latest versions
203
-
204
- ### Runtime Issues
205
-
206
- If the cache still works but you see warnings:
207
-
208
- - Check that all `create` calls are using the new signature
209
- - Verify that dependency versions are compatible
210
-
211
- ## Testing Your Migration
212
-
213
- After migration, verify:
214
-
215
- 1. All create operations work correctly
216
- 2. No TypeScript compilation errors
217
- 3. Existing tests pass
218
- 4. Cache operations function as expected
219
-
220
- ```typescript
221
- // Test create with locations
222
- const item = await cache.operations.create(data, { locations: [loc1] });
223
- expect(item).toBeDefined();
224
-
225
- // Test upsert
226
- const upserted = await cache.operations.upsert(key, updates);
227
- expect(upserted).toBeDefined();
228
- ```
229
-
230
- ## Getting Help
231
-
232
- If you encounter issues during migration:
233
-
234
- 1. Check this guide for common patterns
235
- 2. Review the [PHASE_3_CACHE_MIGRATION.md](../PHASE_3_CACHE_MIGRATION.md) for implementation details
236
- 3. Open an issue on GitHub with your specific use case
237
-
238
- ## Summary
239
-
240
- The migration from v2.x to v3.0 requires minimal changes:
241
-
242
- - ✅ Update `create(item, locations)` to `create(item, { locations })`
243
- - ✅ Optionally use new `upsert` method for update-or-create patterns
244
- - ✅ All other operations remain unchanged
245
- - ✅ Cache behavior and performance unchanged
246
- - ✅ Better type safety and consistency across packages
247
-
248
- The migration should take less than an hour for most codebases, with the majority of time spent finding and updating `create` calls.
249
-