masterrecord 0.3.6 → 0.3.7
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/context.js +14 -6
- package/package.json +1 -1
- package/readme.md +34 -1
- package/test/multiContextCache.test.js +230 -0
- package/test/multiContextCacheSimple.test.js +185 -0
package/context.js
CHANGED
|
@@ -33,18 +33,26 @@ class context {
|
|
|
33
33
|
isMySQL = false;
|
|
34
34
|
isPostgres = false;
|
|
35
35
|
|
|
36
|
+
// Static shared cache - all context instances share the same cache
|
|
37
|
+
static _sharedQueryCache = null;
|
|
38
|
+
|
|
36
39
|
constructor(){
|
|
37
40
|
this. __environment = process.env.master;
|
|
38
41
|
this.__name = this.constructor.name;
|
|
39
42
|
this._SQLEngine = "";
|
|
40
43
|
this.__trackedEntitiesMap = new Map(); // Initialize Map for O(1) lookups
|
|
41
44
|
|
|
42
|
-
// Initialize query cache
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// Initialize shared query cache (only once across all instances)
|
|
46
|
+
if (!context._sharedQueryCache) {
|
|
47
|
+
context._sharedQueryCache = new QueryCache({
|
|
48
|
+
ttl: process.env.QUERY_CACHE_TTL || 5 * 60 * 1000, // 5 min default
|
|
49
|
+
maxSize: process.env.QUERY_CACHE_SIZE || 1000,
|
|
50
|
+
enabled: process.env.QUERY_CACHE_ENABLED !== 'false'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Reference the shared cache
|
|
55
|
+
this._queryCache = context._sharedQueryCache;
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "masterrecord",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "An Object-relational mapping for the Master framework. Master Record connects classes to relational database tables to establish a database with almost zero-configuration ",
|
|
5
5
|
"main": "MasterRecord.js",
|
|
6
6
|
"bin": {
|
package/readme.md
CHANGED
|
@@ -787,7 +787,7 @@ MasterRecord includes a **production-grade two-level caching system** similar to
|
|
|
787
787
|
|
|
788
788
|
#### Basic Usage (Default Behavior)
|
|
789
789
|
|
|
790
|
-
Caching is **enabled by default** and requires zero configuration:
|
|
790
|
+
Caching is **enabled by default** and requires zero configuration. The cache is **shared across all context instances** to ensure consistency:
|
|
791
791
|
|
|
792
792
|
```javascript
|
|
793
793
|
const db = new AppContext();
|
|
@@ -804,6 +804,10 @@ db.saveChanges(); // Cache for User table cleared
|
|
|
804
804
|
|
|
805
805
|
// Next query hits database again (cache miss)
|
|
806
806
|
const user3 = db.User.where(u => u.id == $$, 1).single();
|
|
807
|
+
|
|
808
|
+
// Cache is shared across all context instances
|
|
809
|
+
const db2 = new AppContext();
|
|
810
|
+
const user4 = db2.User.findById(1); // Also uses shared cache
|
|
807
811
|
```
|
|
808
812
|
|
|
809
813
|
#### Configuration
|
|
@@ -988,6 +992,35 @@ if (parseFloat(stats.hitRate) < 50) {
|
|
|
988
992
|
}
|
|
989
993
|
```
|
|
990
994
|
|
|
995
|
+
#### Important: Shared Cache Behavior
|
|
996
|
+
|
|
997
|
+
**The cache is shared across all context instances of the same class.** This ensures consistency:
|
|
998
|
+
|
|
999
|
+
```javascript
|
|
1000
|
+
const db1 = new AppContext();
|
|
1001
|
+
const db2 = new AppContext();
|
|
1002
|
+
|
|
1003
|
+
// Context 1: Cache data
|
|
1004
|
+
const user1 = db1.User.findById(1); // DB query, cached
|
|
1005
|
+
|
|
1006
|
+
// Context 2: Sees cached data
|
|
1007
|
+
const user2 = db2.User.findById(1); // Cache hit!
|
|
1008
|
+
|
|
1009
|
+
// Context 2: Updates invalidate cache for BOTH contexts
|
|
1010
|
+
user2.name = "Updated";
|
|
1011
|
+
db2.saveChanges(); // Invalidates shared cache
|
|
1012
|
+
|
|
1013
|
+
// Context 1: Sees fresh data
|
|
1014
|
+
const user3 = db1.User.findById(1); // Cache miss, fresh data
|
|
1015
|
+
console.log(user3.name); // "Updated"
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
**Why shared cache?**
|
|
1019
|
+
- ✅ Prevents stale data across multiple context instances
|
|
1020
|
+
- ✅ Ensures all parts of your application see consistent data
|
|
1021
|
+
- ✅ Reduces memory usage (one cache instead of many)
|
|
1022
|
+
- ✅ Correct behavior for single-database applications (most use cases)
|
|
1023
|
+
|
|
991
1024
|
### Multi-Context Applications
|
|
992
1025
|
|
|
993
1026
|
Manage multiple databases in one application:
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Multi-Context Cache Sharing
|
|
3
|
+
*
|
|
4
|
+
* Verifies that cache is shared across context instances
|
|
5
|
+
* so invalidation in one context affects all contexts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const context = require('../context');
|
|
9
|
+
const QueryCache = require('../Cache/QueryCache');
|
|
10
|
+
|
|
11
|
+
console.log("╔════════════════════════════════════════════════════════════════╗");
|
|
12
|
+
console.log("║ Multi-Context Cache Sharing Test ║");
|
|
13
|
+
console.log("╚════════════════════════════════════════════════════════════════╝\n");
|
|
14
|
+
|
|
15
|
+
let passed = 0;
|
|
16
|
+
let failed = 0;
|
|
17
|
+
|
|
18
|
+
// Test 1: Multiple context instances share the same cache
|
|
19
|
+
console.log("📝 Test 1: Multiple contexts share same cache instance");
|
|
20
|
+
console.log("──────────────────────────────────────────────────");
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
class TestContext extends context {
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const db1 = new TestContext();
|
|
30
|
+
const db2 = new TestContext();
|
|
31
|
+
|
|
32
|
+
const areSameInstance = db1._queryCache === db2._queryCache;
|
|
33
|
+
const isSharedCache = db1._queryCache === context._sharedQueryCache;
|
|
34
|
+
|
|
35
|
+
if(areSameInstance && isSharedCache) {
|
|
36
|
+
console.log(" ✓ Both context instances share the same cache");
|
|
37
|
+
console.log(" ✓ Cache is stored in static property");
|
|
38
|
+
passed++;
|
|
39
|
+
} else {
|
|
40
|
+
console.log(` ✗ Contexts have separate caches (BUG!)`);
|
|
41
|
+
console.log(` ✗ db1._queryCache === db2._queryCache: ${areSameInstance}`);
|
|
42
|
+
console.log(` ✗ Shared cache exists: ${isSharedCache}`);
|
|
43
|
+
failed++;
|
|
44
|
+
}
|
|
45
|
+
} catch(err) {
|
|
46
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
47
|
+
failed++;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Test 2: Cache operations in one context affect another context
|
|
51
|
+
console.log("\n📝 Test 2: Cache operations are shared");
|
|
52
|
+
console.log("──────────────────────────────────────────────────");
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
class TestContext extends context {
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const db1 = new TestContext();
|
|
62
|
+
const db2 = new TestContext();
|
|
63
|
+
|
|
64
|
+
// Context 1: Add to cache
|
|
65
|
+
const key = db1._queryCache.generateKey('SELECT * FROM users', [], 'users');
|
|
66
|
+
db1._queryCache.set(key, [{ id: 1, name: 'John' }], 'users');
|
|
67
|
+
|
|
68
|
+
// Context 2: Should see the same cached data
|
|
69
|
+
const cached = db2._queryCache.get(key);
|
|
70
|
+
|
|
71
|
+
if(cached && cached[0].name === 'John') {
|
|
72
|
+
console.log(" ✓ Cache data visible across contexts");
|
|
73
|
+
passed++;
|
|
74
|
+
} else {
|
|
75
|
+
console.log(` ✗ Cache data not shared across contexts`);
|
|
76
|
+
failed++;
|
|
77
|
+
}
|
|
78
|
+
} catch(err) {
|
|
79
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
80
|
+
failed++;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Test 3: Cache invalidation in one context affects another
|
|
84
|
+
console.log("\n📝 Test 3: Cache invalidation is shared");
|
|
85
|
+
console.log("──────────────────────────────────────────────────");
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
class TestContext extends context {
|
|
89
|
+
constructor() {
|
|
90
|
+
super();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const db1 = new TestContext();
|
|
95
|
+
const db2 = new TestContext();
|
|
96
|
+
|
|
97
|
+
// Context 1: Add multiple entries to cache
|
|
98
|
+
const key1 = db1._queryCache.generateKey('SELECT * FROM users WHERE id=1', [], 'users');
|
|
99
|
+
const key2 = db1._queryCache.generateKey('SELECT * FROM users WHERE id=2', [], 'users');
|
|
100
|
+
db1._queryCache.set(key1, { id: 1, name: 'John' }, 'users');
|
|
101
|
+
db1._queryCache.set(key2, { id: 2, name: 'Jane' }, 'users');
|
|
102
|
+
|
|
103
|
+
// Verify both are cached
|
|
104
|
+
const beforeCached1 = db1._queryCache.get(key1);
|
|
105
|
+
const beforeCached2 = db1._queryCache.get(key2);
|
|
106
|
+
|
|
107
|
+
// Context 2: Invalidate User table
|
|
108
|
+
db2._queryCache.invalidateTable('users');
|
|
109
|
+
|
|
110
|
+
// Context 1: Should see invalidation
|
|
111
|
+
const afterCached1 = db1._queryCache.get(key1);
|
|
112
|
+
const afterCached2 = db1._queryCache.get(key2);
|
|
113
|
+
|
|
114
|
+
if(beforeCached1 !== null && beforeCached2 !== null && afterCached1 === null && afterCached2 === null) {
|
|
115
|
+
console.log(" ✓ Data was cached in context 1");
|
|
116
|
+
console.log(" ✓ Invalidation in context 2 affected context 1");
|
|
117
|
+
console.log(" ✓ Cache properly shared across contexts");
|
|
118
|
+
passed++;
|
|
119
|
+
} else {
|
|
120
|
+
console.log(` ✗ Invalidation not shared properly`);
|
|
121
|
+
console.log(` ✗ Before: cached1=${beforeCached1 !== null}, cached2=${beforeCached2 !== null}`);
|
|
122
|
+
console.log(` ✗ After: cached1=${afterCached1 !== null}, cached2=${afterCached2 !== null}`);
|
|
123
|
+
failed++;
|
|
124
|
+
}
|
|
125
|
+
} catch(err) {
|
|
126
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
127
|
+
failed++;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Test 4: Cache statistics are shared
|
|
131
|
+
console.log("\n📝 Test 4: Cache statistics are shared");
|
|
132
|
+
console.log("──────────────────────────────────────────────────");
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// Clear existing cache for clean test
|
|
136
|
+
context._sharedQueryCache.clear();
|
|
137
|
+
|
|
138
|
+
class TestContext extends context {
|
|
139
|
+
constructor() {
|
|
140
|
+
super();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const db1 = new TestContext();
|
|
145
|
+
const db2 = new TestContext();
|
|
146
|
+
|
|
147
|
+
// Context 1: Generate hits/misses
|
|
148
|
+
const key = db1._queryCache.generateKey('query', [], 'users');
|
|
149
|
+
db1._queryCache.set(key, 'data', 'users');
|
|
150
|
+
db1._queryCache.get(key); // Hit
|
|
151
|
+
db1._queryCache.get('nonexistent'); // Miss
|
|
152
|
+
|
|
153
|
+
// Context 2: Should see same stats
|
|
154
|
+
const stats1 = db1.getCacheStats();
|
|
155
|
+
const stats2 = db2.getCacheStats();
|
|
156
|
+
|
|
157
|
+
if(stats1.hits === 1 && stats2.hits === 1 && stats1.misses === 1 && stats2.misses === 1) {
|
|
158
|
+
console.log(" ✓ Cache statistics shared across contexts");
|
|
159
|
+
console.log(` ✓ Both contexts see: ${stats1.hits} hit, ${stats1.misses} miss`);
|
|
160
|
+
passed++;
|
|
161
|
+
} else {
|
|
162
|
+
console.log(` ✗ Statistics not shared`);
|
|
163
|
+
console.log(` ✗ db1 stats: ${JSON.stringify(stats1)}`);
|
|
164
|
+
console.log(` ✗ db2 stats: ${JSON.stringify(stats2)}`);
|
|
165
|
+
failed++;
|
|
166
|
+
}
|
|
167
|
+
} catch(err) {
|
|
168
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
169
|
+
failed++;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Test 5: Clear cache from one context affects all
|
|
173
|
+
console.log("\n📝 Test 5: Clear cache affects all contexts");
|
|
174
|
+
console.log("──────────────────────────────────────────────────");
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
class TestContext extends context {
|
|
178
|
+
constructor() {
|
|
179
|
+
super();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const db1 = new TestContext();
|
|
184
|
+
const db2 = new TestContext();
|
|
185
|
+
|
|
186
|
+
// Context 1: Add data
|
|
187
|
+
const key = db1._queryCache.generateKey('query', [], 'users');
|
|
188
|
+
db1._queryCache.set(key, 'data', 'users');
|
|
189
|
+
|
|
190
|
+
// Verify cached in both
|
|
191
|
+
const before1 = db1._queryCache.get(key);
|
|
192
|
+
const before2 = db2._queryCache.get(key);
|
|
193
|
+
|
|
194
|
+
// Context 2: Clear cache
|
|
195
|
+
db2.clearQueryCache();
|
|
196
|
+
|
|
197
|
+
// Both contexts should see empty cache
|
|
198
|
+
const after1 = db1._queryCache.get(key);
|
|
199
|
+
const after2 = db2._queryCache.get(key);
|
|
200
|
+
|
|
201
|
+
if(before1 !== null && before2 !== null && after1 === null && after2 === null) {
|
|
202
|
+
console.log(" ✓ Data cached in both contexts initially");
|
|
203
|
+
console.log(" ✓ Clear from context 2 affected context 1");
|
|
204
|
+
passed++;
|
|
205
|
+
} else {
|
|
206
|
+
console.log(` ✗ Clear not shared across contexts`);
|
|
207
|
+
failed++;
|
|
208
|
+
}
|
|
209
|
+
} catch(err) {
|
|
210
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
211
|
+
failed++;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Summary
|
|
215
|
+
console.log("\n╔════════════════════════════════════════════════════════════════╗");
|
|
216
|
+
console.log("║ Test Summary ║");
|
|
217
|
+
console.log("╚════════════════════════════════════════════════════════════════╝");
|
|
218
|
+
console.log(`\n ✓ Passed: ${passed}`);
|
|
219
|
+
console.log(` ✗ Failed: ${failed}`);
|
|
220
|
+
console.log(` 📊 Total: ${passed + failed}\n`);
|
|
221
|
+
|
|
222
|
+
if(failed === 0) {
|
|
223
|
+
console.log(" 🎉 All tests passed!\n");
|
|
224
|
+
console.log(" ✅ Cache is properly shared across context instances");
|
|
225
|
+
console.log(" ✅ Bug fix verified: Multi-context cache invalidation works\n");
|
|
226
|
+
process.exit(0);
|
|
227
|
+
} else {
|
|
228
|
+
console.log(" ❌ Some tests failed\n");
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Static Cache Sharing
|
|
3
|
+
* Verifies the cache is static/shared as designed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const QueryCache = require('../Cache/QueryCache');
|
|
7
|
+
|
|
8
|
+
console.log("╔════════════════════════════════════════════════════════════════╗");
|
|
9
|
+
console.log("║ Static Cache Test ║");
|
|
10
|
+
console.log("╚════════════════════════════════════════════════════════════════╝\n");
|
|
11
|
+
|
|
12
|
+
let passed = 0;
|
|
13
|
+
let failed = 0;
|
|
14
|
+
|
|
15
|
+
// Simulate context class with static shared cache
|
|
16
|
+
class SimulatedContext {
|
|
17
|
+
static _sharedQueryCache = null;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
// Initialize shared query cache (only once across all instances)
|
|
21
|
+
if (!SimulatedContext._sharedQueryCache) {
|
|
22
|
+
SimulatedContext._sharedQueryCache = new QueryCache({
|
|
23
|
+
ttl: 5 * 60 * 1000,
|
|
24
|
+
maxSize: 1000,
|
|
25
|
+
enabled: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Reference the shared cache
|
|
30
|
+
this._queryCache = SimulatedContext._sharedQueryCache;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getCacheStats() {
|
|
34
|
+
return this._queryCache.getStats();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clearQueryCache() {
|
|
38
|
+
this._queryCache.clear();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Test 1: Multiple instances share the same cache
|
|
43
|
+
console.log("📝 Test 1: Multiple instances share same cache");
|
|
44
|
+
console.log("──────────────────────────────────────────────────");
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const ctx1 = new SimulatedContext();
|
|
48
|
+
const ctx2 = new SimulatedContext();
|
|
49
|
+
|
|
50
|
+
const areSame = ctx1._queryCache === ctx2._queryCache;
|
|
51
|
+
const isStatic = ctx1._queryCache === SimulatedContext._sharedQueryCache;
|
|
52
|
+
|
|
53
|
+
if(areSame && isStatic) {
|
|
54
|
+
console.log(" ✓ Both instances share the same cache");
|
|
55
|
+
console.log(" ✓ Cache is stored in static property");
|
|
56
|
+
passed++;
|
|
57
|
+
} else {
|
|
58
|
+
console.log(` ✗ Instances have separate caches`);
|
|
59
|
+
failed++;
|
|
60
|
+
}
|
|
61
|
+
} catch(err) {
|
|
62
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
63
|
+
failed++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Test 2: Cache SET in one instance visible in another
|
|
67
|
+
console.log("\n📝 Test 2: Cache operations are shared");
|
|
68
|
+
console.log("──────────────────────────────────────────────────");
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const ctx1 = new SimulatedContext();
|
|
72
|
+
const ctx2 = new SimulatedContext();
|
|
73
|
+
|
|
74
|
+
// Instance 1: Add to cache
|
|
75
|
+
const key = ctx1._queryCache.generateKey('SELECT * FROM users', [], 'users');
|
|
76
|
+
ctx1._queryCache.set(key, [{ id: 1, name: 'Alice' }], 'users');
|
|
77
|
+
|
|
78
|
+
// Instance 2: Should see the same cached data
|
|
79
|
+
const cached = ctx2._queryCache.get(key);
|
|
80
|
+
|
|
81
|
+
if(cached && cached[0].name === 'Alice') {
|
|
82
|
+
console.log(" ✓ Cache data visible across instances");
|
|
83
|
+
console.log(" ✓ Data written by ctx1, read by ctx2");
|
|
84
|
+
passed++;
|
|
85
|
+
} else {
|
|
86
|
+
console.log(` ✗ Cache data not shared`);
|
|
87
|
+
failed++;
|
|
88
|
+
}
|
|
89
|
+
} catch(err) {
|
|
90
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
91
|
+
failed++;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Test 3: Cache invalidation in one instance affects another
|
|
95
|
+
console.log("\n📝 Test 3: Cache invalidation is shared");
|
|
96
|
+
console.log("──────────────────────────────────────────────────");
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const ctx1 = new SimulatedContext();
|
|
100
|
+
const ctx2 = new SimulatedContext();
|
|
101
|
+
|
|
102
|
+
// Clear for clean test
|
|
103
|
+
ctx1._queryCache.clear();
|
|
104
|
+
|
|
105
|
+
// Instance 1: Add entries
|
|
106
|
+
const key1 = ctx1._queryCache.generateKey('query1', [], 'users');
|
|
107
|
+
const key2 = ctx1._queryCache.generateKey('query2', [], 'users');
|
|
108
|
+
ctx1._queryCache.set(key1, { id: 1 }, 'users');
|
|
109
|
+
ctx1._queryCache.set(key2, { id: 2 }, 'users');
|
|
110
|
+
|
|
111
|
+
// Verify both cached
|
|
112
|
+
const before1 = ctx1._queryCache.get(key1);
|
|
113
|
+
const before2 = ctx2._queryCache.get(key2);
|
|
114
|
+
|
|
115
|
+
// Instance 2: Invalidate
|
|
116
|
+
ctx2._queryCache.invalidateTable('users');
|
|
117
|
+
|
|
118
|
+
// Both instances should see empty cache
|
|
119
|
+
const after1 = ctx1._queryCache.get(key1);
|
|
120
|
+
const after2 = ctx2._queryCache.get(key2);
|
|
121
|
+
|
|
122
|
+
if(before1 !== null && before2 !== null && after1 === null && after2 === null) {
|
|
123
|
+
console.log(" ✓ Invalidation in ctx2 affected ctx1");
|
|
124
|
+
console.log(" ✓ Cache properly shared");
|
|
125
|
+
passed++;
|
|
126
|
+
} else {
|
|
127
|
+
console.log(` ✗ Invalidation not shared`);
|
|
128
|
+
failed++;
|
|
129
|
+
}
|
|
130
|
+
} catch(err) {
|
|
131
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
132
|
+
failed++;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Test 4: Statistics are shared
|
|
136
|
+
console.log("\n📝 Test 4: Statistics are shared");
|
|
137
|
+
console.log("──────────────────────────────────────────────────");
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const ctx1 = new SimulatedContext();
|
|
141
|
+
const ctx2 = new SimulatedContext();
|
|
142
|
+
|
|
143
|
+
// Clear for clean test
|
|
144
|
+
ctx1.clearQueryCache();
|
|
145
|
+
|
|
146
|
+
// Instance 1: Generate activity
|
|
147
|
+
const key = ctx1._queryCache.generateKey('test', [], 'users');
|
|
148
|
+
ctx1._queryCache.set(key, 'data', 'users');
|
|
149
|
+
ctx1._queryCache.get(key); // Hit
|
|
150
|
+
ctx1._queryCache.get('nonexistent'); // Miss
|
|
151
|
+
|
|
152
|
+
// Both instances should see same stats
|
|
153
|
+
const stats1 = ctx1.getCacheStats();
|
|
154
|
+
const stats2 = ctx2.getCacheStats();
|
|
155
|
+
|
|
156
|
+
if(stats1.hits === 1 && stats2.hits === 1 && stats1.misses === 1 && stats2.misses === 1) {
|
|
157
|
+
console.log(" ✓ Statistics shared across instances");
|
|
158
|
+
console.log(` ✓ Both see: ${stats1.hits} hit, ${stats1.misses} miss`);
|
|
159
|
+
passed++;
|
|
160
|
+
} else {
|
|
161
|
+
console.log(` ✗ Statistics not shared`);
|
|
162
|
+
failed++;
|
|
163
|
+
}
|
|
164
|
+
} catch(err) {
|
|
165
|
+
console.log(` ✗ Error: ${err.message}`);
|
|
166
|
+
failed++;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Summary
|
|
170
|
+
console.log("\n╔════════════════════════════════════════════════════════════════╗");
|
|
171
|
+
console.log("║ Test Summary ║");
|
|
172
|
+
console.log("╚════════════════════════════════════════════════════════════════╝");
|
|
173
|
+
console.log(`\n ✓ Passed: ${passed}`);
|
|
174
|
+
console.log(` ✗ Failed: ${failed}`);
|
|
175
|
+
console.log(` 📊 Total: ${passed + failed}\n`);
|
|
176
|
+
|
|
177
|
+
if(failed === 0) {
|
|
178
|
+
console.log(" 🎉 All tests passed!\n");
|
|
179
|
+
console.log(" ✅ Static cache pattern verified");
|
|
180
|
+
console.log(" ✅ BUG FIX CONFIRMED: Multi-context cache sharing works!\n");
|
|
181
|
+
process.exit(0);
|
|
182
|
+
} else {
|
|
183
|
+
console.log(" ❌ Some tests failed\n");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|