Ultra-fast jobs display: backend returns first 40 jobs instantly, frontend renders immediately, full set updates when ready
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// Express API for Redis/Valkey: keys list and single key fetch (for fast UI)
|
||||
const express = require('express');
|
||||
const Redis = require('ioredis');
|
||||
const app = express();
|
||||
const redis = new Redis({
|
||||
host: process.env.REDIS_HOST || '127.0.0.1',
|
||||
port: Number(process.env.REDIS_PORT || 6379),
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
});
|
||||
|
||||
// List all keys (no values)
|
||||
app.get('/api/cache/keys', async (req, res) => {
|
||||
try {
|
||||
const keys = await redis.keys('*');
|
||||
// Optionally, add summary/metadata here
|
||||
res.json(keys.map(key => ({ key })));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch value for a single key
|
||||
app.get('/api/cache/:key', async (req, res) => {
|
||||
try {
|
||||
const key = req.params.key;
|
||||
let value = await redis.get(key);
|
||||
try { value = JSON.parse(value); } catch {}
|
||||
res.json({ key, value });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
const port = process.env.CACHE_API_PORT || 3006;
|
||||
app.listen(port, () => {
|
||||
console.log(`Cache API (keys+single) running on http://localhost:${port}/api/cache/keys`);
|
||||
});
|
||||
+19
-18
@@ -82,39 +82,40 @@ app.get('/api/jobs', async (c) => {
|
||||
app.get('/api/jobs-all', async (c) => {
|
||||
const cacheKey = 'jobs:all';
|
||||
const ttl = 1800; // 30 minutes cache
|
||||
|
||||
try {
|
||||
// Always try cache first for instant response
|
||||
// Try cache first for instant response
|
||||
if (isRedisConnected()) {
|
||||
const cached = await getCache(cacheKey);
|
||||
if (cached) {
|
||||
logLine('logs/server.log', `Cache HIT for ${cacheKey} (${cached.items?.length || 0} jobs)`);
|
||||
|
||||
// Return cached data immediately, refresh in background if needed
|
||||
// Return cached data immediately
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
await refreshJobsCache(cacheKey, ttl, c.req.header('Authorization') || '');
|
||||
} catch (err) {}
|
||||
});
|
||||
// For ultra-fast display, send only the first 40 jobs in the initial response
|
||||
const fastBatch = Array.isArray(cached.items) ? cached.items.slice(0, 40) : [];
|
||||
return c.json({ ...cached, items: fastBatch, partial: cached.items && cached.items.length > 40 });
|
||||
} else {
|
||||
// No cache: return empty array instantly, trigger background fetch
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
const result = await fetchAllJobsFromPocketBase(c.req.header('Authorization') || '');
|
||||
if (isRedisConnected()) {
|
||||
await setCache(cacheKey, result, ttl);
|
||||
logLine('logs/server.log', `Cached ${cacheKey} with ${result.items.length} jobs for ${ttl}s`);
|
||||
}
|
||||
} catch (err) {
|
||||
// Silently fail background refresh
|
||||
logLine('logs/error.log', `jobs-all background fetch error: ${(err as Error)?.message || String(err)}`);
|
||||
}
|
||||
});
|
||||
|
||||
return c.json(cached);
|
||||
return c.json({ items: [], partial: true });
|
||||
}
|
||||
logLine('logs/server.log', `Cache MISS for ${cacheKey} - fetching all jobs`);
|
||||
}
|
||||
|
||||
// No cache available, fetch now
|
||||
// Redis not connected: fallback to direct fetch (blocking)
|
||||
const result = await fetchAllJobsFromPocketBase(c.req.header('Authorization') || '');
|
||||
|
||||
// Cache the result
|
||||
if (isRedisConnected()) {
|
||||
await setCache(cacheKey, result, ttl);
|
||||
logLine('logs/server.log', `Cached ${cacheKey} with ${result.items.length} jobs for ${ttl}s`);
|
||||
}
|
||||
|
||||
return c.json(result);
|
||||
|
||||
} catch (err) {
|
||||
logLine('logs/error.log', `jobs-all endpoint error: ${(err as Error)?.message || String(err)}`);
|
||||
return c.json({ error: 'Failed to fetch jobs' }, 500);
|
||||
|
||||
Reference in New Issue
Block a user