redis caching updated optimized loading and data readiness
This commit is contained in:
+86
-36
@@ -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}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user