Skip to content

feat(core): expand telemetry to schema v2 with heartbeat and new signals#4933

Open
dlhck wants to merge 9 commits into
masterfrom
feat/core-telemetry-v2
Open

feat(core): expand telemetry to schema v2 with heartbeat and new signals#4933
dlhck wants to merge 9 commits into
masterfrom
feat/core-telemetry-v2

Conversation

@dlhck

@dlhck dlhck commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Expands the anonymous telemetry payload to schema v2, closing the largest signal gaps in the current collection: send cadence, order lifecycle, internationalization breadth, runtime environment, security posture, and strategy customization.

What's new

Cadence & payload metadata

  • Every payload now carries schemaVersion: 2, sendReason (startup | heartbeat) and uptimeSeconds.
  • In addition to the existing one-off startup send (5s after bootstrap), a repeating 24h heartbeat send is scheduled. The interval is unref()ed so it never keeps the process alive, is cleared on shutdown, and all existing guards (worker process, VENDURE_DISABLE_TELEMETRY, CI detection) are re-checked on every send. This makes uptime, retention and churn measurable — previously an installation only reported when it restarted.

runtime section (SystemInfoCollector)

  • runtimeType (node/bun/deno), packageManager (parsed from npm_config_user_agent, same approach as @vendure/create), tsNode, cpuCount, totalMemoryGb.

metrics.orders (DatabaseCollector)

  • Range-bucketed order lifecycle breakdown: placed, active, draft, placedLast30d, and byType (Regular/Seller/Aggregate). Total Order count is dominated by abandoned carts; this separates real trading activity and surfaces draft-order and multi-vendor adoption directly.

metrics.i18n (DatabaseCollector)

  • Distinct language and currency code counts across all Channels (union of default + available codes), plus a derived features.multiCurrency flag.

Config posture fields (ConfigCollector)

  • apiIntrospectionEnabled, apiPlaygroundEnabled, apiDebugEnabled, trustProxyEnabled, corsWildcardOrigin, tokenMethods, requireVerification, authDisabled, defaultSuperadminCredentials, cookieSecure, cookieSameSite, settingsStoreFieldCount.
  • customizedStrategies: dotted config paths (e.g. orderOptions.guestCheckoutStrategy) of single-strategy fields whose live class differs from defaultConfig — compared dynamically, so defaults stay the single source of truth.

Privacy

No raw values are collected anywhere: no hostnames, CORS origins, API paths, secrets, credentials, DB details, filesystem paths, or custom strategy class names. defaultSuperadminCredentials is a boolean comparison against the well-known defaults; the configured values are never read into the payload. Entity counts remain range-bucketed. Everything sensitive reduces to booleans, short enums, counts, or config paths.

All new payload fields are optional on the receiving side, so this remains backward/forward compatible with the collection endpoint.

Testing

  • 375 unit tests passing across the telemetry suite (+42 new), covering runtime user-agent parsing, posture booleans, defaultSuperadminCredentials, customizedStrategies (default vs customized), order-metric bucketing and byType, i18n counting, multiCurrency, and heartbeat scheduling (second send after 24h, interval unref'ed, cleared on shutdown, not scheduled when telemetry is disabled).
  • tsc --noEmit clean; eslint/prettier clean on all changed files.
  • Order/i18n queries use dialect-agnostic TypeORM (count(where), query-builder groupBy) — no raw SQL.

Breaking changes

None. Telemetry remains opt-out via VENDURE_DISABLE_TELEMETRY and disabled in CI; the only behavioral change is the added daily heartbeat send.

Checklist

  • Tests added for new behavior
  • No breaking changes

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • New Features
    • Telemetry now reports a richer startup and heartbeat payload, including runtime details, schema version, uptime, and additional configuration and feature summaries.
    • Added broader telemetry coverage for orders, localization, and security posture insights.
  • Bug Fixes
    • Improved telemetry reliability by handling partial failures more gracefully and avoiding missed data when some metrics can’t be collected.
    • Refined timing behavior so telemetry sends occur after startup and then at regular intervals.
  • Documentation
    • Updated telemetry guides and API references to match the expanded telemetry data and privacy details.

Adds schemaVersion 2 to the telemetry payload along with a repeating 24h
heartbeat send (sendReason startup/heartbeat), process uptime, and a
runtime section (runtime type, package manager, ts-node, cpu/memory).
Extends DB metrics with order lifecycle and i18n breadth, adds API and
security posture flags plus dotted customized-strategy paths to config,
and a multiCurrency feature flag.
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
vendure-storybook Ready Ready Preview, Comment Jul 8, 2026 11:46am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63a2511a-a7c8-4a8a-844a-5822c7d25d7e

📥 Commits

Reviewing files that changed from the base of the PR and between 1693fd8 and da5c7f8.

📒 Files selected for processing (2)
  • docs/manifest.json
  • packages/core/src/telemetry/collectors/config.collector.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/manifest.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/telemetry/collectors/config.collector.ts

Walkthrough

This PR expands anonymous telemetry: new SendReason and TelemetryRuntime types, config collector fields for API/security posture and customized strategy detection, database collector order/i18n metrics with chunked entity counting, a multiCurrency feature flag, runtime detection in system-info, and a reworked telemetry service supporting delayed startup and recurring jittered heartbeat sends. Documentation and manifest timestamps were regenerated accordingly.

Changes

Telemetry expansion

Layer / File(s) Summary
Telemetry type definitions
packages/core/src/telemetry/telemetry.types.ts
Adds SendReason, TelemetryRuntime, TelemetryI18nMetrics, TelemetryOrderMetrics; extends TelemetryEntityMetrics, TelemetryConfig, TelemetryFeatures, and TelemetryPayload (required schemaVersion, optional sendReason, uptimeSeconds, runtime).
Config collector: posture and strategy detection
packages/core/src/telemetry/collectors/config.collector.ts, config.collector.spec.ts
Adds SINGLE_STRATEGY_PATHS, new API/auth/CORS/cookie/settings-store telemetry fields, getCustomizedStrategies() comparing live vs defaultConfig strategies, and corresponding tests.
Database collector: order and i18n metrics
packages/core/src/telemetry/collectors/database.collector.ts, database.collector.spec.ts
Adds concurrent order lifecycle metrics, per-type bucketing, i18n language/currency counts, safeBucket(), and switches entity counting to bounded countInChunks(); adds matching tests.
Features collector: multiCurrency flag
packages/core/src/telemetry/collectors/features.collector.ts, features.collector.spec.ts
collect() accepts optional currencyCount and derives multiCurrency; adds tests for true/false/undefined cases.
System-info collector: runtime detection
packages/core/src/telemetry/collectors/system-info.collector.ts, system-info.collector.spec.ts
Adds runtime field with helpers for runtime type, package manager, ts-node detection, CPU count, and memory GB; adds runtime spec coverage.
Telemetry service: startup and heartbeat scheduling
packages/core/src/telemetry/telemetry.service.ts, telemetry.service.spec.ts
Introduces delayed startup send and self-rescheduling jittered heartbeat sends, reason-aware sendTelemetry/collectPayload, timer cleanup on shutdown; expands tests.
Regenerated docs metadata
docs/docs/reference/typescript-api/telemetry/telemetry-service.mdx, docs/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.mdx, docs/docs/guides/developer-guide/telemetry/index.mdx, docs/manifest.json
Updates generated sourceLine/lastModified metadata and expands telemetry documentation with timing, runtime, order/i18n, and security posture details.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TelemetryService
  participant FeaturesCollector
  participant SystemInfoCollector
  participant DatabaseCollector
  participant Endpoint

  TelemetryService->>TelemetryService: schedule delayed sendTelemetry('startup')
  TelemetryService->>DatabaseCollector: collect entity/order/i18n metrics
  DatabaseCollector-->>TelemetryService: metrics (with currency count)
  TelemetryService->>SystemInfoCollector: collect runtime info
  SystemInfoCollector-->>TelemetryService: runtime data
  TelemetryService->>FeaturesCollector: collect(config, currencyCount)
  FeaturesCollector-->>TelemetryService: features (multiCurrency)
  TelemetryService->>Endpoint: POST payload (schemaVersion, sendReason=startup)
  TelemetryService->>TelemetryService: scheduleHeartbeat() (jittered, self-rescheduling)
  loop every ~24h
    TelemetryService->>Endpoint: POST payload (sendReason=heartbeat)
  end
Loading

Possibly related PRs

  • vendurehq/vendure#4192: The main PR's telemetry updates build directly on the anonymous telemetry module introduced in this earlier PR.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main telemetry schema v2 heartbeat expansion.
Description check ✅ Passed The description thoroughly summarizes the changes and breaking changes, though it omits the related issue and some checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/core-telemetry-v2

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.

@vendure-developer-hub

vendure-developer-hub Bot commented Jul 8, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Dashboard Preview: https://admin-dashboard-eumn087p5-vendure.vercel.app

@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.

🧹 Nitpick comments (3)
packages/core/src/telemetry/collectors/system-info.collector.ts (1)

51-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment claims parity with helpers.ts but the fallback behavior differs.

The comment says this "mirrors" packages/create/src/helpers.ts, but that helper defaults unrecognized/absent user-agents to 'npm', while this method returns 'unknown' in the same case. Not a bug (an explicit "unknown" is arguably more honest for telemetry), but the comment is misleading for future maintainers.

📝 Suggested comment fix
-            // Mirrors the parsing in packages/create/src/helpers.ts
+            // Parses similarly to packages/create/src/helpers.ts, but reports
+            // 'unknown' (rather than defaulting to 'npm') when unrecognized.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/telemetry/collectors/system-info.collector.ts` around lines
51 - 62, Update the inline note in system-info.collector.ts near
getPackageManager so it no longer claims parity with
packages/create/src/helpers.ts if the fallback behavior differs; either remove
the “Mirrors” wording or clarify that this method intentionally diverges by
returning unknown for unrecognized or absent user agents. Keep the
implementation in getPackageManager unchanged and make the comment accurately
describe the current telemetry behavior.
packages/core/src/telemetry/collectors/config.collector.ts (1)

179-188: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use the shared superadmin defaults here
getDefaultSuperadminCredentials() should compare against SUPER_ADMIN_USER_IDENTIFIER and SUPER_ADMIN_USER_PASSWORD from @vendure/common/lib/shared-constants, not hardcoded 'superadmin' literals. That keeps this telemetry flag aligned with defaultConfig and prevents it from drifting if the defaults ever change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/telemetry/collectors/config.collector.ts` around lines 179
- 188, The getDefaultSuperadminCredentials() method is using hardcoded
'superadmin' literals instead of the shared superadmin defaults. Update the
comparison in config.collector.ts to use SUPER_ADMIN_USER_IDENTIFIER and
SUPER_ADMIN_USER_PASSWORD from `@vendure/common/lib/shared-constants`, keeping the
telemetry check aligned with defaultConfig and preventing drift if the defaults
change.
packages/core/src/telemetry/collectors/database.collector.ts (1)

82-82: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Drop the unnecessary as any cast on the draft-order count. Order.state already uses OrderState, and 'Draft' is a valid member; removing the cast keeps the type check intact and avoids hiding typos or future state changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/telemetry/collectors/database.collector.ts` at line 82, The
draft-order count in database.collector.ts is using an unnecessary type cast
that hides type checking. Update the count logic in the collector method that
calls repo.count for the Draft state by removing the as any cast and using the
existing OrderState type directly, so the Order.state filter remains type-safe
and continues to catch invalid state values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/telemetry/collectors/config.collector.ts`:
- Around line 179-188: The getDefaultSuperadminCredentials() method is using
hardcoded 'superadmin' literals instead of the shared superadmin defaults.
Update the comparison in config.collector.ts to use SUPER_ADMIN_USER_IDENTIFIER
and SUPER_ADMIN_USER_PASSWORD from `@vendure/common/lib/shared-constants`, keeping
the telemetry check aligned with defaultConfig and preventing drift if the
defaults change.

In `@packages/core/src/telemetry/collectors/database.collector.ts`:
- Line 82: The draft-order count in database.collector.ts is using an
unnecessary type cast that hides type checking. Update the count logic in the
collector method that calls repo.count for the Draft state by removing the as
any cast and using the existing OrderState type directly, so the Order.state
filter remains type-safe and continues to catch invalid state values.

In `@packages/core/src/telemetry/collectors/system-info.collector.ts`:
- Around line 51-62: Update the inline note in system-info.collector.ts near
getPackageManager so it no longer claims parity with
packages/create/src/helpers.ts if the fallback behavior differs; either remove
the “Mirrors” wording or clarify that this method intentionally diverges by
returning unknown for unrecognized or absent user agents. Keep the
implementation in getPackageManager unchanged and make the comment accurately
describe the current telemetry behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e532066d-a5f3-4b1d-aa4e-7e031246f2d1

📥 Commits

Reviewing files that changed from the base of the PR and between 5820363 and f117d7e.

📒 Files selected for processing (14)
  • docs/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.mdx
  • docs/docs/reference/typescript-api/telemetry/telemetry-service.mdx
  • docs/manifest.json
  • packages/core/src/telemetry/collectors/config.collector.spec.ts
  • packages/core/src/telemetry/collectors/config.collector.ts
  • packages/core/src/telemetry/collectors/database.collector.spec.ts
  • packages/core/src/telemetry/collectors/database.collector.ts
  • packages/core/src/telemetry/collectors/features.collector.spec.ts
  • packages/core/src/telemetry/collectors/features.collector.ts
  • packages/core/src/telemetry/collectors/system-info.collector.spec.ts
  • packages/core/src/telemetry/collectors/system-info.collector.ts
  • packages/core/src/telemetry/telemetry.service.spec.ts
  • packages/core/src/telemetry/telemetry.service.ts
  • packages/core/src/telemetry/telemetry.types.ts

dlhck and others added 4 commits July 8, 2026 11:54
Update the telemetry disclosure docs and TelemetryService docblock to cover
the 24h heartbeat and new data categories. Allowlist order-type keys against
the OrderType enum so arbitrary varchar values cannot leak. Replace the fixed
heartbeat interval with a self-rescheduling, jittered timeout and bound the
entity-count sweep concurrency. Parallelize the order sub-queries, run order
and i18n collection concurrently, return undefined for an empty orders result,
detect the deprecated root-level entityIdStrategy in customizedStrategies, and
extract a named TelemetryI18nMetrics interface.

@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.

🧹 Nitpick comments (2)
docs/docs/guides/developer-guide/telemetry/index.mdx (2)

32-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider moving "Process uptime" under "Runtime and Hardware Shape".

"Process uptime in seconds at the time of the event" (line 32) is listed under "Version Information," but the newly added "Runtime and Hardware Shape" section (lines 34–39) is a more natural home for this metric alongside runtime type, CPU count, and memory.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/guides/developer-guide/telemetry/index.mdx` around lines 32 - 39,
Move the “Process uptime in seconds at the time of the event” entry from the
Version Information list into the Runtime and Hardware Shape section so it sits
with the runtime/CPU/memory metrics. Update the telemetry docs in the developer
guide index accordingly, keeping the existing wording but relocating the bullet
to the new section.

109-115: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the distinction between "order data" and "Order Lifecycle Metrics".

Line 110 states "Customer data, order data, or any business data" is not collected, while lines 71–73 describe "Order Lifecycle Metrics" (range-bucketed counts). A reader may find this contradictory. Consider adding a brief parenthetical to line 110, e.g., "Customer data, order data (actual order contents), or any business data" to make clear that only aggregate counts are collected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/guides/developer-guide/telemetry/index.mdx` around lines 109 - 115,
Clarify the telemetry exclusions in the docs by distinguishing actual order
contents from aggregate metrics. Update the bullets in the telemetry guide
around the “Customer data, order data, or any business data” item so it
explicitly says “order data” means actual order contents, not the aggregated
“Order Lifecycle Metrics” described earlier. Keep the wording aligned with the
existing telemetry section and use the same list item block so readers don’t
read the metrics description as contradictory.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/docs/guides/developer-guide/telemetry/index.mdx`:
- Around line 32-39: Move the “Process uptime in seconds at the time of the
event” entry from the Version Information list into the Runtime and Hardware
Shape section so it sits with the runtime/CPU/memory metrics. Update the
telemetry docs in the developer guide index accordingly, keeping the existing
wording but relocating the bullet to the new section.
- Around line 109-115: Clarify the telemetry exclusions in the docs by
distinguishing actual order contents from aggregate metrics. Update the bullets
in the telemetry guide around the “Customer data, order data, or any business
data” item so it explicitly says “order data” means actual order contents, not
the aggregated “Order Lifecycle Metrics” described earlier. Keep the wording
aligned with the existing telemetry section and use the same list item block so
readers don’t read the metrics description as contradictory.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b18f4349-8e1d-4dc9-aafb-2ec8fbed4617

📥 Commits

Reviewing files that changed from the base of the PR and between f117d7e and 1693fd8.

📒 Files selected for processing (11)
  • docs/docs/guides/developer-guide/telemetry/index.mdx
  • docs/docs/reference/typescript-api/telemetry/telemetry-service.mdx
  • docs/manifest.json
  • packages/core/src/telemetry/collectors/config.collector.spec.ts
  • packages/core/src/telemetry/collectors/config.collector.ts
  • packages/core/src/telemetry/collectors/database.collector.spec.ts
  • packages/core/src/telemetry/collectors/database.collector.ts
  • packages/core/src/telemetry/collectors/system-info.collector.spec.ts
  • packages/core/src/telemetry/telemetry.service.spec.ts
  • packages/core/src/telemetry/telemetry.service.ts
  • packages/core/src/telemetry/telemetry.types.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/manifest.json
  • docs/docs/reference/typescript-api/telemetry/telemetry-service.mdx
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/core/src/telemetry/collectors/system-info.collector.spec.ts
  • packages/core/src/telemetry/collectors/config.collector.ts
  • packages/core/src/telemetry/collectors/config.collector.spec.ts
  • packages/core/src/telemetry/collectors/database.collector.spec.ts
  • packages/core/src/telemetry/collectors/database.collector.ts
  • packages/core/src/telemetry/telemetry.types.ts
  • packages/core/src/telemetry/telemetry.service.spec.ts

dlhck added 2 commits July 8, 2026 13:43
The telemetry config collector deliberately reads the deprecated
adminApiPlayground/shopApiPlayground options and the deprecated root-level
entityIdStrategy: measuring remaining adoption of deprecated options is what
tells the core team when they can be removed. Annotate these reads with
// NOSONAR so SonarCloud does not flag rule S1874, with a comment explaining
the intent.
@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

private async sendTelemetry(): Promise<void> {
const payload = await this.collectPayload();
private scheduleHeartbeat(): void {
const delay = HEARTBEAT_INTERVAL_MS + Math.floor(Math.random() * HEARTBEAT_MAX_JITTER_MS);

@michaelbromley michaelbromley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The design is solid and the privacy engineering is careful — I traced the customizedStrategies comparison through mergeConfig/simpleDeepClone and it's genuinely sound (un-overridden strategy instances are kept by reference, and comparing by class name correctly handles plugins re-instantiating a default strategy). No privacy leaks found: corsWildcardOrigin only ever reduces to a boolean, tokenMethod is a closed union so nothing can leak via stringification, and the superadmin-credentials check never puts the values anywhere. Failure isolation and the guard re-checking claims both hold up.

Two must-fixes though, both cheap:

  1. Order-metrics queries are unindexed full scans on a table that can have millions of rows, and they fire on the startup send too — see inline comment on database.collector.ts.
  2. onApplicationBootstrap() isn't idempotent, and a second call creates a heartbeat chain that shutdown can't cancel — see inline comments on telemetry.service.ts and the spec.

Nits (non-blocking):

  • SINGLE_STRATEGY_PATHS omits authOptions.entityAccessControlStrategy and orderOptions.activeOrderStrategy — the latter especially is commonly customized and worth adding (inline comment).
  • collectPayload() awaits installationIdCollector.collect() and databaseCollector.collect() sequentially even though they're independent — could be Promise.all'd for consistency with the parallelization used elsewhere in this PR. Immaterial at a 24h cadence.
  • The diff picks up an unrelated line-number drift in docs/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.mdx (sourceLine="155""156") — harmless doc-generator side effect, but worth a note in the PR description so reviewers aren't puzzled by an S3 doc in a telemetry PR.

Test quality overall is strong — config sections that throw on access, minified class names, rogue OrderType values, jitter timing, unref, shutdown-clears-timer are all pinned. The one gap is the double-bootstrap test (inline comment).

Comment on lines +81 to +89
// A handful of small, indexed queries — run them in parallel (unlike
// the larger entity-count sweep).
const [placed, active, draft, placedLast30d, byType] = await Promise.all([
this.safeBucket(() => repo.count({ where: { orderPlacedAt: Not(IsNull()) } })),
this.safeBucket(() => repo.count({ where: { active: true } })),
this.safeBucket(() => repo.count({ where: { state: 'Draft' as any } })),
this.safeBucket(() => repo.count({ where: { orderPlacedAt: MoreThanOrEqual(since) } })),
this.collectOrdersByType(repo),
]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Must fix: the comment says "small, indexed queries", but Order has no index on active, state, or type — only code, orderPlacedAt, aggregateOrder and customer are indexed (see order.entity.ts). So count({ where: { active: true } }), count({ where: { state: 'Draft' } }) and the GROUP BY o.type in collectOrdersByType are full-scan-class queries on what is typically the largest table in a production shop (abandoned carts easily push it into the millions).

And this isn't just a heartbeat cost: collectOrderMetrics() runs on the startup send too, 5 seconds after bootstrap — exactly when a freshly-deployed server is least able to absorb an unplanned scan of its biggest table.

Options, roughly in order of preference:

  • collect order metrics on heartbeat sends only, skipping them for startup;
  • gate collection behind the already-collected total Order count (skip when the bucket is 100k+);
  • add indexes on active and state via a migration (heavier, affects all installs).

Comment on lines 91 to +97
this.delayTimeout = setTimeout(() => {
this.delayTimeout = undefined;
this.sendTelemetry().catch(() => {
this.sendTelemetry('startup').catch(() => {
// Silently ignore all errors
});
}, 5000);
this.scheduleHeartbeat();
}, STARTUP_DELAY_MS);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Must fix: onApplicationBootstrap() isn't idempotent — this assigns a fresh timeout without clearing any existing one, and scheduleHeartbeat() does the same with this.heartbeatTimeout. If the hook ever fires twice on the same instance (module double-imported without @Global(), hybrid multi-app setups, test harnesses re-triggering lifecycle hooks), you get two startup sends and — worse — two independent self-rescheduling heartbeat chains. Since each chain reassigns this.heartbeatTimeout on every reschedule, onApplicationShutdown() only holds a reference to whichever timer was scheduled last: it can cancel one chain, and the other keeps sending duplicate heartbeats for the life of the process with no way to stop it.

Cheap fix: a hasBootstrapped guard here, or clearTimeout any existing ref before reassigning in both places.

Relatedly, the existing spec 'handles double onApplicationBootstrap call without error' (telemetry.service.spec.ts, ~line 663) is too weak to catch this: toHaveBeenCalled() passes whether one send happens or two. Asserting toHaveBeenCalledTimes(1) there would fail against the current implementation — and would pin the idempotency fix once it's in.

Comment on lines +14 to +51
const SINGLE_STRATEGY_PATHS: Record<string, string[]> = {
authOptions: [
'sessionCacheStrategy',
'passwordHashingStrategy',
'passwordValidationStrategy',
'verificationTokenStrategy',
'adminApiKeyStrategy',
'shopApiKeyStrategy',
'customerChannelAssignmentStrategy',
],
assetOptions: ['assetNamingStrategy', 'assetStorageStrategy', 'assetPreviewStrategy'],
catalogOptions: [
'productVariantPriceSelectionStrategy',
'productVariantPriceCalculationStrategy',
'productVariantPriceUpdateStrategy',
'stockDisplayStrategy',
'stockLocationStrategy',
],
orderOptions: [
'orderItemPriceCalculationStrategy',
'stockAllocationStrategy',
'mergeStrategy',
'checkoutMergeStrategy',
'orderCodeStrategy',
'orderByCodeAccessStrategy',
'changedPriceHandlingStrategy',
'orderLineDiscountDistributionStrategy',
'orderPlacedStrategy',
'orderSellerStrategy',
'guestCheckoutStrategy',
],
taxOptions: ['taxZoneStrategy', 'taxLineCalculationStrategy', 'orderTaxCalculationStrategy'],
entityOptions: ['entityIdStrategy', 'moneyStrategy', 'slugStrategy'],
importExportOptions: ['assetImportStrategy'],
jobQueueOptions: ['jobQueueStrategy', 'jobBufferStorageStrategy'],
schedulerOptions: ['schedulerStrategy'],
systemOptions: ['cacheStrategy', 'instrumentationStrategy'],
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: this allowlist omits two real single-strategy fields from default-config.ts: authOptions.entityAccessControlStrategy and orderOptions.activeOrderStrategy. Not a correctness bug — just silent under-coverage. activeOrderStrategy in particular is a commonly-customized field that seems worth the signal.

Comment on lines 145 to 146
const installationId = await this.installationIdCollector.collect();
const databaseInfo = await this.databaseCollector.collect();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: these two collects are independent and could be Promise.all'd — immaterial at this cadence, just inconsistent with the parallelization pattern this PR uses in the collectors themselves.

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