Skip to content

fix(dashboard): read Native Observe stats from Analytics Engine - #2772

Merged
riderx merged 7 commits into
mainfrom
fix/native-observe-stats-cf-dual-path
Jul 28, 2026
Merged

fix(dashboard): read Native Observe stats from Analytics Engine#2772
riderx merged 7 commits into
mainfrom
fix/native-observe-stats-cf-dual-path

Conversation

@riderx

@riderx riderx commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Wire POST /private/native_observe_stats to the same dual path as other private stats: APP_LOG for global Observe metrics, DEVICE_INFO for plugin versions, Postgres only when those bindings are absent.
  • Reuse day-chunked readUpdateDeliveryTimingEventsCF for Observe actions, aggregate durations/issues in JS, and keep release markers on Postgres deploy_history.
  • Add CF plugin-version query + unit coverage and register the AE SQL fixture.

Motivation (AI generated)

Production plugin stats land in Cloudflare Analytics Engine when APP_LOG/DEVICE_INFO are set. Observe still queried Postgres public.stats / public.devices, so the console chart stayed empty after the delivery-latency fix (#2770).

Business Impact (AI generated)

Native Observe becomes usable in production again, so customers can see launch/webview latency and crash signals without waiting on Postgres-backed stats that no longer receive this traffic.

Test Plan (AI generated)

  • Unit tests: bunx vitest run tests/native-observe-stats.unit.test.ts tests/analytics-engine-sql.unit.test.ts
  • Local without APP_LOG: Observe still returns Postgres-backed data
  • Prod/staging with APP_LOG: global Observe charts populate from AE
  • Prod/staging with DEVICE_INFO: plugins tab shows plugin_version breakdown
  • Release markers still appear from deploy_history

Generated with AI

Made with Cursor

Review in cubic

Summary by CodeRabbit

  • New Features
    • Native Observe statistics now include daily, action, version, and overview metrics.
    • Added duration percentiles (p50, p90, p99), issue counts, device counts, and timeout metrics.
    • Added reporting for native Observe plugin versions with associated device counts.
  • Bug Fixes
    • Improved handling of missing or invalid duration metadata and normalized event timing for consistent reporting.
    • More efficient stats processing for large event volumes.
  • Tests
    • Expanded unit coverage for duration parsing, CF-to-metrics aggregation, and plugin-version reporting.

Mirror the update-delivery dual path so prod Observe charts use APP_LOG/DEVICE_INFO instead of empty Postgres stats.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f04ecca4-908b-4035-a0d5-fe0e6553ad55

📥 Commits

Reviewing files that changed from the base of the PR and between f42df9d and 5ce45a2.

📒 Files selected for processing (2)
  • supabase/functions/_backend/private/native_observe_stats.ts
  • tests/native-observe-stats.unit.test.ts
📝 Walkthrough

Walkthrough

The PR adds Cloudflare-based native observe aggregation with duration parsing, percentile metrics, UTC-day chunking, release markers, and CF/SB path selection. It also adds Analytics Engine plugin-version reads, SQL fixtures, and unit tests.

Changes

Native observe statistics

Layer / File(s) Summary
Timing sample aggregation
supabase/functions/_backend/private/native_observe_stats.ts, tests/native-observe-stats.unit.test.ts
Cloudflare timing events are normalized, durations and percentiles are computed, and samples are aggregated into daily, action, version, and overview metrics with unit coverage.
CF/SB statistics response paths
supabase/functions/_backend/private/native_observe_stats.ts
CF statistics use UTC-day timing-event chunks and release markers, while Postgres logic remains in the SB reader and path selection uses APP_LOG.
Cloudflare plugin-version reader
supabase/functions/_backend/utils/cloudflare.ts, tests/helpers/collectAnalyticsEngineSqlFixtures.ts
Analytics Engine derives the latest plugin version per production device, maps query results, and adds generated SQL fixtures.

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

Sequence Diagram(s)

sequenceDiagram
  participant NativeObserveStats
  participant CloudflareTimingEvents
  participant ReleaseMarkers
  participant NativeObserveAggregation
  NativeObserveStats->>CloudflareTimingEvents: fetch timing events by UTC day
  NativeObserveStats->>ReleaseMarkers: fetch release markers
  CloudflareTimingEvents->>NativeObserveAggregation: normalize and fold samples
  ReleaseMarkers->>NativeObserveStats: provide release metadata
  NativeObserveAggregation-->>NativeObserveStats: return metric rows and percentiles
Loading

Possibly related PRs

  • Cap-go/capgo.app#2681: Adds related native observe plugin-version adoption metrics and response wiring.

Suggested labels: codex

Suggested reviewers: wcaleniewolny

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: reading Native Observe stats from Analytics Engine.
Description check ✅ Passed The description includes a solid summary and test plan, and the missing screenshots/checklist sections are non-critical here.
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.

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

@codspeed-hq

codspeed-hq Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing fix/native-observe-stats-cf-dual-path (5ce45a2) with main (23eb291)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@riderx
riderx marked this pull request as ready for review July 28, 2026 08:53
Comment thread supabase/functions/_backend/private/native_observe_stats.ts
@cursor
cursor Bot requested a review from WcaleNieWolny July 28, 2026 08:55

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: medium. Left a non-blocking comment — Cursor Bugbot found 1 unresolved issue (plugin total_devices undercount on the AE path), so this is not approved. Assigned a human reviewer for that finding.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: medium. Left a non-blocking comment — Cursor Bugbot found 1 unresolved issue, and this Analytics Engine dual-path change exceeds the low-risk approval threshold. Assigned a reviewer for human review.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

Sum DEVICE_INFO plugin-version device counts before slicing the top 12 so share denominators match Postgres.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread supabase/functions/_backend/private/native_observe_stats.ts Outdated
cursor[bot]
cursor Bot approved these changes Jul 28, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: medium. Left a non-blocking comment — Cursor Bugbot is clean on the latest commit, but this Analytics Engine dual-path stats change exceeds the low-risk approval threshold, so it is not approved. A human reviewer is already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

Aggregate each day chunk as it arrives so high-volume apps cannot pin large event arrays in the worker.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Risk: medium. Left a non-blocking comment — Cursor Bugbot is clean on the latest commit with prior findings resolved, but this Analytics Engine dual-path stats change exceeds the low-risk approval threshold, so it is not approved. A human reviewer is already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

cursor[bot]
cursor Bot approved these changes Jul 28, 2026
Comment thread supabase/functions/_backend/utils/cloudflare.ts Outdated
Comment thread supabase/functions/_backend/private/native_observe_stats.ts Outdated
Use a separate DEVICE_INFO total-devices query with a limited top-12 list, and cap folded events plus duration samples to keep worker memory/CPU bounded.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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: 5

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

Inline comments:
In `@supabase/functions/_backend/private/native_observe_stats.ts`:
- Around line 347-365: Replace the unbounded duration arrays and device-id Sets
in NativeObserveAggregateState, including dailyMap, actionMap, versionMap,
allDevices, and issueDevices, with bounded percentile summaries and approximate
distinct counters. Update the aggregation and percentile/reporting logic to use
these representations while preserving existing metrics and keeping memory usage
independent of event volume.
- Around line 652-675: The foldNativeObserveTimingEventsCFChunked loop currently
performs every daily Analytics Engine request serially without timeout or
partial-failure handling. Update this function to process chunks with bounded
concurrency, such as 4–6 requests in flight, fold each result as it resolves,
and apply an abort/timeout to each readUpdateDeliveryTimingEventsCF request
while preserving bounded memory usage.
- Around line 468-498: Update the stats aggregation around versionRows and
overviewRow to copy and sort each launchDurations and webviewDurations
collection once, then reuse the sorted arrays for both percentileCont calls.
Apply the same reuse within each version mapping callback and for the overview
state arrays, preserving all existing percentile values and row behavior.

In `@supabase/functions/_backend/utils/cloudflare.ts`:
- Around line 1343-1347: Update the device-state SELECT to retrieve
plugin_version, is_prod, and is_emulator from one deterministic latest event
rather than independent argMax aggregates; use a supported row-valued aggregate
or timestamp plus a unique secondary ordering key, and add a fixture covering
duplicate timestamps to verify the fields remain coherent.

In `@tests/native-observe-stats.unit.test.ts`:
- Line 94: Update the pure-helper test cases at the tests beginning “aggregates
CF observe samples…” and the additionally referenced test to use it.concurrent()
instead of it().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b0433f3-527e-4a62-a7fb-ab75c72f4646

📥 Commits

Reviewing files that changed from the base of the PR and between 23eb291 and e2431e0.

📒 Files selected for processing (4)
  • supabase/functions/_backend/private/native_observe_stats.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • tests/helpers/collectAnalyticsEngineSqlFixtures.ts
  • tests/native-observe-stats.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread supabase/functions/_backend/private/native_observe_stats.ts
Comment thread supabase/functions/_backend/private/native_observe_stats.ts
Comment thread supabase/functions/_backend/private/native_observe_stats.ts
Comment thread supabase/functions/_backend/utils/cloudflare.ts Outdated
Comment thread tests/native-observe-stats.unit.test.ts Outdated
@coderabbitai coderabbitai Bot added the codex label Jul 28, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f42df9d. Configure here.

Comment thread supabase/functions/_backend/private/native_observe_stats.ts Outdated
Comment thread supabase/functions/_backend/private/native_observe_stats.ts
Sort duration samples once, fetch day chunks with bounded concurrency, and run pure Observe helper tests concurrently.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d735a8c6-42a9-4071-a713-1095ffea7e86)

Drop Math.random reservoir replacement and fold day chunks from newest to oldest so caps truncate older days instead of recent ones.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_fc6eb9ff-21a9-4aac-9f41-9fc58cfd003e)

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
supabase/functions/_backend/private/native_observe_stats.ts (1)

666-689: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not silently return only the oldest 100,000 events.

The cursor advances from start, and Lines 679 and 687 stop folding once the cap is reached. High-volume apps will therefore receive incomplete totals and omit later days—often the most recent ones—without any truncation signal. Preserve complete range-level counters via Analytics Engine aggregation, or explicitly surface/handle truncation instead of reporting partial statistics as complete.

🤖 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 `@supabase/functions/_backend/private/native_observe_stats.ts` around lines 666
- 689, The chunked aggregation in foldNativeObserveTimingEventsCFChunked must
not silently report partial results when MAX_NATIVE_OBSERVE_EVENTS is reached.
Replace the event-cap truncation with Analytics Engine aggregation that
preserves complete range-level counters, or propagate an explicit truncation
state through the caller and response so consumers cannot treat incomplete
totals as complete; retain day-by-day folding for ranges that remain within the
limit.
🤖 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.

Inline comments:
In `@supabase/functions/_backend/private/native_observe_stats.ts`:
- Around line 324-332: Update pushDurationSample to track a per-target count of
all observed durations, including samples retained before MAX_DURATION_SAMPLES.
Increment the count for every value, then after the cap draw the replacement
index from [0, seen) rather than target.length + 1, replacing only when the
index is within the reservoir; preserve the existing cap and target-specific
sampling behavior.

---

Outside diff comments:
In `@supabase/functions/_backend/private/native_observe_stats.ts`:
- Around line 666-689: The chunked aggregation in
foldNativeObserveTimingEventsCFChunked must not silently report partial results
when MAX_NATIVE_OBSERVE_EVENTS is reached. Replace the event-cap truncation with
Analytics Engine aggregation that preserves complete range-level counters, or
propagate an explicit truncation state through the caller and response so
consumers cannot treat incomplete totals as complete; retain day-by-day folding
for ranges that remain within the limit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 82acacd8-d859-4bf4-a097-f1616978d6cc

📥 Commits

Reviewing files that changed from the base of the PR and between e2431e0 and f42df9d.

📒 Files selected for processing (3)
  • supabase/functions/_backend/private/native_observe_stats.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • tests/helpers/collectAnalyticsEngineSqlFixtures.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread supabase/functions/_backend/private/native_observe_stats.ts Outdated
Extract bucket/counter helpers so Sonar cognitive complexity and duplicated aggregation paths stay under the quality gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Jul 28, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_17c8b91c-4b55-4f80-8836-96dde517e03a)

@riderx
riderx merged commit 012b0f5 into main Jul 28, 2026
52 of 73 checks passed
@riderx
riderx deleted the fix/native-observe-stats-cf-dual-path branch July 28, 2026 09:20
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
6.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant