fix(updateManager): Refine cache management for service worker updates.

This commit is contained in:
Philipp Kunz 2025-02-03 00:25:05 +01:00
parent 8202ce6227
commit c5cb8c1f01
3 changed files with 51 additions and 7 deletions

View File

@ -1,5 +1,12 @@
# Changelog
## 2025-02-03 - 3.0.57 - fix(updateManager)
Refine cache management for service worker updates.
- Ensured cache is forcibly updated if older than defined maximum age.
- Implemented interval checks and forced updates for cache staleness.
- Updated version information and cache timestamps upon forced updates or validations.
## 2025-02-03 - 3.0.56 - fix(cachemanager)
Adjust cache control headers and fix redundant code

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@api.global/typedserver',
version: '3.0.56',
version: '3.0.57',
description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.'
}

View File

@ -17,24 +17,42 @@ export class UpdateManager {
/**
* checks wether an update is needed
*/
private readonly MAX_CACHE_AGE = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
private readonly MIN_CHECK_INTERVAL = 100000; // 100 seconds in milliseconds
private lastCacheTimestamp: number = 0;
public async checkUpdate(cacheManager: CacheManager): Promise<boolean> {
const lswVersionInfoKey = 'versionInfo';
const cacheTimestampKey = 'cacheTimestamp';
// Initialize or load version info
if (!this.lastVersionInfo && !(await this.serviceworkerRef.store.check(lswVersionInfoKey))) {
this.lastVersionInfo = {
appHash: '',
appSemVer: 'v0.0.0',
};
} else if (
!this.lastVersionInfo &&
(await this.serviceworkerRef.store.check(lswVersionInfoKey))
) {
} else if (!this.lastVersionInfo && (await this.serviceworkerRef.store.check(lswVersionInfoKey))) {
this.lastVersionInfo = await this.serviceworkerRef.store.get(lswVersionInfoKey);
}
// Load or initialize cache timestamp
if (await this.serviceworkerRef.store.check(cacheTimestampKey)) {
this.lastCacheTimestamp = await this.serviceworkerRef.store.get(cacheTimestampKey);
}
const now = Date.now();
const millisSinceLastCheck = now - this.lastUpdateCheck;
if (millisSinceLastCheck < 100000) {
// TODO account for being offline
const cacheAge = now - this.lastCacheTimestamp;
// Force update if cache is too old
if (cacheAge > this.MAX_CACHE_AGE) {
logger.log('info', `Cache is older than ${this.MAX_CACHE_AGE}ms, forcing update...`);
await this.forceUpdate(cacheManager);
return true;
}
// Regular update check interval
if (millisSinceLastCheck < this.MIN_CHECK_INTERVAL && cacheAge < this.MAX_CACHE_AGE) {
return false;
}
logger.log('info', 'checking for update of the app by comparing app hashes...');
@ -49,9 +67,17 @@ export class UpdateManager {
this.performAsyncUpdateDebouncedTask.trigger();
this.lastVersionInfo = currentVersionInfo;
await this.serviceworkerRef.store.set(lswVersionInfoKey, this.lastVersionInfo);
// Update cache timestamp
this.lastCacheTimestamp = now;
await this.serviceworkerRef.store.set('cacheTimestamp', now);
} else {
logger.log('ok', 'caches are still valid, performing revalidation in a bit...');
this.performAsyncCacheRevalidationDebouncedTask.trigger();
// Update cache timestamp after successful revalidation
this.lastCacheTimestamp = now;
await this.serviceworkerRef.store.set('cacheTimestamp', now);
}
}
@ -70,6 +96,17 @@ export class UpdateManager {
/**
* this task is executed once we know that there is a new version available
*/
private async forceUpdate(cacheManager: CacheManager) {
logger.log('info', 'Forcing cache update due to staleness');
await this.serviceworkerRef.cacheManager.cleanCaches('Cache is stale, forcing update.');
const currentVersionInfo = await this.getVersionInfoFromServer();
this.lastVersionInfo = currentVersionInfo;
await this.serviceworkerRef.store.set('versionInfo', this.lastVersionInfo);
this.lastCacheTimestamp = Date.now();
await this.serviceworkerRef.store.set('cacheTimestamp', this.lastCacheTimestamp);
await this.serviceworkerRef.leleServiceWorkerBackend.triggerReloadAll();
}
public performAsyncUpdateDebouncedTask = new plugins.taskbuffer.TaskDebounced({
name: 'performAsyncUpdate',
taskFunction: async () => {