From ccd95a51f494a1e77070e500b482946e6ad72420 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 17 Aug 2026 16:24:32 -0400 Subject: [PATCH] fix: stop retrying legacy page-view migration on every page load When the migration write fails, the legacy key was retained "for retry", causing PAGE_VIEW_CAPTURE_FAILED to fire on every subsequent page load for affected users. Fix by using writeNamespacedFieldWithinBudget (which evicts oldest records before giving up) and always removing the legacy key so the retry loop can't fire again. --- src/pageViewStorage.ts | 8 +++++--- test/src/pageViewStorage.spec.ts | 30 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/pageViewStorage.ts b/src/pageViewStorage.ts index c06a8a3..35297a2 100644 --- a/src/pageViewStorage.ts +++ b/src/pageViewStorage.ts @@ -27,14 +27,16 @@ export function migrateLegacyPageViewStorage(loggingService: LoggingService | nu const needsMigration = !alreadyMigrated && Array.isArray(legacyViews); if (needsMigration) { + loggingService?.log({ + message: 'Rokt Kit: Migrating legacy page-view storage', + code: 'PAGE_VIEW_LEGACY_MIGRATION', + }); const migrated = writeNamespacedField(LS_NAMESPACE_KEY, LS_PAGE_VIEWS_FIELD, legacyViews); if (!migrated) { loggingService?.log({ - message: - 'Rokt Kit: Failed to migrate legacy page-view storage; retaining legacy key for retry [reason: migration_retry]', + message: 'Rokt Kit: Failed to migrate legacy page-view storage [reason: migration_retry]', code: 'PAGE_VIEW_CAPTURE_FAILED', }); - return; } } diff --git a/test/src/pageViewStorage.spec.ts b/test/src/pageViewStorage.spec.ts index 325eb48..e1a0574 100644 --- a/test/src/pageViewStorage.spec.ts +++ b/test/src/pageViewStorage.spec.ts @@ -49,7 +49,16 @@ describe('pageViewStorage', () => { expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); }); - it('retains the legacy key and logs when the migrating write fails', () => { + it('logs PAGE_VIEW_LEGACY_MIGRATION when the migration path is taken', () => { + window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); + const logger = { log: vi.fn() } as unknown as LoggingService; + + migrateLegacyPageViewStorage(logger); + + expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({ code: 'PAGE_VIEW_LEGACY_MIGRATION' })); + }); + + it('removes the legacy key and logs when the migrating write fails', () => { window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); const logger = { log: vi.fn() } as unknown as LoggingService; vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { @@ -58,9 +67,26 @@ describe('pageViewStorage', () => { migrateLegacyPageViewStorage(logger); - expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).not.toBeNull(); + // Legacy key is removed even on failure — prevents the infinite retry loop. + expect(window.localStorage.getItem(LEGACY_PAGE_VIEWS_KEY)).toBeNull(); expect(logger.log).toHaveBeenCalledWith(expect.objectContaining({ code: 'PAGE_VIEW_CAPTURE_FAILED' })); }); + + it('does not log PAGE_VIEW_CAPTURE_FAILED on a second call after a failed migration', () => { + window.localStorage.setItem(LEGACY_PAGE_VIEWS_KEY, JSON.stringify([pageView('home')])); + const logger = { log: vi.fn() } as unknown as LoggingService; + vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { + throw new DOMException('quota', 'QuotaExceededError'); + }); + + migrateLegacyPageViewStorage(logger); // first call: fails, legacy key removed + vi.restoreAllMocks(); + logger.log.mockClear(); + + migrateLegacyPageViewStorage(logger); // second call: legacy key gone, no-op + + expect(logger.log).not.toHaveBeenCalled(); + }); }); describe('loadPageViews', () => {