+101
-2
src/cache.ts
+101
-2
src/cache.ts
···
49
49
private analyticsCache: Map<string, { data: any; timestamp: number }> = new Map();
50
50
private analyticsCacheTTL = 30000; // 30 second cache for faster updates
51
51
52
+
// Background user update queue to avoid Slack API limits
53
+
private userUpdateQueue: Set<string> = new Set();
54
+
private isProcessingQueue = false;
55
+
private slackWrapper?: any; // Will be injected after construction
56
+
52
57
/**
53
58
* Creates a new Cache instance
54
59
* @param dbPath Path to SQLite database file
···
66
71
67
72
this.initDatabase();
68
73
this.setupPurgeSchedule();
69
-
74
+
this.startQueueProcessor();
75
+
70
76
// Run migrations
71
77
this.runMigrations();
72
78
}
···
322
328
}
323
329
324
330
/**
331
+
* Sets the Slack wrapper for user updates
332
+
* @param slackWrapper SlackWrapper instance for API calls
333
+
*/
334
+
setSlackWrapper(slackWrapper: any) {
335
+
this.slackWrapper = slackWrapper;
336
+
}
337
+
338
+
/**
339
+
* Adds a user to the background update queue
340
+
* @param userId User ID to queue for update
341
+
* @private
342
+
*/
343
+
private queueUserUpdate(userId: string) {
344
+
this.userUpdateQueue.add(userId.toUpperCase());
345
+
}
346
+
347
+
/**
348
+
* Starts the background queue processor
349
+
* @private
350
+
*/
351
+
private startQueueProcessor() {
352
+
// Process queue every 30 seconds to respect Slack API limits
353
+
setInterval(async () => {
354
+
await this.processUserUpdateQueue();
355
+
}, 30 * 1000);
356
+
}
357
+
358
+
/**
359
+
* Processes the user update queue with rate limiting
360
+
* @private
361
+
*/
362
+
private async processUserUpdateQueue() {
363
+
if (this.isProcessingQueue || this.userUpdateQueue.size === 0 || !this.slackWrapper) {
364
+
return;
365
+
}
366
+
367
+
this.isProcessingQueue = true;
368
+
369
+
try {
370
+
// Process up to 3 users at a time to respect API limits
371
+
const usersToUpdate = Array.from(this.userUpdateQueue).slice(0, 3);
372
+
373
+
for (const userId of usersToUpdate) {
374
+
try {
375
+
console.log(`Background updating user: ${userId}`);
376
+
const slackUser = await this.slackWrapper.getUserInfo(userId);
377
+
378
+
// Update user in cache with fresh data
379
+
await this.insertUser(
380
+
slackUser.id,
381
+
slackUser.real_name || slackUser.name || "Unknown",
382
+
slackUser.profile?.pronouns || "",
383
+
slackUser.profile?.image_512 || slackUser.profile?.image_192 || ""
384
+
);
385
+
386
+
// Remove from queue after successful update
387
+
this.userUpdateQueue.delete(userId);
388
+
} catch (error) {
389
+
console.warn(`Failed to update user ${userId}:`, error);
390
+
// Remove from queue even if failed to prevent infinite retry
391
+
this.userUpdateQueue.delete(userId);
392
+
}
393
+
}
394
+
} catch (error) {
395
+
console.error("Error processing user update queue:", error);
396
+
} finally {
397
+
this.isProcessingQueue = false;
398
+
}
399
+
}
400
+
401
+
/**
325
402
* Inserts a user into the cache
326
403
* @param userId Unique identifier for the user
327
404
* @param imageUrl URL of the user's image
···
480
557
return null;
481
558
}
482
559
483
-
if (new Date(result.expiration).getTime() < Date.now()) {
560
+
const now = Date.now();
561
+
const expiration = new Date(result.expiration).getTime();
562
+
563
+
// If user is expired, remove and return null
564
+
if (expiration < now) {
484
565
this.db.run("DELETE FROM users WHERE userId = ?", [userId]);
485
566
return null;
567
+
}
568
+
569
+
// Touch-to-refresh: if user is older than 24 hours, extend TTL and queue for background update
570
+
const twentyFourHoursAgo = now - (24 * 60 * 60 * 1000);
571
+
const userAge = expiration - (7 * 24 * 60 * 60 * 1000); // When user was originally cached
572
+
573
+
if (userAge < twentyFourHoursAgo) {
574
+
// Extend TTL by another 7 days from now
575
+
const newExpiration = now + (7 * 24 * 60 * 60 * 1000);
576
+
this.db.run("UPDATE users SET expiration = ? WHERE userId = ?", [
577
+
newExpiration,
578
+
userId.toUpperCase()
579
+
]);
580
+
581
+
// Queue for background update to get fresh data
582
+
this.queueUserUpdate(userId);
583
+
584
+
console.log(`Touch-refresh: Extended TTL for user ${userId} and queued for update`);
486
585
}
487
586
488
587
return {