Skip to content

Commit c1d5e70

Browse files
committed
feat(cache): Implement cache management UI with statistics and clear options for various cache types
1 parent f7de4dc commit c1d5e70

4 files changed

Lines changed: 809 additions & 9 deletions

File tree

src/lib/cache.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ export class ProductCache {
114114
Cache.invalidatePattern('^products:');
115115
Cache.invalidatePattern('^product:');
116116
}
117+
118+
static clear(): void {
119+
Cache.invalidatePattern('^products:');
120+
Cache.invalidatePattern('^product:');
121+
}
117122
}
118123

119124
export class CheckoutCache {
@@ -148,6 +153,11 @@ export class CheckoutCache {
148153
static invalidateAllCheckouts(): void {
149154
Cache.invalidatePattern('^checkout:');
150155
}
156+
157+
static clear(): void {
158+
Cache.invalidatePattern('^checkout:');
159+
Cache.invalidatePattern('^shipping_methods');
160+
}
151161
}
152162

153163
export class OrderCache {
@@ -167,6 +177,10 @@ export class OrderCache {
167177
const key = `order:${id}`;
168178
Cache.invalidate(key);
169179
}
180+
181+
static clear(): void {
182+
Cache.invalidatePattern('^order:');
183+
}
170184
}
171185

172186
// Cache statistics and debugging

src/lib/server/commercify/cache.ts

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ interface ServerCacheEntry<T> {
1111
class ServerCache {
1212
private cache = new Map<string, ServerCacheEntry<any>>();
1313
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
14+
private hits = 0;
15+
private misses = 0;
1416

1517
constructor() {
1618
// Start cleanup interval in production
@@ -50,15 +52,18 @@ class ServerCache {
5052
const entry = this.cache.get(key);
5153

5254
if (!entry) {
55+
this.misses++;
5356
return null;
5457
}
5558

5659
// Check if expired
5760
if (Date.now() - entry.timestamp > entry.ttl) {
5861
this.cache.delete(key);
62+
this.misses++;
5963
return null;
6064
}
6165

66+
this.hits++;
6267
return entry.data;
6368
}
6469

@@ -77,24 +82,69 @@ class ServerCache {
7782

7883
clear(): void {
7984
this.cache.clear();
85+
this.hits = 0;
86+
this.misses = 0;
8087
}
8188

8289
size(): number {
8390
return this.cache.size;
8491
}
8592

86-
getStats(): { size: number; keys: string[] } {
93+
getStats(): { size: number; keys: string[]; hits: number; misses: number; hitRate: number } {
94+
const total = this.hits + this.misses;
95+
const hitRate = total > 0 ? (this.hits / total) * 100 : 0;
96+
8797
return {
8898
size: this.cache.size,
89-
keys: Array.from(this.cache.keys())
99+
keys: Array.from(this.cache.keys()),
100+
hits: this.hits,
101+
misses: this.misses,
102+
hitRate: Math.round(hitRate * 100) / 100
90103
};
91104
}
92105

93-
destroy(): void {
94-
if (this.cleanupInterval) {
95-
clearInterval(this.cleanupInterval);
96-
}
97-
this.clear();
106+
getCacheCounts(): Record<string, number> {
107+
const keys = Array.from(this.cache.keys());
108+
const counts = {
109+
product: 0,
110+
category: 0,
111+
order: 0,
112+
checkout: 0,
113+
discount: 0,
114+
currency: 0,
115+
shipping: 0,
116+
user: 0,
117+
other: 0
118+
};
119+
120+
keys.forEach((key) => {
121+
if (key.startsWith('product')) {
122+
counts.product++;
123+
} else if (key.startsWith('categor')) {
124+
counts.category++;
125+
} else if (key.startsWith('order')) {
126+
counts.order++;
127+
} else if (key.startsWith('checkout')) {
128+
counts.checkout++;
129+
} else if (key.startsWith('discount')) {
130+
counts.discount++;
131+
} else if (key.startsWith('currenc')) {
132+
counts.currency++;
133+
} else if (key.startsWith('shipping')) {
134+
counts.shipping++;
135+
} else if (key.startsWith('user')) {
136+
counts.user++;
137+
} else {
138+
counts.other++;
139+
}
140+
});
141+
142+
return counts;
143+
}
144+
145+
resetStats(): void {
146+
this.hits = 0;
147+
this.misses = 0;
98148
}
99149
}
100150

@@ -163,8 +213,21 @@ export class CheckoutSessionCache {
163213
}
164214

165215
// Memory usage monitoring
166-
export function getCacheStats(): { size: number; keys: string[] } {
167-
return serverCache.getStats();
216+
export function getCacheStats(): {
217+
size: number;
218+
keys: string[];
219+
hits: number;
220+
misses: number;
221+
hitRate: number;
222+
counts: Record<string, number>;
223+
} {
224+
const stats = serverCache.getStats();
225+
const counts = serverCache.getCacheCounts();
226+
227+
return {
228+
...stats,
229+
counts
230+
};
168231
}
169232

170233
// ==========================================
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import type { PageServerLoad, Actions } from './$types';
2+
import { Cache, ProductCache, OrderCache, CheckoutCache } from '$lib/cache';
3+
import { serverCache, getCacheStats } from '$lib/server/commercify/cache';
4+
import { fail } from '@sveltejs/kit';
5+
6+
export const load: PageServerLoad = async ({ locals }) => {
7+
// Get cache statistics from the server cache (which is actually used)
8+
const stats = getCacheStats();
9+
10+
return {
11+
cacheStats: {
12+
hits: stats.hits,
13+
misses: stats.misses,
14+
hitRate: stats.hitRate,
15+
size: stats.size,
16+
counts: stats.counts
17+
}
18+
};
19+
};
20+
21+
export const actions: Actions = {
22+
clearCache: async ({ locals }) => {
23+
try {
24+
// Clear both client-side and server-side cache
25+
Cache.clear();
26+
serverCache.clear();
27+
28+
return {
29+
success: true,
30+
message: 'All cache cleared successfully'
31+
};
32+
} catch (error) {
33+
console.error('Failed to clear cache:', error);
34+
return fail(500, {
35+
success: false,
36+
message: 'Failed to clear cache'
37+
});
38+
}
39+
},
40+
41+
clearProductCache: async ({ locals }) => {
42+
try {
43+
// Clear only product-related cache
44+
ProductCache.clear();
45+
serverCache.invalidatePattern('^product');
46+
serverCache.invalidatePattern('^products:');
47+
48+
return {
49+
success: true,
50+
message: 'Product cache cleared successfully'
51+
};
52+
} catch (error) {
53+
console.error('Failed to clear product cache:', error);
54+
return fail(500, {
55+
success: false,
56+
message: 'Failed to clear product cache'
57+
});
58+
}
59+
},
60+
61+
clearOrderCache: async ({ locals }) => {
62+
try {
63+
// Clear only order-related cache
64+
OrderCache.clear();
65+
serverCache.invalidatePattern('^order');
66+
67+
return {
68+
success: true,
69+
message: 'Order cache cleared successfully'
70+
};
71+
} catch (error) {
72+
console.error('Failed to clear order cache:', error);
73+
return fail(500, {
74+
success: false,
75+
message: 'Failed to clear order cache'
76+
});
77+
}
78+
},
79+
80+
clearCheckoutCache: async ({ locals }) => {
81+
try {
82+
// Clear only checkout-related cache
83+
CheckoutCache.clear();
84+
serverCache.invalidatePattern('^checkout');
85+
86+
return {
87+
success: true,
88+
message: 'Checkout cache cleared successfully'
89+
};
90+
} catch (error) {
91+
console.error('Failed to clear checkout cache:', error);
92+
return fail(500, {
93+
success: false,
94+
message: 'Failed to clear checkout cache'
95+
});
96+
}
97+
},
98+
99+
clearCategoryCache: async ({ locals }) => {
100+
try {
101+
// Clear category-related cache
102+
serverCache.invalidatePattern('^category');
103+
serverCache.invalidatePattern('^categories:');
104+
105+
return {
106+
success: true,
107+
message: 'Category cache cleared successfully'
108+
};
109+
} catch (error) {
110+
console.error('Failed to clear category cache:', error);
111+
return fail(500, {
112+
success: false,
113+
message: 'Failed to clear category cache'
114+
});
115+
}
116+
},
117+
118+
clearDiscountCache: async ({ locals }) => {
119+
try {
120+
// Clear discount-related cache
121+
serverCache.invalidatePattern('^discount');
122+
123+
return {
124+
success: true,
125+
message: 'Discount cache cleared successfully'
126+
};
127+
} catch (error) {
128+
console.error('Failed to clear discount cache:', error);
129+
return fail(500, {
130+
success: false,
131+
message: 'Failed to clear discount cache'
132+
});
133+
}
134+
},
135+
136+
clearCurrencyCache: async ({ locals }) => {
137+
try {
138+
// Clear currency-related cache
139+
serverCache.invalidatePattern('^currenc');
140+
141+
return {
142+
success: true,
143+
message: 'Currency cache cleared successfully'
144+
};
145+
} catch (error) {
146+
console.error('Failed to clear currency cache:', error);
147+
return fail(500, {
148+
success: false,
149+
message: 'Failed to clear currency cache'
150+
});
151+
}
152+
},
153+
154+
clearShippingCache: async ({ locals }) => {
155+
try {
156+
// Clear shipping-related cache
157+
serverCache.invalidatePattern('^shipping');
158+
159+
return {
160+
success: true,
161+
message: 'Shipping cache cleared successfully'
162+
};
163+
} catch (error) {
164+
console.error('Failed to clear shipping cache:', error);
165+
return fail(500, {
166+
success: false,
167+
message: 'Failed to clear shipping cache'
168+
});
169+
}
170+
},
171+
172+
clearUserCache: async ({ locals }) => {
173+
try {
174+
// Clear user session and profile cache
175+
serverCache.invalidatePattern('^user');
176+
177+
return {
178+
success: true,
179+
message: 'User cache cleared successfully'
180+
};
181+
} catch (error) {
182+
console.error('Failed to clear user cache:', error);
183+
return fail(500, {
184+
success: false,
185+
message: 'Failed to clear user cache'
186+
});
187+
}
188+
},
189+
190+
resetStats: async ({ locals }) => {
191+
try {
192+
// Reset cache statistics without clearing the cache
193+
serverCache.resetStats();
194+
195+
return {
196+
success: true,
197+
message: 'Cache statistics reset successfully'
198+
};
199+
} catch (error) {
200+
console.error('Failed to reset cache statistics:', error);
201+
return fail(500, {
202+
success: false,
203+
message: 'Failed to reset cache statistics'
204+
});
205+
}
206+
},
207+
208+
exportCacheInfo: async ({ locals }) => {
209+
try {
210+
// Get detailed cache information
211+
const stats = getCacheStats();
212+
213+
return {
214+
success: true,
215+
message: 'Cache information exported successfully',
216+
cacheInfo: {
217+
...stats,
218+
exportedAt: new Date().toISOString()
219+
}
220+
};
221+
} catch (error) {
222+
console.error('Failed to export cache information:', error);
223+
return fail(500, {
224+
success: false,
225+
message: 'Failed to export cache information'
226+
});
227+
}
228+
}
229+
};

0 commit comments

Comments
 (0)