Skip to content

fix: switch to in-memory storage for events when necessary#2494

Closed
saikumarrs wants to merge 6 commits into
developfrom
feature/sdk-3972-switch-to-in-memory-storage-for-storing-events-when
Closed

fix: switch to in-memory storage for events when necessary#2494
saikumarrs wants to merge 6 commits into
developfrom
feature/sdk-3972-switch-to-in-memory-storage-for-storing-events-when

Conversation

@saikumarrs

@saikumarrs saikumarrs commented Sep 23, 2025

Copy link
Copy Markdown
Member

PR Description

Switched to in-memory storage for events when local storage is unavailable. This is needed in restrictive environments like Safari v26 and above.

Linear task (optional)

https://linear.app/rudderstack/issue/SDK-3972/switch-to-in-memory-storage-for-storing-events-when-persistent-storage

Cross Browser Tests

Please confirm you have tested for the following browsers:

  • Chrome
  • Firefox
  • Safari

Sanity Suite

  • All sanity suite test cases pass locally

Security

  • The code changed/added as part of this pull request won't create any security issues with how the software is being used.

Summary by CodeRabbit

  • New Features

    • Queue entries now include an optional reclaimed flag for improved visibility of item state.
  • Bug Fixes

    • Analytics queue now falls back to in-memory storage when local storage isn’t available, improving reliability in restricted environments.
  • Chores

    • CI/CD workflows updated to supply NPM authentication and enable authenticated, verbose publishing steps.

@saikumarrs saikumarrs self-assigned this Sep 23, 2025
@saikumarrs saikumarrs requested a review from a team as a code owner September 23, 2025 19:52
Copilot AI review requested due to automatic review settings September 23, 2025 19:52
@coderabbitai

coderabbitai Bot commented Sep 23, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

📝 Walkthrough

XhrQueue now falls back to in-memory storage when localStorage is unavailable; queue item type gains an optional reclaimed?: boolean field and tests updated to assert it. CI workflows were updated to expose and use an NPM_TOKEN secret for authenticated npm publish/deprecate steps.

Changes

Cohort / File(s) Summary
Types & Tests
packages/analytics-js-plugins/src/types/plugins.ts, packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
Added optional reclaimed?: boolean to exported QueueItemData type; tests updated to expect reclaimed (often undefined) in queue item payloads.
XhrQueue runtime behavior
packages/analytics-js-plugins/src/xhrQueue/index.ts
Initialization now selects storage backend at runtime: use LOCAL_STORAGE if available, otherwise fall back to MEMORY_STORAGE. Comment documents fallback.
CI workflows (npm auth)
.github/workflows/deploy-beta.yml, .github/workflows/deploy-npm.yml, .github/workflows/publish-new-release.yml, .github/workflows/rollback.yml
Add NPM_TOKEN secret exposure and set up npm auth (npm set //registry.npmjs.org/:_authToken) and/or NODE_AUTH_TOKEN in publish/deprecate steps; enable authenticated npm publish/deprecation with verbose publish flag in some steps.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App
    participant XhrQueue
    participant StorageSelector
    participant LocalStorage as "LOCAL_STORAGE"
    participant MemoryStorage as "MEMORY_STORAGE"

    App->>XhrQueue: initialize()
    XhrQueue->>StorageSelector: isLocalStorageAvailable?
    alt localStorage available
        StorageSelector-->>LocalStorage: select
        XhrQueue-->>App: use LOCAL_STORAGE
    else fallback
        StorageSelector-->>MemoryStorage: select
        XhrQueue-->>App: use MEMORY_STORAGE
    end
    Note right of XhrQueue: queue items include optional\n`reclaimed?: boolean` in stored shape
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix: switch to in-memory storage for events when necessary" is concise and accurately describes the main change — falling back to in-memory storage when localStorage is unavailable (as implemented for event/XhrQueue storage); it is clear, focused, and appropriate for a single-line PR title.
Description Check ✅ Passed The PR description includes a clear summary, motivation, and the Linear task link and notes Chrome testing, but it diverges from the repository template by listing Safari instead of the template's IE11 entry and leaves Firefox, Sanity Suite, and Security checkboxes unchecked; the core change and rationale are present so the description is mostly complete but some required checklist items and template conformity are missing.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/analytics-js-plugins/src/xhrQueue/index.ts (1)

128-129: Good fallback; add a one-time log for observability when memory storage is used.

This helps support/debugging in environments without localStorage (SW, Safari ITP, third‑party iframes). Suggest logging once at init.

Apply this diff:

         storeManager,
-        // If local storage is available, use it, else use memory storage
-        state.capabilities.storage.isLocalStorageAvailable.value ? LOCAL_STORAGE : MEMORY_STORAGE,
+        // If local storage is available, use it, else use memory storage
+        (() => {
+          const storageType = state.capabilities.storage.isLocalStorageAvailable.value
+            ? LOCAL_STORAGE
+            : MEMORY_STORAGE;
+          if (storageType === MEMORY_STORAGE) {
+            logger?.info?.(
+              'XhrQueue: Using in-memory storage for queue (localStorage unavailable)',
+            );
+          }
+          return storageType;
+        })(),
         logger,
packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts (2)

216-216: Avoid asserting reclaimed: undefined in strict deep equals.

This is brittle and ties tests to an internal optional field being materialized as undefined. Prefer partial/object match.

Example change (pattern):

-    expect(queue.getStorageEntry('queue')).toStrictEqual([
-      {
-        item: { /* ... */ },
-        attemptNumber: 1,
-        lastAttemptedAt: expect.any(Number),
-        firstAttemptedAt: expect.any(Number),
-        reclaimed: undefined,
-        id: 'sample_uuid',
-        time: 1 + 1000 * 2 ** 1,
-        type: 'Single',
-        retryReason: 'server-429',
-      },
-    ]);
+    expect(queue.getStorageEntry('queue')).toMatchObject([
+      {
+        item: { /* ... */ },
+        attemptNumber: 1,
+        lastAttemptedAt: expect.any(Number),
+        firstAttemptedAt: expect.any(Number),
+        id: 'sample_uuid',
+        time: 1 + 1000 * 2 ** 1,
+        type: 'Single',
+        retryReason: 'server-429',
+      },
+    ]);

Repeat similarly for the other two assertions.

Also applies to: 395-395, 503-503


55-66: Add a unit test for the storage fallback path.

Set state.capabilities.storage.isLocalStorageAvailable.value = false and assert we log the in‑memory fallback during init.

You can add a test like:

it('falls back to memory storage when localStorage is unavailable', () => {
  batch(() => {
    state.capabilities.storage.isLocalStorageAvailable.value = false;
  });

  (XhrQueue()?.dataplaneEventsQueue as ExtensionPoint).init?.(
    state,
    defaultHttpClient,
    defaultStoreManager,
    undefined,
    defaultLogger,
  );

  expect(defaultLogger.info).toHaveBeenCalledWith(
    'XhrQueue: Using in-memory storage for queue (localStorage unavailable)',
  );
});

If defaultLogger.info isn’t available in the mock, switch to warn in both code and test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f62677 and f6e41e7.

📒 Files selected for processing (2)
  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts (3 hunks)
  • packages/analytics-js-plugins/src/xhrQueue/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/002-javascript-typescript.mdc)

Code must work in browsers AND service workers (no Node.js APIs)

Files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
  • packages/analytics-js-plugins/src/xhrQueue/index.ts
packages/*/__tests__/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place unit tests under packages/[package-name]/tests/

Files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
packages/*/__tests__/**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Jest for tests and MSW for HTTP mocking

Files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Adhere to the repository’s ESLint configuration for JavaScript/TypeScript code

Files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
  • packages/analytics-js-plugins/src/xhrQueue/index.ts
packages/*/src/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place all package source code under packages/[package-name]/src/

Files:

  • packages/analytics-js-plugins/src/xhrQueue/index.ts
packages/!(analytics-js-common)/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer shared TypeScript types from analytics-js-common across packages

Files:

  • packages/analytics-js-plugins/src/xhrQueue/index.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-10-08T15:52:59.819Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-06-14T09:50:33.511Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
📚 Learning: 2024-11-08T13:17:51.356Z
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1823
File: packages/analytics-js-common/src/utilities/retryQueue/utilities.ts:0-0
Timestamp: 2024-11-08T13:17:51.356Z
Learning: The issue regarding missing test coverage for the `findOtherQueues` function in `packages/analytics-js-common/src/utilities/retryQueue/utilities.ts` is no longer applicable.

Applied to files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
  • packages/analytics-js-plugins/src/xhrQueue/index.ts
📚 Learning: 2024-11-06T16:32:46.257Z
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1823
File: packages/analytics-js-plugins/src/xhrQueue/index.ts:34-34
Timestamp: 2024-11-06T16:32:46.257Z
Learning: The deprecated plugins have been removed from the `PluginName` type, so the explicit `PluginName` type annotation is no longer necessary in `packages/analytics-js-plugins/src/xhrQueue/index.ts`.

Applied to files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
  • packages/analytics-js-plugins/src/xhrQueue/index.ts
📚 Learning: 2024-10-08T15:52:59.819Z
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1708
File: packages/analytics-js-plugins/__tests__/deviceModeTransformation/index.test.ts:10-11
Timestamp: 2024-10-08T15:52:59.819Z
Learning: The misuse of `IHttpClient` in type assertions within the file `packages/analytics-js-plugins/__tests__/deviceModeTransformation/index.test.ts` has been corrected by the user.

Applied to files:

  • packages/analytics-js-plugins/__tests__/xhrQueue/index.test.ts
🧬 Code graph analysis (1)
packages/analytics-js-plugins/src/xhrQueue/index.ts (1)
packages/analytics-js-plugins/src/shared-chunks/common.ts (2)
  • LOCAL_STORAGE (3-3)
  • MEMORY_STORAGE (4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Bundle size checks
  • GitHub Check: Code quality checks
  • GitHub Check: Unit Tests and Lint
🔇 Additional comments (1)
packages/analytics-js-plugins/src/xhrQueue/index.ts (1)

34-35: Import looks correct.

Brings in MEMORY_STORAGE for the fallback; aligns with the new selection logic.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 23, 2025
@github-actions

github-actions Bot commented Sep 23, 2025

Copy link
Copy Markdown

size-limit report 📦

Name Size Delta Limit check
Core - Legacy - NPM (ESM) 48.27 KB 0 B (0% 🟢) 50 KB (✅)
Core - Legacy - NPM (CJS) 48.58 KB 0 B (0% 🟢) 50 KB (✅)
Core - Legacy - NPM (UMD) 48.23 KB 0 B (0% 🟢) 50 KB (✅)
Core - Legacy - CDN 48.37 KB 0 B (0% 🟢) 50 KB (✅)
Core - Modern - NPM (ESM) 27.77 KB 0 B (0% 🟢) 28 KB (✅)
Core - Modern - NPM (CJS) 28.05 KB 0 B (0% 🟢) 28.5 KB (✅)
Core - Modern - NPM (UMD) 27.82 KB 0 B (0% 🟢) 28 KB (✅)
Core - Modern - CDN 28.07 KB 0 B (0% 🟢) 28.5 KB (✅)
Core (Bundled) - Legacy - NPM (ESM) 48.27 KB 0 B (0% 🟢) 50 KB (✅)
Core (Bundled) - Legacy - NPM (CJS) 48.51 KB 0 B (0% 🟢) 50 KB (✅)
Core (Bundled) - Legacy - NPM (UMD) 48.23 KB 0 B (0% 🟢) 50 KB (✅)
Core (Bundled) - Modern - NPM (ESM) 40.9 KB 0 B (0% 🟢) 41 KB (✅)
Core (Bundled) - Modern - NPM (CJS) 41.18 KB 0 B (0% 🟢) 41.5 KB (✅)
Core (Bundled) - Modern - NPM (UMD) 40.94 KB 0 B (0% 🟢) 41 KB (✅)
Core (Content Script) - Legacy - NPM (ESM) 48.2 KB 0 B (0% 🟢) 50 KB (✅)
Core (Content Script) - Legacy - NPM (CJS) 48.51 KB 0 B (0% 🟢) 50 KB (✅)
Core (Content Script) - Legacy - NPM (UMD) 48.23 KB 0 B (0% 🟢) 50 KB (✅)
Core (Content Script) - Modern - NPM (ESM) 40.86 KB 0 B (0% 🟢) 41 KB (✅)
Core (Content Script) - Modern - NPM (CJS) 41.09 KB 0 B (0% 🟢) 41.5 KB (✅)
Core (Content Script) - Modern - NPM (UMD) 40.9 KB 0 B (0% 🟢) 41 KB (✅)
Load Snippet 780 B 0 B (0% 🟢) 1 KB (✅)
Plugins Module Federation Mapping - Legacy - CDN 330 B 0 B (0% 🟢) 512 B (✅)
Plugins Module Federation Mapping - Modern - CDN 330 B 0 B (0% 🟢) 512 B (✅)
Plugins - Legacy - CDN 13.55 KB 0 B (0% 🟢) 15 KB (✅)
Plugins - Modern - CDN 5.48 KB 0 B (0% 🟢) 6 KB (✅)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
.github/workflows/deploy-npm.yml (1)

164-164: Verbose publish is fine; double-check logs for secrets.

--verbose is helpful for debugging. Please verify nx/npm verbose output won’t echo tokens, npmrc, or other secrets. Optionally gate verbosity via an input (e.g., only for beta) to reduce noise.

.github/workflows/deploy-beta.yml (2)

85-86: Non-mergeable PR guard disabled.

Deploys may proceed even when PRs aren’t mergeable (conflicts). If this is temporary, consider adding a feature flag/input to re-enable the check without code edits.


105-106: ‘mergeable_state !== clean’ guard disabled.

This permits deploys with failing checks or missing approvals. If intended, consider logging the computed reason (blocked/dirty/unstable/unknown) at info level for auditability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6e41e7 and 4d0a617.

📒 Files selected for processing (2)
  • .github/workflows/deploy-beta.yml (5 hunks)
  • .github/workflows/deploy-npm.yml (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-10-08T15:52:59.819Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-06-14T09:50:33.511Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
📚 Learning: 2025-06-26T12:33:40.146Z
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#2308
File: .github/workflows/deploy-staging.yml:26-27
Timestamp: 2025-06-26T12:33:40.146Z
Learning: The `deploy-staging.yml` workflow only triggers on `pull_request` events, so the `trigger_source` variable correctly assumes PR context is always available and doesn't need guards for other event types.

Applied to files:

  • .github/workflows/deploy-beta.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Deploy to NPM / Deploy to NPM
  • GitHub Check: Get affected projects
  • GitHub Check: Unit Tests and Lint
🔇 Additional comments (1)
.github/workflows/deploy-beta.yml (1)

72-73: Draft PR guard disabled.

Commenting out the failure/return allows beta deploys from draft PRs. Confirm this relaxation is intentional for this workflow.

Comment thread .github/workflows/deploy-beta.yml Outdated
Comment thread .github/workflows/deploy-beta.yml Outdated
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d0a617 and 29b4d0c.

📒 Files selected for processing (4)
  • .github/workflows/deploy-beta.yml (1 hunks)
  • .github/workflows/deploy-npm.yml (3 hunks)
  • .github/workflows/publish-new-release.yml (1 hunks)
  • .github/workflows/rollback.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/deploy-npm.yml
  • .github/workflows/deploy-beta.yml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-10-08T15:52:59.819Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
Learnt from: saikumarrs
PR: rudderlabs/rudder-sdk-js#1754
File: packages/analytics-js-service-worker/README.md:0-0
Timestamp: 2024-06-14T09:50:33.511Z
Learning: saikumarrs prefers simplified language in documentation, avoiding redundant phrases.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Bundle size checks
  • GitHub Check: Code quality checks
  • GitHub Check: Unit Tests and Lint
🔇 Additional comments (1)
.github/workflows/publish-new-release.yml (1)

271-273: NPM token plumbed — deploy-npm.yml consumes it

deploy-npm.yml sets NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} and runs npm set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }} before publishing/deprecating (see .github/workflows/deploy-npm.yml).

Comment thread .github/workflows/rollback.yml
@saikumarrs

Copy link
Copy Markdown
Member Author

This is not needed anymore as it is being released as a hotfix.

@saikumarrs saikumarrs closed this Sep 25, 2025
@github-actions github-actions Bot deleted the feature/sdk-3972-switch-to-in-memory-storage-for-storing-events-when branch November 25, 2025 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants