Skip to content

fix(persistence): only persist session activity timestamp every 5s#3686

Open
pauldambra wants to merge 7 commits into
mainfrom
posthog-code/persist-activity-ts-granularity
Open

fix(persistence): only persist session activity timestamp every 5s#3686
pauldambra wants to merge 7 commits into
mainfrom
posthog-code/persist-activity-ts-granularity

Conversation

@pauldambra
Copy link
Copy Markdown
Member

@pauldambra pauldambra commented May 28, 2026

Problem

there is an open P2 bug in chromium that local storage writes cause a memory leak https://issues.chromium.org/issues/394823902

we write to local storage a lotthe PH memory leak in PostHog/posthog#32479 is fiendish to replicate in a test

Changes

let's write to local storage less since it can't hurt...

first, let's not track activitity timestamp in millsecond increments writing each to local storage. we can afford to drift a little

-----

Problem

SessionIdManager writes the session-activity-timestamp into PostHog persistence on every event capture (4+ times per second during active use). Each write serializes the full props blob, writes through localStorage (or cookie + localStorage), and broadcasts a cross-tab storage event in every same-origin tab — even when no listener reacts. Chrome allocates the payload buffer in mojo IPC regardless of subscribers, so this is real work.

Changes

Throttle activity-timestamp-only writes to a 5s granularity. Id-changes and session-start-changes still persist immediately. The in-memory _sessionActivityTimestamp is updated at full resolution so within-tab reads still see the freshest tick.

Throttle logic:

  • Compare against the last persisted value, not the last in-memory tick, so cumulative sub-granularity drift still crosses the gate at the right point.
  • Math.abs defends against backwards clock jumps (NTP correction, devtools time travel, another tab writing an older value).
  • On destroy and beforeunload, flush the latest in-memory value through to persistence so a tab closing inside the granularity window does not leave the persisted timestamp up to 5s stale for other tabs / future loads.

Cross-tab semantics

This is a deliberate, narrow change to cross-tab behaviour: other tabs reading SESSION_ID directly from persistence.props[SESSION_ID] now see an activity timestamp that can lag the freshest tick by up to 5s. With the minimum configurable session_idle_timeout_seconds = 60, that's ~8% of the minimum idle threshold — well under the safety margin, and a regression test pins the invariant. Downstream consumers that read SESSION_ID directly (session replay, surveys, feature flags) are not sensitive to sub-5s timestamp freshness.

Release info

Libraries affected

  • posthog-js (web)

Checklist

  • Tests for new code
  • Accounted for the impact of any changes across different platforms
  • Accounted for backwards compatibility of any changes (no breaking changes!)
  • Took care not to unnecessarily increase the bundle size

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
posthog-example-next-app-router Ready Ready Preview May 30, 2026 11:28am
posthog-js Ready Ready Preview May 30, 2026 11:28am
posthog-nextjs-config Ready Ready Preview May 30, 2026 11:28am

Request Review

Copy link
Copy Markdown
Member Author

pauldambra commented May 28, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Size Change: +11.4 kB (+0.07%)

Total Size: 16.5 MB

Filename Size Change
packages/browser/dist/array.full.es5.js 349 kB +1.17 kB (+0.34%)
packages/browser/dist/array.full.js 430 kB +659 B (+0.15%)
packages/browser/dist/array.full.no-********.js 505 kB +1.13 kB (+0.22%)
packages/browser/dist/array.js 192 kB +654 B (+0.34%)
packages/browser/dist/array.no-********.js 211 kB +1.13 kB (+0.54%)
packages/browser/dist/default-extensions.js 191 kB +658 B (+0.35%)
packages/browser/dist/main.js 197 kB +658 B (+0.34%)
packages/browser/dist/module.full.js 433 kB +659 B (+0.15%)
packages/browser/dist/module.full.no-********.js 507 kB +1.13 kB (+0.22%)
packages/browser/dist/module.js 196 kB +654 B (+0.33%)
packages/browser/dist/module.no-********.js 214 kB +1.13 kB (+0.53%)
packages/browser/dist/module.slim.js 103 kB +654 B (+0.64%)
packages/browser/dist/module.slim.no-********.js 109 kB +1.13 kB (+1.05%)
ℹ️ View Unchanged
Filename Size Change
packages/ai/dist/anthropic/index.cjs 26.2 kB 0 B
packages/ai/dist/anthropic/index.mjs 25.9 kB 0 B
packages/ai/dist/gemini/index.cjs 34.6 kB 0 B
packages/ai/dist/gemini/index.mjs 34.5 kB 0 B
packages/ai/dist/index.cjs 177 kB 0 B
packages/ai/dist/index.mjs 176 kB 0 B
packages/ai/dist/langchain/index.cjs 47.9 kB 0 B
packages/ai/dist/langchain/index.mjs 47.3 kB 0 B
packages/ai/dist/openai-agents/index.cjs 25.5 kB 0 B
packages/ai/dist/openai-agents/index.mjs 25.4 kB 0 B
packages/ai/dist/openai/index.cjs 54.3 kB 0 B
packages/ai/dist/openai/index.mjs 54 kB 0 B
packages/ai/dist/otel/index.cjs 5.68 kB 0 B
packages/ai/dist/otel/index.mjs 5.57 kB 0 B
packages/ai/dist/vercel/index.cjs 44.8 kB 0 B
packages/ai/dist/vercel/index.mjs 44.7 kB 0 B
packages/browser/dist/all-external-dependencies.js 261 kB 0 B
packages/browser/dist/conversations.js 67.3 kB 0 B
packages/browser/dist/crisp-chat-integration.js 1.97 kB 0 B
packages/browser/dist/customizations.full.js 18 kB 0 B
packages/browser/dist/dead-clicks-autocapture.js 14.3 kB 0 B
packages/browser/dist/element-inference.js 5.69 kB 0 B
packages/browser/dist/exception-autocapture.js 11.8 kB 0 B
packages/browser/dist/extension-bundles.js 106 kB 0 B
packages/browser/dist/external-scripts-loader.js 3.13 kB 0 B
packages/browser/dist/intercom-integration.js 2.03 kB 0 B
packages/browser/dist/lazy-********.js 151 kB 0 B
packages/browser/dist/logs.js 40.6 kB 0 B
packages/browser/dist/posthog-********.js 151 kB 0 B
packages/browser/dist/product-tours-preview.js 76.4 kB 0 B
packages/browser/dist/product-tours.js 115 kB 0 B
packages/browser/dist/recorder-v2.js 99 kB 0 B
packages/browser/dist/recorder.js 99 kB 0 B
packages/browser/dist/rrweb-plugin-console-record.js 6.67 kB 0 B
packages/browser/dist/rrweb-types.js 2.28 kB 0 B
packages/browser/dist/rrweb.js 280 kB 0 B
packages/browser/dist/surveys-preview.js 76.3 kB 0 B
packages/browser/dist/surveys.js 94.7 kB 0 B
packages/browser/dist/tracing-headers.js 1.84 kB 0 B
packages/browser/dist/web-vitals-with-attribution.js 11.8 kB 0 B
packages/browser/dist/web-vitals.js 6.39 kB 0 B
packages/browser/react/dist/esm/index.js 21.2 kB 0 B
packages/browser/react/dist/esm/slim/index.js 17.6 kB 0 B
packages/browser/react/dist/esm/surveys/index.js 4.68 kB 0 B
packages/browser/react/dist/umd/index.js 24.4 kB 0 B
packages/browser/react/dist/umd/slim/index.js 20.4 kB 0 B
packages/browser/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/convex/dist/client/feature-flags/crypto.js 461 B 0 B
packages/convex/dist/client/feature-flags/evaluator.js 16.5 kB 0 B
packages/convex/dist/client/feature-flags/index.js 196 B 0 B
packages/convex/dist/client/feature-flags/match-********.js 14.8 kB 0 B
packages/convex/dist/client/feature-flags/types.js 44 B 0 B
packages/convex/dist/client/index.js 14.7 kB 0 B
packages/convex/dist/component/_generated/api.js 712 B 0 B
packages/convex/dist/component/_generated/component.js 212 B 0 B
packages/convex/dist/component/_generated/dataModel.js 230 B 0 B
packages/convex/dist/component/_generated/server.js 3.74 kB 0 B
packages/convex/dist/component/convex.config.js 1.11 kB 0 B
packages/convex/dist/component/crons.js 1.4 kB 0 B
packages/convex/dist/component/lib.js 21.4 kB 0 B
packages/convex/dist/component/schema.js 694 B 0 B
packages/convex/dist/component/version.js 67 B 0 B
packages/core/dist/cookie.js 5.34 kB 0 B
packages/core/dist/cookie.mjs 3.12 kB 0 B
packages/core/dist/error-tracking/chunk-ids.js 2.54 kB 0 B
packages/core/dist/error-tracking/chunk-ids.mjs 1.31 kB 0 B
packages/core/dist/error-tracking/coercers/dom-exception-coercer.js 2.3 kB 0 B
packages/core/dist/error-tracking/coercers/dom-exception-coercer.mjs 993 B 0 B
packages/core/dist/error-tracking/coercers/error-coercer.js 2.02 kB 0 B
packages/core/dist/error-tracking/coercers/error-coercer.mjs 794 B 0 B
packages/core/dist/error-tracking/coercers/error-event-coercer.js 1.76 kB 0 B
packages/core/dist/error-tracking/coercers/error-event-coercer.mjs 513 B 0 B
packages/core/dist/error-tracking/coercers/event-coercer.js 1.82 kB 0 B
packages/core/dist/error-tracking/coercers/event-coercer.mjs 548 B 0 B
packages/core/dist/error-tracking/coercers/index.js 6.79 kB 0 B
packages/core/dist/error-tracking/coercers/index.mjs 326 B 0 B
packages/core/dist/error-tracking/coercers/object-coercer.js 3.46 kB 0 B
packages/core/dist/error-tracking/coercers/object-coercer.mjs 2.07 kB 0 B
packages/core/dist/error-tracking/coercers/primitive-coercer.js 1.67 kB 0 B
packages/core/dist/error-tracking/coercers/primitive-coercer.mjs 419 B 0 B
packages/core/dist/error-tracking/coercers/promise-rejection-event.js 2.59 kB 0 B
packages/core/dist/error-tracking/coercers/promise-rejection-event.mjs 1.25 kB 0 B
packages/core/dist/error-tracking/coercers/string-coercer.js 2.01 kB 0 B
packages/core/dist/error-tracking/coercers/string-coercer.mjs 820 B 0 B
packages/core/dist/error-tracking/coercers/utils.js 2.06 kB 0 B
packages/core/dist/error-tracking/coercers/utils.mjs 716 B 0 B
packages/core/dist/error-tracking/error-properties-builder.js 5.56 kB 0 B
packages/core/dist/error-tracking/error-properties-builder.mjs 4.23 kB 0 B
packages/core/dist/error-tracking/exception-steps.js 6.87 kB 0 B
packages/core/dist/error-tracking/exception-steps.mjs 4.71 kB 0 B
packages/core/dist/error-tracking/index.js 4.74 kB 0 B
packages/core/dist/error-tracking/index.mjs 191 B 0 B
packages/core/dist/error-tracking/parsers/base.js 1.83 kB 0 B
packages/core/dist/error-tracking/parsers/base.mjs 464 B 0 B
packages/core/dist/error-tracking/parsers/chrome.js 2.73 kB 0 B
packages/core/dist/error-tracking/parsers/chrome.mjs 1.32 kB 0 B
packages/core/dist/error-tracking/parsers/gecko.js 2.47 kB 0 B
packages/core/dist/error-tracking/parsers/gecko.mjs 1.13 kB 0 B
packages/core/dist/error-tracking/parsers/index.js 4.75 kB 0 B
packages/core/dist/error-tracking/parsers/index.mjs 2.1 kB 0 B
packages/core/dist/error-tracking/parsers/node.js 3.94 kB 0 B
packages/core/dist/error-tracking/parsers/node.mjs 2.68 kB 0 B
packages/core/dist/error-tracking/parsers/opera.js 2.26 kB 0 B
packages/core/dist/error-tracking/parsers/opera.mjs 746 B 0 B
packages/core/dist/error-tracking/parsers/safari.js 1.88 kB 0 B
packages/core/dist/error-tracking/parsers/safari.mjs 574 B 0 B
packages/core/dist/error-tracking/parsers/winjs.js 1.72 kB 0 B
packages/core/dist/error-tracking/parsers/winjs.mjs 426 B 0 B
packages/core/dist/error-tracking/types.js 1.33 kB 0 B
packages/core/dist/error-tracking/types.mjs 131 B 0 B
packages/core/dist/error-tracking/utils.js 1.8 kB 0 B
packages/core/dist/error-tracking/utils.mjs 604 B 0 B
packages/core/dist/eventemitter.js 1.78 kB 0 B
packages/core/dist/eventemitter.mjs 571 B 0 B
packages/core/dist/featureFlagUtils.js 6.8 kB 0 B
packages/core/dist/featureFlagUtils.mjs 4.32 kB 0 B
packages/core/dist/gzip.js 5.72 kB 0 B
packages/core/dist/gzip.mjs 3.84 kB 0 B
packages/core/dist/index.js 13.6 kB 0 B
packages/core/dist/index.mjs 1.31 kB 0 B
packages/core/dist/logs/index.js 9.47 kB 0 B
packages/core/dist/logs/index.mjs 7.87 kB 0 B
packages/core/dist/logs/logs-utils.js 5.96 kB 0 B
packages/core/dist/logs/logs-utils.mjs 3.99 kB 0 B
packages/core/dist/logs/types.js 603 B 0 B
packages/core/dist/logs/types.mjs 0 B 0 B 🆕
packages/core/dist/posthog-core-stateless.js 34.6 kB 0 B
packages/core/dist/posthog-core-stateless.mjs 31.7 kB 0 B
packages/core/dist/posthog-core.js 41.8 kB 0 B
packages/core/dist/posthog-core.mjs 36.8 kB 0 B
packages/core/dist/surveys/events.js 4.21 kB 0 B
packages/core/dist/surveys/events.mjs 1.99 kB 0 B
packages/core/dist/surveys/index.js 4.57 kB 0 B
packages/core/dist/surveys/index.mjs 894 B 0 B
packages/core/dist/surveys/translations.js 9.4 kB 0 B
packages/core/dist/surveys/translations.mjs 7.03 kB 0 B
packages/core/dist/surveys/validation.js 3.06 kB 0 B
packages/core/dist/surveys/validation.mjs 1.51 kB 0 B
packages/core/dist/testing/index.js 2.93 kB 0 B
packages/core/dist/testing/index.mjs 79 B 0 B
packages/core/dist/testing/PostHogCoreTestClient.js 3.15 kB 0 B
packages/core/dist/testing/PostHogCoreTestClient.mjs 1.74 kB 0 B
packages/core/dist/testing/test-utils.js 2.83 kB 0 B
packages/core/dist/testing/test-utils.mjs 1.15 kB 0 B
packages/core/dist/tracing-headers.js 3.38 kB 0 B
packages/core/dist/tracing-headers.mjs 2.08 kB 0 B
packages/core/dist/types.js 9.62 kB 0 B
packages/core/dist/types.mjs 7.07 kB 0 B
packages/core/dist/utils/bot-detection.js 3.28 kB 0 B
packages/core/dist/utils/bot-detection.mjs 1.95 kB 0 B
packages/core/dist/utils/bucketed-rate-limiter.js 3 kB 0 B
packages/core/dist/utils/bucketed-rate-limiter.mjs 1.62 kB 0 B
packages/core/dist/utils/index.js 11.9 kB 0 B
packages/core/dist/utils/index.mjs 1.98 kB 0 B
packages/core/dist/utils/logger.js 2.58 kB 0 B
packages/core/dist/utils/logger.mjs 1.29 kB 0 B
packages/core/dist/utils/number-utils.js 3.32 kB 0 B
packages/core/dist/utils/number-utils.mjs 1.68 kB 0 B
packages/core/dist/utils/promise-queue.js 2 kB 0 B
packages/core/dist/utils/promise-queue.mjs 768 B 0 B
packages/core/dist/utils/string-utils.js 2.73 kB 0 B
packages/core/dist/utils/string-utils.mjs 1.09 kB 0 B
packages/core/dist/utils/type-utils.js 7.04 kB 0 B
packages/core/dist/utils/type-utils.mjs 3.11 kB 0 B
packages/core/dist/utils/user-agent-utils.js 15.5 kB 0 B
packages/core/dist/utils/user-agent-utils.mjs 12.4 kB 0 B
packages/core/dist/vendor/uuidv7.js 8.29 kB 0 B
packages/core/dist/vendor/uuidv7.mjs 6.72 kB 0 B
packages/mcp/dist/index.js 1.29 kB 0 B
packages/mcp/dist/index.mjs 61 B 0 B
packages/mcp/dist/version.js 1.21 kB 0 B
packages/mcp/dist/version.mjs 45 B 0 B
packages/next/dist/app/PostHogProvider.js 3.33 kB 0 B
packages/next/dist/client/ClientPostHogProvider.js 1.76 kB 0 B
packages/next/dist/client/hooks.js 172 B 0 B
packages/next/dist/client/PostHogPageView.js 1.76 kB 0 B
packages/next/dist/index.client.js 401 B 0 B
packages/next/dist/index.edge.js 447 B 0 B
packages/next/dist/index.js 444 B 0 B
packages/next/dist/index.react-server.js 420 B 0 B
packages/next/dist/middleware/postHogMiddleware.js 3.7 kB 0 B
packages/next/dist/pages.client.js 502 B 0 B
packages/next/dist/pages.edge.js 570 B 0 B
packages/next/dist/pages.js 414 B 0 B
packages/next/dist/pages/getServerSidePostHog.js 1.99 kB 0 B
packages/next/dist/pages/PostHogPageView.js 1.32 kB 0 B
packages/next/dist/pages/PostHogProvider.js 1.61 kB 0 B
packages/next/dist/server/getPostHog.js 2.79 kB 0 B
packages/next/dist/server/nodeClientCache.js 1.31 kB 0 B
packages/next/dist/shared/browser.js 195 B 0 B
packages/next/dist/shared/config.js 2.08 kB 0 B
packages/next/dist/shared/constants.js 201 B 0 B
packages/next/dist/shared/cookie.js 540 B 0 B
packages/next/dist/shared/identity.js 264 B 0 B
packages/next/dist/shared/tracing-headers.js 2.18 kB 0 B
packages/nextjs-config/dist/config.js 5.82 kB 0 B
packages/nextjs-config/dist/config.mjs 4.34 kB 0 B
packages/nextjs-config/dist/index.js 2.24 kB 0 B
packages/nextjs-config/dist/index.mjs 30 B 0 B
packages/nextjs-config/dist/utils.js 2.94 kB 0 B
packages/nextjs-config/dist/utils.mjs 826 B 0 B
packages/node/dist/client.js 45.6 kB 0 B
packages/node/dist/client.mjs 43.2 kB 0 B
packages/node/dist/entrypoints/index.edge.js 3.85 kB 0 B
packages/node/dist/entrypoints/index.edge.mjs 720 B 0 B
packages/node/dist/entrypoints/index.node.js 5.67 kB 0 B
packages/node/dist/entrypoints/index.node.mjs 1.25 kB 0 B
packages/node/dist/entrypoints/nestjs.js 2.31 kB 0 B
packages/node/dist/entrypoints/nestjs.mjs 42 B 0 B
packages/node/dist/experimental.js 870 B 0 B
packages/node/dist/experimental.mjs 267 B 0 B
packages/node/dist/exports.js 6.75 kB 0 B
packages/node/dist/exports.mjs 582 B 0 B
packages/node/dist/extensions/context/context.js 2.13 kB 0 B
packages/node/dist/extensions/context/context.mjs 863 B 0 B
packages/node/dist/extensions/context/types.js 603 B 0 B
packages/node/dist/extensions/context/types.mjs 0 B 0 B 🆕
packages/node/dist/extensions/error-tracking/autocapture.js 2.66 kB 0 B
packages/node/dist/extensions/error-tracking/autocapture.mjs 1.24 kB 0 B
packages/node/dist/extensions/error-tracking/index.js 4.15 kB 0 B
packages/node/dist/extensions/error-tracking/index.mjs 2.88 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/context-lines.node.js 8.81 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/context-lines.node.mjs 7.15 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/module.node.js 2.78 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/module.node.mjs 1.45 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/relative-path.node.js 1.97 kB 0 B
packages/node/dist/extensions/error-tracking/modifiers/relative-path.node.mjs 624 B 0 B
packages/node/dist/extensions/express.js 4.6 kB 0 B
packages/node/dist/extensions/express.mjs 2.49 kB 0 B
packages/node/dist/extensions/feature-flags/cache.js 603 B 0 B
packages/node/dist/extensions/feature-flags/cache.mjs 0 B 0 B 🆕
packages/node/dist/extensions/feature-flags/crypto.js 1.57 kB 0 B
packages/node/dist/extensions/feature-flags/crypto.mjs 395 B 0 B
packages/node/dist/extensions/feature-flags/feature-flags.js 40.6 kB 0 B
packages/node/dist/extensions/feature-flags/feature-flags.mjs 38.5 kB 0 B
packages/node/dist/extensions/nestjs.js 5 kB 0 B
packages/node/dist/extensions/nestjs.mjs 2.9 kB 0 B
packages/node/dist/extensions/sentry-integration.js 4.66 kB 0 B
packages/node/dist/extensions/sentry-integration.mjs 3.17 kB 0 B
packages/node/dist/extensions/tracing-headers.js 3.31 kB 0 B
packages/node/dist/extensions/tracing-headers.mjs 1.53 kB 0 B
packages/node/dist/feature-flag-evaluations.js 5.97 kB 0 B
packages/node/dist/feature-flag-evaluations.mjs 4.63 kB 0 B
packages/node/dist/storage-memory.js 1.52 kB 0 B
packages/node/dist/storage-memory.mjs 297 B 0 B
packages/node/dist/types.js 1.43 kB 0 B
packages/node/dist/types.mjs 224 B 0 B
packages/node/dist/version.js 1.21 kB 0 B
packages/node/dist/version.mjs 46 B 0 B
packages/nuxt/dist/module.mjs 5.29 kB 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagEnabled.js 566 B 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagPayload.js 690 B 0 B
packages/nuxt/dist/runtime/composables/useFeatureFlagVariantKey.js 591 B 0 B
packages/nuxt/dist/runtime/composables/usePostHog.js 128 B 0 B
packages/nuxt/dist/runtime/nitro-plugin.js 1.08 kB 0 B
packages/nuxt/dist/runtime/vue-plugin.js 1.14 kB 0 B
packages/plugin-utils/dist/cli.js 3.14 kB 0 B
packages/plugin-utils/dist/cli.mjs 1.64 kB 0 B
packages/plugin-utils/dist/config.js 3.07 kB 0 B
packages/plugin-utils/dist/config.mjs 1.83 kB 0 B
packages/plugin-utils/dist/index.js 4.3 kB 0 B
packages/plugin-utils/dist/index.mjs 217 B 0 B
packages/plugin-utils/dist/spawn-local.js 2.17 kB 0 B
packages/plugin-utils/dist/spawn-local.mjs 918 B 0 B
packages/plugin-utils/dist/utils.js 3.27 kB 0 B
packages/plugin-utils/dist/utils.mjs 1.3 kB 0 B
packages/react-native/dist/autocapture.js 5.05 kB 0 B
packages/react-native/dist/error-tracking/index.js 5.77 kB 0 B
packages/react-native/dist/error-tracking/utils.js 2.58 kB 0 B
packages/react-native/dist/frameworks/wix-navigation.js 1.3 kB 0 B
packages/react-native/dist/hooks/useFeatureFlag.js 1.7 kB 0 B
packages/react-native/dist/hooks/useFeatureFlagResult.js 963 B 0 B
packages/react-native/dist/hooks/useFeatureFlags.js 921 B 0 B
packages/react-native/dist/hooks/useNavigationTracker.js 2.45 kB 0 B
packages/react-native/dist/hooks/usePostHog.js 544 B 0 B
packages/react-native/dist/hooks/utils.js 988 B 0 B
packages/react-native/dist/index.js 4.33 kB 0 B
packages/react-native/dist/logs-********.js 3.32 kB 0 B
packages/react-native/dist/native-deps.js 8.77 kB 0 B
packages/react-native/dist/optional/OptionalAsyncStorage.js 299 B 0 B
packages/react-native/dist/optional/OptionalExpoApplication.js 377 B 0 B
packages/react-native/dist/optional/OptionalExpoDevice.js 347 B 0 B
packages/react-native/dist/optional/OptionalExpoFileSystem.js 386 B 0 B
packages/react-native/dist/optional/OptionalExpoFileSystemLegacy.js 423 B 0 B
packages/react-native/dist/optional/OptionalExpoLocalization.js 383 B 0 B
packages/react-native/dist/optional/OptionalReactNativeDeviceInfo.js 415 B 0 B
packages/react-native/dist/optional/OptionalReactNativeLocalize.js 303 B 0 B
packages/react-native/dist/optional/OptionalReactNativeNavigation.js 415 B 0 B
packages/react-native/dist/optional/OptionalReactNativeNavigationWix.js 443 B 0 B
packages/react-native/dist/optional/OptionalReactNativeSafeArea.js 644 B 0 B
packages/react-native/dist/optional/OptionalReactNativeSvg.js 872 B 0 B
packages/react-native/dist/optional/OptionalSessionReplay.js 455 B 0 B
packages/react-native/dist/posthog-rn.js 47.6 kB 0 B
packages/react-native/dist/PostHogContext.js 329 B 0 B
packages/react-native/dist/PostHogErrorBoundary.js 3.19 kB 0 B
packages/react-native/dist/PostHogMaskView.js 1.68 kB 0 B
packages/react-native/dist/PostHogProvider.js 4.55 kB 0 B
packages/react-native/dist/storage.js 5.76 kB 0 B
packages/react-native/dist/surveys/components/BottomSection.js 1.46 kB 0 B
packages/react-native/dist/surveys/components/Cancel.js 909 B 0 B
packages/react-native/dist/surveys/components/ConfirmationMessage.js 1.65 kB 0 B
packages/react-native/dist/surveys/components/QuestionHeader.js 1.37 kB 0 B
packages/react-native/dist/surveys/components/QuestionTypes.js 13.3 kB 0 B
packages/react-native/dist/surveys/components/SurveyModal.js 6.27 kB 0 B
packages/react-native/dist/surveys/components/Surveys.js 6.58 kB 0 B
packages/react-native/dist/surveys/getActiveMatchingSurveys.js 2.64 kB 0 B
packages/react-native/dist/surveys/icons.js 9.97 kB 0 B
packages/react-native/dist/surveys/index.js 600 B 0 B
packages/react-native/dist/surveys/PostHogSurveyProvider.js 6.28 kB 0 B
packages/react-native/dist/surveys/survey-translations.js 1.11 kB 0 B
packages/react-native/dist/surveys/surveys-utils.js 14.2 kB 0 B
packages/react-native/dist/surveys/useActivatedSurveys.js 3.67 kB 0 B
packages/react-native/dist/surveys/useSurveyStorage.js 2.16 kB 0 B
packages/react-native/dist/tooling/expoconfig.js 4.05 kB 0 B
packages/react-native/dist/tooling/metroconfig.js 2.32 kB 0 B
packages/react-native/dist/tooling/posthogMetroSerializer.js 4.86 kB 0 B
packages/react-native/dist/tooling/utils.js 4.05 kB 0 B
packages/react-native/dist/tooling/vendor/expo/expoconfig.js 70 B 0 B
packages/react-native/dist/tooling/vendor/metro/countLines.js 237 B 0 B
packages/react-native/dist/tooling/vendor/metro/utils.js 3.35 kB 0 B
packages/react-native/dist/types.js 70 B 0 B
packages/react-native/dist/utils.js 1.14 kB 0 B
packages/react-native/dist/version.js 130 B 0 B
packages/react/dist/esm/index.js 21.2 kB 0 B
packages/react/dist/esm/slim/index.js 17.6 kB 0 B
packages/react/dist/esm/surveys/index.js 4.68 kB 0 B
packages/react/dist/umd/index.js 24.4 kB 0 B
packages/react/dist/umd/slim/index.js 20.4 kB 0 B
packages/react/dist/umd/surveys/index.js 5.45 kB 0 B
packages/rollup-plugin/dist/index.js 2.44 kB 0 B
packages/rrweb/all/dist/rrweb-all.cjs 612 kB 0 B
packages/rrweb/all/dist/rrweb-all.js 612 kB 0 B
packages/rrweb/all/dist/rrweb-all.umd.cjs 615 kB 0 B
packages/rrweb/all/dist/rrweb-all.umd.min.cjs 290 kB 0 B
packages/rrweb/packer/dist/base-********.js 18.2 kB 0 B
packages/rrweb/packer/dist/base-********.cjs 18.3 kB 0 B
packages/rrweb/packer/dist/base-********.umd.cjs 18.7 kB 0 B
packages/rrweb/packer/dist/base-********.umd.min.cjs 9.5 kB 0 B
packages/rrweb/packer/dist/pack.cjs 347 B 0 B
packages/rrweb/packer/dist/pack.js 285 B 0 B
packages/rrweb/packer/dist/pack.umd.cjs 1.63 kB 0 B
packages/rrweb/packer/dist/pack.umd.min.cjs 1.11 kB 0 B
packages/rrweb/packer/dist/packer.cjs 257 B 0 B
packages/rrweb/packer/dist/packer.js 136 B 0 B
packages/rrweb/packer/dist/packer.umd.cjs 662 B 0 B
packages/rrweb/packer/dist/packer.umd.min.cjs 626 B 0 B
packages/rrweb/packer/dist/unpack.cjs 769 B 0 B
packages/rrweb/packer/dist/unpack.js 702 B 0 B
packages/rrweb/packer/dist/unpack.umd.cjs 1.17 kB 0 B
packages/rrweb/packer/dist/unpack.umd.min.cjs 955 B 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.cjs 37.6 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.js 37.5 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.cjs 38 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-record/dist/rrweb-plugin-canvas-webrtc-record.umd.min.cjs 22.2 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.cjs 34.3 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.js 34.2 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.cjs 34.7 kB 0 B
packages/rrweb/plugins/rrweb-plugin-canvas-webrtc-replay/dist/rrweb-plugin-canvas-webrtc-replay.umd.min.cjs 20.5 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.cjs 14.9 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.js 14.8 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.cjs 15.4 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-record/dist/rrweb-plugin-console-record.umd.min.cjs 7.33 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.cjs 5.01 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.js 4.9 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.cjs 5.44 kB 0 B
packages/rrweb/plugins/rrweb-plugin-console-replay/dist/rrweb-plugin-console-replay.umd.min.cjs 2.64 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.cjs 681 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.js 548 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.cjs 1.12 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-record/dist/rrweb-plugin-sequential-id-record.umd.min.cjs 829 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.cjs 933 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.js 820 B 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.cjs 1.37 kB 0 B
packages/rrweb/plugins/rrweb-plugin-sequential-id-replay/dist/rrweb-plugin-sequential-id-replay.umd.min.cjs 968 B 0 B
packages/rrweb/record/dist/rrweb-record.cjs 174 kB 0 B
packages/rrweb/record/dist/rrweb-record.js 174 kB 0 B
packages/rrweb/record/dist/rrweb-record.umd.cjs 174 kB 0 B
packages/rrweb/record/dist/rrweb-record.umd.min.cjs 83.5 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.cjs 440 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.js 440 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.umd.cjs 443 kB 0 B
packages/rrweb/replay/dist/rrweb-replay.umd.min.cjs 210 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.cjs 150 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.js 149 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.umd.cjs 152 kB 0 B
packages/rrweb/rrdom-nodejs/dist/rrdom-nodejs.umd.min.cjs 70.8 kB 0 B
packages/rrweb/rrdom/dist/rrdom.cjs 174 kB 0 B
packages/rrweb/rrdom/dist/rrdom.js 173 kB 0 B
packages/rrweb/rrdom/dist/rrdom.umd.cjs 175 kB 0 B
packages/rrweb/rrdom/dist/rrdom.umd.min.cjs 80.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.cjs 32.6 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.js 31.6 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.umd.cjs 53.9 kB 0 B
packages/rrweb/rrweb-snapshot/dist/record.umd.min.cjs 25.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.cjs 138 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.js 137 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.umd.cjs 161 kB 0 B
packages/rrweb/rrweb-snapshot/dist/replay.umd.min.cjs 74 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.cjs 2.27 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.js 1.42 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.umd.cjs 218 kB 0 B
packages/rrweb/rrweb-snapshot/dist/rrweb-********.umd.min.cjs 92 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.cjs 18.3 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.umd.cjs 18.8 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.umd.min.cjs 9.31 kB 0 B
packages/rrweb/rrweb-snapshot/dist/types-********.js 17.8 kB 0 B
packages/rrweb/rrweb/dist/rrweb.cjs 595 kB 0 B
packages/rrweb/rrweb/dist/rrweb.js 595 kB 0 B
packages/rrweb/rrweb/dist/rrweb.umd.cjs 596 kB 0 B
packages/rrweb/rrweb/dist/rrweb.umd.min.cjs 281 kB 0 B
packages/rrweb/types/dist/rrweb-types.cjs 5.64 kB 0 B
packages/rrweb/types/dist/rrweb-types.js 5.38 kB 0 B
packages/rrweb/types/dist/rrweb-types.umd.cjs 6.04 kB 0 B
packages/rrweb/types/dist/rrweb-types.umd.min.cjs 2.8 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.cjs 6.41 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.js 5.95 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.umd.cjs 6.82 kB 0 B
packages/rrweb/utils/dist/rrweb-utils.umd.min.cjs 3.51 kB 0 B
packages/types/dist/capture-log.js 603 B 0 B
packages/types/dist/capture-log.mjs 0 B 0 B 🆕
packages/types/dist/capture.js 603 B 0 B
packages/types/dist/capture.mjs 0 B 0 B 🆕
packages/types/dist/common.js 603 B 0 B
packages/types/dist/common.mjs 0 B 0 B 🆕
packages/types/dist/feature-flags.js 603 B 0 B
packages/types/dist/feature-flags.mjs 0 B 0 B 🆕
packages/types/dist/index.js 603 B 0 B
packages/types/dist/index.mjs 0 B 0 B 🆕
packages/types/dist/posthog-config.js 603 B 0 B
packages/types/dist/posthog-config.mjs 0 B 0 B 🆕
packages/types/dist/posthog.js 603 B 0 B
packages/types/dist/posthog.mjs 0 B 0 B 🆕
packages/types/dist/request.js 603 B 0 B
packages/types/dist/request.mjs 0 B 0 B 🆕
packages/types/dist/segment.js 603 B 0 B
packages/types/dist/segment.mjs 0 B 0 B 🆕
packages/types/dist/session-recording.js 603 B 0 B
packages/types/dist/session-recording.mjs 0 B 0 B 🆕
packages/types/dist/survey.js 603 B 0 B
packages/types/dist/survey.mjs 0 B 0 B 🆕
packages/types/dist/toolbar.js 603 B 0 B
packages/types/dist/toolbar.mjs 0 B 0 B 🆕
packages/types/dist/tree-shakeable.js 603 B 0 B
packages/types/dist/tree-shakeable.mjs 0 B 0 B 🆕
packages/web/dist/index.cjs 13.8 kB 0 B
packages/web/dist/index.mjs 13.7 kB 0 B
packages/webpack-plugin/dist/config.js 1.53 kB 0 B
packages/webpack-plugin/dist/config.mjs 543 B 0 B
packages/webpack-plugin/dist/index.js 5.38 kB 0 B
packages/webpack-plugin/dist/index.mjs 2.04 kB 0 B
tooling/changelog/dist/index.js 3.31 kB 0 B
tooling/rollup-utils/dist/index.js 1.17 kB 0 B

compressed-size-action

Copy link
Copy Markdown
Member Author

@pauldambra pauldambra left a comment

Choose a reason for hiding this comment

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

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review complete. See inline comments + summary thread below.

Comment thread packages/browser/src/sessionid.ts Outdated
Comment thread packages/browser/src/sessionid.ts
Comment thread packages/browser/src/sessionid.ts
Comment thread packages/browser/src/sessionid.ts Outdated
Comment thread packages/browser/src/sessionid.ts Outdated
Comment thread packages/browser/src/sessionid.ts
Comment thread packages/browser/src/sessionid.ts
Comment thread packages/browser/src/__tests__/sessionid.test.ts
Comment thread packages/browser/src/__tests__/sessionid.test.ts
@pauldambra
Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: qa-team (specialists + generalists), paul-reviewer, xp-reviewer, security-audit

Verdict: 💬 APPROVE WITH NITS

All four reviewers landed in the same place: the change is well-scoped, the logic is correct, the tests pin the right boundaries. The 5s throttle vs 60s minimum idle timeout gives a 12x safety margin so this cannot cause incorrect idle detection. No critical or high findings. Several low/nit observations worth considering but none block merge.

Key findings

🟢 LOW

  • Implicit resetSessionId semantics (convergent: paul + xp) — comment claims a null reset that's actually achieved indirectly via _setSessionId(null,null,null). Tighten the comment or add explicit reset.
  • _setSessionId is doing three things at mixed abstraction (convergent: qa-team + xp) — candidate for ComposedMethod extraction (_shouldPersistActivityChange).
  • Cross-tab staleness contract (qa-team) — downstream consumers reading SESSION_ID from persistence now see up to 5s lag. Math is safe (5s ≪ 60s min idle) but worth noting in the commit body.
  • No flush-on-unload (qa-team) — tab closed within 5s of last event leaves the persisted activity timestamp up to 5s stale. Not a correctness bug; optional polish.
  • No observability (paul) — every "drop if X" gate without a counter is hard to verify in the wild. Suggest a debug-level skip counter.

⚪ NIT

  • Math.abs (convergent x3: qa-team + paul + xp) — clock-skew defense or accidental defense? Pin a stance.
  • 5s as a hardcoded constant (convergent: paul + xp) — paul wants it config-exposed; xp wants a test asserting the safety relationship with MIN_SESSION_IDLE_TIMEOUT_SECONDS.
  • Missing test cases (qa-team) — backwards-delta + post-id-change baseline.
  • Positive: parameterized tests + last-persisted test (paul + xp) — both reviewers explicitly praised these. The "compares against last persisted, not last in-memory" test is the most valuable in the suite.

Convergence

Findings flagged independently by 2+ reviewers (high confidence):

  • Math.abs on the timestamp delta — 3 reviewers (qa-team + paul + xp)
  • Implicit resetSessionId reset semantics — 2 reviewers (paul + xp)
  • _setSessionId structure / extract method — 2 reviewers (qa-team + xp)
  • 5s constant tradeoffs (config vs safety coupling) — 2 reviewers (paul + xp)
  • "Compares against last persisted" test is the best — 2 reviewers, positive (paul + xp)

Reviewer summaries

Reviewer Assessment
🔍 qa-team Focused, well-scoped optimisation. 12x safety margin vs minimum idle timeout makes incorrect idle detection impossible. Findings are minor (comment-vs-extract-method, optional unload flush, test-coverage gaps, cross-tab contract note). Approve with optional nits.
👤 paul high trust, ship as you see fit. logic is clear, tests cover boundaries plus the subtle compare-against-last-persisted case. one real ask: observability — a debug-level skip counter would let us answer "is this doing what we think" without a redeploy. rest is preference.
📐 xp Focused perf fix in the spirit of MakeItWorkMakeItRightMakeItFast. Naming intention-revealing, comments explain why not what, parameterized tests pin boundary behaviour. Quibble: _setSessionId could use ComposedMethod extraction; reset semantics rely on implicit fallout. Otherwise clean, simple, ship it.
🛡 security-audit 0 findings. The throttle window (5s) is an order of magnitude below the minimum configurable idle timeout (60s). SessionId/start changes always force a write. Math.abs covers backwards time. JS is single-threaded → no race on _lastPersistedActivityTimestamp. An attacker who controls localStorage already controls the session tuple — no new sink, no control removed.

Automated by QA Swarm — not a human review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86ebe2ac23

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/browser/src/sessionid.ts
Comment thread packages/browser/src/sessionid.ts
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/browser/src/sessionid.ts:240
`this._sessionId ?? null` is redundant — `_sessionId` is declared as `string | null`, so it can never be `undefined` and the nullish-coalescing fallback never fires. The two other fields in the same tuple (`this._sessionActivityTimestamp`, `this._sessionStartTimestamp`) are written directly without `?? null`, so this is also inconsistent.

```suggestion
            [SESSION_ID]: [this._sessionActivityTimestamp, this._sessionId, this._sessionStartTimestamp],
```

Reviews (1): Last reviewed commit: "refactor(persistence): address QA Swarm ..." | Re-trigger Greptile

Comment thread packages/browser/src/sessionid.ts
Copy link
Copy Markdown
Member

@lricoy lricoy left a comment

Choose a reason for hiding this comment

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

Let's test it, LGTM.

How will we track whether it IS working or failing, though?

@marandaneto
Copy link
Copy Markdown
Member

issues.chromium.org/issues/394823902

uff...

How will we track whether it IS working or failing, though?

yeah not sure either, down to try tho!

Every `capture()` call updates the session activity timestamp via
`SessionIdManager._setSessionId`. Until now this called
`persistence.register({ SESSION_ID: [...] })` for every change, which
wrote the entire posthog-js persistence blob to localStorage. On an
active page that was 4+ writes per second, each ~hundreds of KB,
broadcasting cross-tab via `storage` events on every other same-origin
tab.

The activity timestamp's only consumer is idle-timeout detection,
which uses thresholds in the tens-of-minutes range. Per-millisecond
freshness in the persisted value is not needed; per-5-seconds is more
than accurate enough — the minimum configurable idle timeout is 60s.

This change keeps the in-memory `_sessionActivityTimestamp` at full
resolution so in-process readers see the latest tick. Only the
write-through to persistence is throttled, by comparing the new
timestamp against the last value we actually persisted.

No throttling applies when the sessionId or sessionStartTimestamp
change — those are semantically important and always persisted.

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
…ctivityTimestamp

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
… throttle

- Extract _isActivityChangeBelowGranularity helper, embed clock-skew
  rationale alongside the Math.abs in the helper rather than the call site
- Introduce isActivityOnlyChange local to remove duplicated predicate
- Reset _lastPersistedActivityTimestamp explicitly in resetSessionId so
  the reset path is self-evident rather than relying on _setSessionId
  re-assignment
- Flush the pending activity timestamp on destroy and beforeunload so a
  tab closing inside the granularity window does not leave the persisted
  value up to 5s stale for other tabs / future loads
- Export MIN_SESSION_IDLE_TIMEOUT_SECONDS and ACTIVITY_TIMESTAMP_PERSIST_GRANULARITY_MS
- Add tests: invariant (granularity << min idle timeout), backward-delta
  table (clock skew), post-id-change-baseline, flush-on-destroy with and
  without pending data

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
…inst cross-tab rotation

Two correctness fixes raised in Codex review of the activity-timestamp
throttle:

1. Idle detection now compares the current wall clock against
   `max(persisted, in_memory)` via a new `_freshestActivityTimestamp`
   helper. Without this, the throttle could hold in-memory activity up
   to 5s ahead of persisted, and idle checks reading from persistence
   alone would rotate an active session up to 5s early. Used in both
   `checkAndGetSessionAndWindowId` and the `_resetIdleTimer` callback.

2. `_flushPendingActivityTimestamp` now refreshes from storage and
   bails out if the persisted session id / start timestamp no longer
   match this tab's cached values. Otherwise an unloading tab with a
   pending throttled tick could overwrite a sibling tab's rotated
   session with stale id/start values.

Tests:
- Replaced the pre-existing `persistence is source of truth over in-memory
  cache` block, which was implicitly testing the bug (it forced persisted
  to go backwards relative to in-memory and expected idle to fire). New
  tests cover the realistic scenarios: wall clock crossing timeout while
  both views are static, and an older sibling write being ignored.
- Added a 'does not rotate session early when in-memory is fresher
  than persisted' regression test pinning the Codex case.
- Added flush-clobber and flush-proceeds tests for the cross-tab guard.

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
Method and constant names already say what the code does. Keep only the
non-obvious WHY: Math.abs clock-skew defense, in-memory-ahead-of-persisted
design, cross-tab clobber recheck, and the granularity-vs-min-timeout
invariant.

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
…le blind spot

resetSessionId previously left _enforceIdleTimeout armed, so a queued
fire after an external reset could call resetSessionId on an already-
nulled session. Clearing the timer in resetSessionId closes that latent
bug — surfaced more aggressively by the cross-tab refresh follow-up.

Also adds a one-line comment near ACTIVITY_TIMESTAMP_PERSIST_GRANULARITY_MS
that sibling tabs only observe persisted activity, not in-memory ticks
within the throttle window — a documented design tradeoff.

Generated-By: PostHog Code
Task-Id: 3946962f-7365-4b7b-8f5f-8f9309ed88c6
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