redis caching updated optimized loading and data readiness

This commit is contained in:
2025-12-28 06:19:55 +00:00
parent dd0eba8aee
commit ccdfeb782d
+86 -36
View File
@@ -81,57 +81,36 @@ app.get('/api/jobs', async (c) => {
// Fast endpoint: returns all jobs in a single cached request // Fast endpoint: returns all jobs in a single cached request
app.get('/api/jobs-all', async (c) => { app.get('/api/jobs-all', async (c) => {
const cacheKey = 'jobs:all'; const cacheKey = 'jobs:all';
const ttl = 300; // 5 minutes cache const ttl = 1800; // 30 minutes cache
try { try {
// Try to get from cache first // Always try cache first for instant response
if (isRedisConnected()) { if (isRedisConnected()) {
const cached = await getCache(cacheKey); const cached = await getCache(cacheKey);
if (cached) { if (cached) {
logLine('logs/server.log', `Cache HIT for ${cacheKey} (${cached.items?.length || 0} jobs)`); logLine('logs/server.log', `Cache HIT for ${cacheKey} (${cached.items?.length || 0} jobs)`);
// Return cached data immediately, refresh in background if needed
setImmediate(async () => {
try {
await refreshJobsCache(cacheKey, ttl, c.req.header('Authorization') || '');
} catch (err) {
// Silently fail background refresh
}
});
return c.json(cached); return c.json(cached);
} }
logLine('logs/server.log', `Cache MISS for ${cacheKey} - fetching all jobs`); logLine('logs/server.log', `Cache MISS for ${cacheKey} - fetching all jobs`);
} }
const authHeader = c.req.header('Authorization') || ''; // No cache available, fetch now
const allItems: any[] = []; const result = await fetchAllJobsFromPocketBase(c.req.header('Authorization') || '');
let page = 1;
const perPage = 500; // Large page size
// Fetch all pages from PocketBase
while (true) {
const pbUrl = `https://pocketbase.ccllc.pro/api/collections/pbc_1407612689/records?page=${page}&perPage=${perPage}&sort=-Job_Number`;
const response = await fetch(pbUrl, {
headers: authHeader ? { Authorization: authHeader } : {}
});
if (!response.ok) {
throw new Error(`PocketBase returned ${response.status}`);
}
const data = await response.json();
const items = data.items || [];
if (items.length === 0) break;
allItems.push(...items);
if (allItems.length >= (data.totalItems || 0)) break;
page++;
}
const result = {
page: 1,
perPage: allItems.length,
totalItems: allItems.length,
totalPages: 1,
items: allItems
};
// Cache the result // Cache the result
if (isRedisConnected()) { if (isRedisConnected()) {
await setCache(cacheKey, result, ttl); await setCache(cacheKey, result, ttl);
logLine('logs/server.log', `Cached ${cacheKey} with ${allItems.length} jobs for ${ttl}s`); logLine('logs/server.log', `Cached ${cacheKey} with ${result.items.length} jobs for ${ttl}s`);
} }
return c.json(result); return c.json(result);
@@ -142,6 +121,52 @@ app.get('/api/jobs-all', async (c) => {
} }
}); });
// Helper function to fetch all jobs from PocketBase
async function fetchAllJobsFromPocketBase(authHeader: string): Promise<any> {
const allItems: any[] = [];
let page = 1;
const perPage = 500;
while (true) {
const pbUrl = `https://pocketbase.ccllc.pro/api/collections/pbc_1407612689/records?page=${page}&perPage=${perPage}&sort=-Job_Number`;
const response = await fetch(pbUrl, {
headers: authHeader ? { Authorization: authHeader } : {}
});
if (!response.ok) {
throw new Error(`PocketBase returned ${response.status}`);
}
const data = await response.json();
const items = data.items || [];
if (items.length === 0) break;
allItems.push(...items);
if (allItems.length >= (data.totalItems || 0)) break;
page++;
}
return {
page: 1,
perPage: allItems.length,
totalItems: allItems.length,
totalPages: 1,
items: allItems
};
}
// Helper function to refresh jobs cache in background
async function refreshJobsCache(cacheKey: string, ttl: number, authHeader: string): Promise<void> {
try {
const result = await fetchAllJobsFromPocketBase(authHeader);
await setCache(cacheKey, result, ttl);
logLine('logs/server.log', `Background refresh: cached ${cacheKey} with ${result.items.length} jobs`);
} catch (err) {
logLine('logs/error.log', `Background refresh error: ${(err as Error)?.message || String(err)}`);
}
}
// --- Microsoft Graph helpers --- // --- Microsoft Graph helpers ---
const getGraphToken = (c?: any) => { const getGraphToken = (c?: any) => {
@@ -448,6 +473,31 @@ const PORT = Number(process.env.PORT || 3004);
logLine('logs/server.log', `Starting frontend server (Redis version) on port ${PORT}`); logLine('logs/server.log', `Starting frontend server (Redis version) on port ${PORT}`);
// Warm up cache on startup
(async () => {
try {
if (isRedisConnected()) {
logLine('logs/server.log', 'Warming up jobs cache...');
const result = await fetchAllJobsFromPocketBase('');
await setCache('jobs:all', result, 1800);
logLine('logs/server.log', `✓ Cache warmed with ${result.items.length} jobs`);
}
} catch (err) {
logLine('logs/error.log', `Cache warmup failed: ${(err as Error)?.message || String(err)}`);
}
})();
// Background refresh every 5 minutes to keep cache hot
setInterval(async () => {
try {
if (isRedisConnected()) {
await refreshJobsCache('jobs:all', 1800, '');
}
} catch (err) {
logLine('logs/error.log', `Periodic refresh error: ${(err as Error)?.message || String(err)}`);
}
}, 5 * 60 * 1000); // Every 5 minutes
// Graceful shutdown logging // Graceful shutdown logging
const shutdown = (signal: string) => { const shutdown = (signal: string) => {
logLine('logs/server.log', `Shutting down due to ${signal}`); logLine('logs/server.log', `Shutting down due to ${signal}`);