From c5cb8c1f012146febeea926101672b310ab28aee Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Mon, 3 Feb 2025 00:25:05 +0100 Subject: [PATCH] fix(updateManager): Refine cache management for service worker updates. --- changelog.md | 7 +++ ts/00_commitinfo_data.ts | 2 +- ts_web_serviceworker/classes.updatemanager.ts | 49 ++++++++++++++++--- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 1edd048..8530b89 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 7d978fe..ba58134 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -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.' } diff --git a/ts_web_serviceworker/classes.updatemanager.ts b/ts_web_serviceworker/classes.updatemanager.ts index 5afa993..39f6ed9 100644 --- a/ts_web_serviceworker/classes.updatemanager.ts +++ b/ts_web_serviceworker/classes.updatemanager.ts @@ -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 { 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 () => {