Skip to content

fix(cron): keep global stats snapshots running#2484

Open
riderx wants to merge 5 commits into
mainfrom
codex/fix-global-stats-logsnag-timeout
Open

fix(cron): keep global stats snapshots running#2484
riderx wants to merge 5 commits into
mainfrom
codex/fix-global-stats-logsnag-timeout

Conversation

@riderx

@riderx riderx commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Move logsnag_insights global stats work out of one monolithic request and into independent admin_stats shard messages.
  • Dispatch shards as distinct queue HTTP calls via targets like logsnag_insights_core, logsnag_insights_usage, and logsnag_insights_revenue.
  • Split global stats writes into bounded shards: core, usage, revenue, plugins, builds, retention, paid_products, ltv, and delayed notifications.
  • Add snapshot completion tracking with completed_shards, including notification completion, so retries do not skip notification delivery or resend completed snapshots.
  • Store snapshot orgs count and use snapshot-bounded revenue, active-app, usage, device, plugin, and build windows for replayed dates.
  • Preserve retry behavior by reserving a delayed dispatcher retry before 202, cancelling it only after durable shard queueing, and propagating retry-cancel failures.

Motivation (AI generated)

Cloudflare logs showed /triggers/logsnag_insights switching from 200 ok to 0 canceled around the queue HTTP timeout. The slow work was the full global stats snapshot refresh inside one LogSnag-named trigger. Splitting the queue into separate HTTP shard calls lets Cloudflare retry only the failed metric group and avoids relying on one long Worker invocation.

Business Impact (AI generated)

Restores Capgo admin/global metrics freshness without increasing queue timeouts. Daily snapshots can make partial progress, recover from one slow metric group, and avoid sending LogSnag/tracking events with incomplete, stale, or mixed-date values.

Test Plan (AI generated)

  • bun test tests/cloudflare-datetime.unit.test.ts tests/logsnag-insights-revenue.unit.test.ts
  • bun lint (passes with existing unrelated JSDoc warning in src/services/compatibilityEvents.ts)
  • bun lint:backend
  • bun run cli:typecheck
  • bun run typecheck:backend
  • bun run typecheck:frontend
  • bash scripts/check-supabase-migration-order.sh
  • git diff --check
  • Commit hook: bun run cli:typecheck && bun run typecheck:backend && bun run typecheck:frontend

Generated with AI

Summary by CodeRabbit

  • New Features
    • Rolled out sharded global insights computation with persisted snapshot state and queued, background execution (requests return 202).
    • Added per-shard LogSnag endpoints for core, usage, revenue, plugins, builds, retention, paid products, LTV, and notifications.
  • Bug Fixes
    • Made “last month” analytics deterministic and snapshot-based for consistent reporting.
    • Improved accuracy across usage/revenue/build metrics and strengthened snapshot-readiness gating for notification dispatch.
    • Added stricter failure handling options for insight and tracking delivery.
  • Tests
    • Expanded unit coverage for sharded scheduling, retry/idempotency, snapshot windows, and error cases.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces the monolithic LogSnag global-stats computation with a pgmq-backed sharded snapshot pipeline persisted in public.global_stats. Introduces 9 named shards with completion tracking, parametrizes Cloudflare analytics time windows via referenceDate, adds strict error propagation to tracking and LogSnag utilities, and registers per-shard HTTP routes in the triggers entrypoint.

Changes

Sharded Global Stats Pipeline

Layer / File(s) Summary
Database schema and shard type definitions
supabase/migrations/20260615112920_add_global_stats_completed_shards.sql, supabase/functions/_backend/triggers/logsnag_insights.ts
global_stats gains orgs (bigint) and completed_shards (jsonb) columns with backfill; old GlobalStats interface removed and new shard name list/types and import wiring added.
Shard/retry infrastructure and payload normalization
supabase/functions/_backend/triggers/logsnag_insights.ts
Defines shard lists/types, required-vs-optional shards, retry limits, and delay constants. Adds helpers to normalize retry counts, date/shard payload values, pgmq message builders, and functions to reserve/cancel dispatcher retries.
Parametrized Cloudflare analytics time windows
supabase/functions/_backend/utils/cloudflare.ts, tests/cloudflare-datetime.unit.test.ts
Exports getLastMonthAnalyticsWindow(referenceDate?) returning SQL start/end expressions for a rolling 30-day window. Propagates referenceDate to readActiveAppsCF, readLastMonthUpdatesCF, readLastMonthDevicesCF, readLastMonthDevicesByPlatformCF, and getPluginBreakdownCF. New unit tests assert deterministic SQL expressions for a fixed snapshot date and verify time-of-day preservation.
Revenue calculation with snapshot-window accuracy
supabase/functions/_backend/triggers/logsnag_insights.ts
calculateRevenue accepts optional referenceDate to constrain subscription rows by snapshot-end timestamp. Refactors to use price_id→(plan, billing-period) mapping. Anchors build stats rolling window and countDemoSeededApps ranges to snapshot day instead of Date.now().
Strict error propagation for tracking and LogSnag
supabase/functions/_backend/utils/logsnag.ts, supabase/functions/_backend/utils/tracking.ts
logsnagInsights accepts LogsnagInsightsOptions with strict flag; missing config or HTTP failures throw when strict. runTrackedCall/executeTracking propagate strict from SendEventToTrackingOptions to both logsnag and posthog providers.
Snapshot persistence and sharded dispatch orchestration
supabase/functions/_backend/triggers/logsnag_insights.ts
Ensures global_stats row existence per date_id, resets and tracks completed_shards, patches snapshot fields per shard, determines missing required shards, conditionally queues notification retries, and enqueues shard jobs into admin_stats with per-shard delays.
Per-shard runner implementations
supabase/functions/_backend/triggers/logsnag_insights.ts
Implements all 9 shard runners (core, usage, revenue, plugins, builds, retention, paid_products, ltv, notifications). Notifications are gated on required-shard completion. Exports logsnagInsightsShardApps and updates main dispatcher to return HTTP 202.
Per-shard HTTP route registration
supabase/functions/triggers/index.ts, cloudflare_workers/api/index.ts
Imports logsnagInsightsShardApps and registers /logsnag_insights_* routes for all 9 shards in both triggers and Cloudflare worker entrypoints.
Test coverage for sharding, scheduling, and strict mode
tests/logsnag-insights-revenue.unit.test.ts, tests/cloudflare-datetime.unit.test.ts
Adds tests for snapshot date replay, required-shard detection, plan bucket/retry count normalization, message building, EdgeRuntime background scheduling with retry cancellation, and strict-mode failure propagation via env-var overrides.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(70, 130, 180, 0.5)
    Note over Trigger,pgmq: Dispatcher invocation
    Trigger->>logsnag_insights app: POST /logsnag_insights
    logsnag_insights app->>pgmq: reserve next retry message
    logsnag_insights app->>EdgeRuntime: waitUntil(scheduleLogsnagInsightsUpdate)
    logsnag_insights app-->>Trigger: HTTP 202
  end
  rect rgba(60, 179, 113, 0.5)
    Note over EdgeRuntime,global_stats: Background shard scheduling
    EdgeRuntime->>global_stats: upsert row for date_id, reset completed_shards
    EdgeRuntime->>global_stats: read completed_shards
    EdgeRuntime->>EdgeRuntime: compute missing required shards
    loop each missing shard
      EdgeRuntime->>pgmq: enqueue logsnag_insights_<shard> with delay
    end
    EdgeRuntime->>pgmq: cancel reserved retry (on success)
  end
  rect rgba(210, 105, 30, 0.5)
    Note over pgmq,global_stats: Per-shard execution
    pgmq->>ShardRunner: POST /logsnag_insights_<shard>
    ShardRunner->>CloudflareAnalytics: query with referenceDate window
    ShardRunner->>SupabaseRPC: query plan/org counts
    ShardRunner->>global_stats: patch shard fields
    ShardRunner->>global_stats: mark shard complete in completed_shards
    ShardRunner->>TrackingLogSnag: send events (strict mode)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(cron): keep global stats snapshots running' directly relates to the main objective: refactoring the LogSnag global stats workflow to prevent timeouts and keep snapshots running reliably by splitting work into independent shard messages.
Description check ✅ Passed The description includes an AI-generated summary with motivation and business impact, plus a comprehensive test plan with executed checks, but lacks explicit manual testing steps and documentation update checklist items from the template.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@codspeed-hq

codspeed-hq Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing codex/fix-global-stats-logsnag-timeout (788271a) with main (483e430)

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 force-pushed the codex/fix-global-stats-logsnag-timeout branch from 5309d18 to 2eab7d2 Compare June 11, 2026 11:30

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2eab7d2668

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 2eab7d2 to 6599525 Compare June 11, 2026 11:47

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6599525296

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 6599525 to ab2c638 Compare June 11, 2026 12:03
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from ab2c638 to aa7b5d9 Compare June 11, 2026 13:30

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa7b5d9c4f

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from aa7b5d9 to 350bfaf Compare June 11, 2026 13:46
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 350bfaf to 83ff4db Compare June 11, 2026 14:08

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83ff4db9cd

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 83ff4db to 3beacac Compare June 11, 2026 14:24

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3beacac22d

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 3beacac to a3ec396 Compare June 11, 2026 14:35

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3ec396759

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from a3ec396 to 8d0e6d0 Compare June 11, 2026 14:58

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d0e6d01b2

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 8d0e6d0 to bf7017c Compare June 11, 2026 15:09

@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

🤖 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 `@tests/logsnag-insights-revenue.unit.test.ts`:
- Around line 246-248: The `.then()` callback used on line 247 for handling the
app request response violates the async/await requirement for TypeScript files.
Replace the Promise.resolve().then() pattern with async/await syntax by
converting the callback that extracts response.status into an async arrow
function or IIFE that awaits the app.request call and returns the status
property, ensuring it integrates properly with the Promise.race() call.
🪄 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: 27d51e83-bf0f-4fe7-b9a2-24fb99b37129

📥 Commits

Reviewing files that changed from the base of the PR and between ab6a886 and 2a8d7f7.

📒 Files selected for processing (8)
  • supabase/functions/_backend/triggers/logsnag_insights.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • supabase/functions/_backend/utils/logsnag.ts
  • supabase/functions/_backend/utils/tracking.ts
  • supabase/functions/triggers/index.ts
  • supabase/migrations/20260611160348_add_global_stats_completed_shards.sql
  • tests/cloudflare-datetime.unit.test.ts
  • tests/logsnag-insights-revenue.unit.test.ts
🔗 Linked repositories identified

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

  • Cap-go/capacitor-updater (manual)

Comment thread tests/logsnag-insights-revenue.unit.test.ts

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

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/triggers/logsnag_insights.ts (1)

1230-1304: 🛠️ Refactor suggestion | 🟠 Major

Replace .then() chains with async/await in TS shard logic.

The Promise pipelines in these ranges rely on chained .then() callbacks; TypeScript code must use async/await style per coding guidelines.

🤖 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/triggers/logsnag_insights.ts` around lines 1230 -
1304, The Promise.all() array contains multiple Promise chains using .then()
callbacks instead of async/await style, which violates the TypeScript coding
guidelines. Convert all .then() chains in the array (used with countAllApps,
countAllUpdates, countAllUpdatesExternal, users query, orgs query,
getGithubStars, get_customer_counts RPC, paying_orgs_for_conversion query,
count_all_onboarded RPC, count_all_need_upgrade RPC, count_all_plans_v2 RPC, and
readActiveAppsCF) to use async/await syntax by either extracting each promise
chain into separate async helper functions or wrapping the Promise.all() logic
in an async IIFE, ensuring error handling and return values remain consistent.

Source: Coding guidelines

♻️ Duplicate comments (1)
tests/logsnag-insights-revenue.unit.test.ts (1)

247-247: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace .then() with async/await in this TypeScript test path (Line 247).

This line still uses a .then() callback; the repository TypeScript guideline requires async/await for promise handling.

Suggested patch
       const requestTimeoutMs = 500
+      const responseStatusPromise = (async () => {
+        const response = await app.request('http://localhost/', { method: 'POST' })
+        return response.status
+      })()
       const result = await Promise.race([
-        Promise.resolve(app.request('http://localhost/', { method: 'POST' })).then((response: Response) => response.status),
+        responseStatusPromise,
         new Promise<'timeout'>(resolve => setTimeout(() => resolve('timeout'), requestTimeoutMs)),
       ])

As per coding guidelines, TypeScript promise handling should use async/await instead of .then() callbacks.

🤖 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 `@tests/logsnag-insights-revenue.unit.test.ts` at line 247, The
Promise.resolve().then() chain at the app.request call is using callback-based
promise handling instead of async/await syntax required by the repository's
TypeScript guidelines. Refactor this line to use async/await pattern by moving
it into an async function context, awaiting the app.request call, and then
accessing the response.status property directly on the awaited result instead of
chaining with .then().

Source: Coding guidelines

🤖 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/triggers/logsnag_insights.ts`:
- Around line 1387-1417: The local variables in the Promise.all destructuring
assignment use snake_case naming (updates_last_month, devices_last_month,
devices_by_platform, registers_today, bundle_storage_gb, success_rate,
demo_apps_created) which violates TypeScript/JavaScript conventions that require
camelCase for local identifiers. Convert all these variable names to camelCase
(updatesLastMonth, devicesLastMonth, devicesByPlatform, registersToday,
bundleStorageGb, successRate, demoAppsCreated) in the destructuring assignment,
and then update all corresponding references to these variables throughout the
block, including in the updateGlobalStatsSnapshot function call where these
variables are passed as object properties. Note: keep the object property keys
in the updateGlobalStatsSnapshot call as snake_case to match the database column
names.
- Line 1813: The current instance creation in logsnag_insights.ts uses `new
Hono<MiddlewareKeyVariables>()` which is inconsistent with the guideline
recommending `createHono` from `utils/hono.ts`. However, since this pattern is
used across nearly all backend files (triggers, plugins, private directories)
and only `admin_credits.ts` uses `createHono`, a decision is needed: either
refactor the codebase to standardize on `createHono` across all backend handlers
to ensure request-context middleware is applied consistently, or clarify the
guideline on when `createHono` is required versus the direct pattern. If
proceeding with standardization, replace the `new
Hono<MiddlewareKeyVariables>()` instantiation with the result of calling
`createHono()`, ensuring the import from `utils/hono.ts` is added, and apply
this change codebase-wide across all backend handler files.

In `@tests/cloudflare-datetime.unit.test.ts`:
- Around line 13-18: The test for the getLastMonthAnalyticsWindow function is
currently nested inside the describe('formatDateCF') block, but it should have
its own describe block for better test organization and clarity. Extract this
test by creating a new describe block specifically for
getLastMonthAnalyticsWindow tests and move the it.concurrent test into that new
describe block, keeping the test logic unchanged.

---

Outside diff comments:
In `@supabase/functions/_backend/triggers/logsnag_insights.ts`:
- Around line 1230-1304: The Promise.all() array contains multiple Promise
chains using .then() callbacks instead of async/await style, which violates the
TypeScript coding guidelines. Convert all .then() chains in the array (used with
countAllApps, countAllUpdates, countAllUpdatesExternal, users query, orgs query,
getGithubStars, get_customer_counts RPC, paying_orgs_for_conversion query,
count_all_onboarded RPC, count_all_need_upgrade RPC, count_all_plans_v2 RPC, and
readActiveAppsCF) to use async/await syntax by either extracting each promise
chain into separate async helper functions or wrapping the Promise.all() logic
in an async IIFE, ensuring error handling and return values remain consistent.

---

Duplicate comments:
In `@tests/logsnag-insights-revenue.unit.test.ts`:
- Line 247: The Promise.resolve().then() chain at the app.request call is using
callback-based promise handling instead of async/await syntax required by the
repository's TypeScript guidelines. Refactor this line to use async/await
pattern by moving it into an async function context, awaiting the app.request
call, and then accessing the response.status property directly on the awaited
result instead of chaining with .then().
🪄 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: 1e576883-41f1-482a-b2fa-6ae29940c5fe

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8d7f7 and c2f3a8d.

📒 Files selected for processing (8)
  • supabase/functions/_backend/triggers/logsnag_insights.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • supabase/functions/_backend/utils/logsnag.ts
  • supabase/functions/_backend/utils/tracking.ts
  • supabase/functions/triggers/index.ts
  • supabase/migrations/20260615112920_add_global_stats_completed_shards.sql
  • tests/cloudflare-datetime.unit.test.ts
  • tests/logsnag-insights-revenue.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/triggers/logsnag_insights.ts Outdated
Comment thread supabase/functions/_backend/triggers/logsnag_insights.ts
Comment thread tests/cloudflare-datetime.unit.test.ts
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from c2f3a8d to 5fe173e Compare June 15, 2026 11:55
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 5fe173e to 649d4c9 Compare June 15, 2026 11:58

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 649d4c94e5

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts

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

Caution

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

⚠️ Outside diff range comments (2)
supabase/functions/_backend/triggers/logsnag_insights.ts (2)

381-451: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate required-shard data-source failures so retries can work.

These required shard paths log provider/DB errors and then return zero/default values. Since runLogsnagInsightsShard() marks a shard complete after the runner returns, transient failures can be persisted as a completed snapshot and the queue will not retry the failed shard. Throw on query/provider errors; only write zero for successful empty results.

Also applies to: 531-548, 1289-1325

🤖 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/triggers/logsnag_insights.ts` around lines 381 -
451, The issue is that when database queries fail in the required shard
data-sources, the code logs the error and returns default zero values, causing
`runLogsnagInsightsShard()` to mark the shard as complete despite the failure.
Transient failures then get persisted as completed snapshots and won't be
retried. Instead of returning default values when plansError or subsError occur,
throw the error to propagate the failure up to the retry logic. Only return the
default zero-value objects when the queries succeed but return empty data (when
plansData or subsData is null/empty due to a successful empty result, not due to
an error). This pattern needs to be applied at all three affected locations: the
plansError block in the shown diff, the subsError block in the shown diff, and
the two additional error-handling blocks mentioned in the consolidated sites at
line ranges 531-548 and 1289-1325.

1263-1337: ⚠️ Potential issue | 🟠 Major

Replace .then() callbacks with async/await to comply with TypeScript guidelines.

The code contains multiple promise callback chains violating the guideline that requires async/await instead of .then() callbacks in TypeScript files. Lines 1263–1337 include 7+ .then() chains (lines 1283, 1287, 1289, 1299, 1306, 1311, 1316, 1320, 1327), and lines 1461–1546 contain 5+ additional instances (lines 1488, 1505, 1517, 1529, 1541). Refactor these Promise.all entries using async helpers or IIFEs to maintain consistency with the codebase style.

🤖 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/triggers/logsnag_insights.ts` around lines 1263 -
1337, The Promise.all() array contains multiple `.then()` callback chains that
should be replaced with async/await syntax to comply with TypeScript guidelines.
Refactor each promise entry that uses `.then()` (such as the chains on the
supabase queries for users, orgs, customers, paying_orgs_for_conversion,
onboarded, need_upgrade, plans, and actives, as well as the readActiveAppsCF
call) by wrapping them in async helper functions or IIFEs that use async/await
instead of chaining .then() callbacks. This will make the code more consistent
with the codebase style and modern TypeScript practices.

Source: Coding guidelines

🤖 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/triggers/logsnag_insights.ts`:
- Around line 190-195: The function `readLogsnagInsightsPayload()` currently
silently converts JSON parse failures to an empty object, which causes malformed
non-empty payloads to use default values instead of rejecting them. Modify the
function to distinguish between truly empty request bodies (which should return
an empty object) and non-empty bodies with invalid JSON (which should return a
400 error response). Check the raw request body content before attempting to
parse it, and reject with a 400 status code when the body is non-empty but
contains invalid JSON.
- Around line 1280-1287: The queries on the users and orgs tables are using
.select('*', { count: 'exact' }) which fetches full row payloads in addition to
the count, creating unnecessary overhead on large tables. Replace both of these
inefficient queries with Supabase's count-only approach by using head: true
instead, matching the pattern already used elsewhere in this file. This applies
to both the supabase.from('users') query and the supabase.from('orgs') query to
ensure only count data is requested without fetching row payloads.

In `@supabase/functions/_backend/utils/cloudflare.ts`:
- Around line 1009-1014: The fallback rolling-window calculation is losing
precision by truncating the ISO datetime to just the date portion using slice(0,
10), which drops the time-of-day and causes the window to start from midnight
instead of the exact timestamp 30 days ago. This can result in queries spanning
up to 31 days instead of a true rolling 30-day window. Modify the oneMonthAgo
variable construction to preserve the full ISO datetime format (without slicing
to just YYYY-MM-DD) when formatting the startExpression, ensuring the rolling
window is exactly LAST_MONTH_ANALYTICS_WINDOW_MS milliseconds and not widened by
calendar day boundaries.

---

Outside diff comments:
In `@supabase/functions/_backend/triggers/logsnag_insights.ts`:
- Around line 381-451: The issue is that when database queries fail in the
required shard data-sources, the code logs the error and returns default zero
values, causing `runLogsnagInsightsShard()` to mark the shard as complete
despite the failure. Transient failures then get persisted as completed
snapshots and won't be retried. Instead of returning default values when
plansError or subsError occur, throw the error to propagate the failure up to
the retry logic. Only return the default zero-value objects when the queries
succeed but return empty data (when plansData or subsData is null/empty due to a
successful empty result, not due to an error). This pattern needs to be applied
at all three affected locations: the plansError block in the shown diff, the
subsError block in the shown diff, and the two additional error-handling blocks
mentioned in the consolidated sites at line ranges 531-548 and 1289-1325.
- Around line 1263-1337: The Promise.all() array contains multiple `.then()`
callback chains that should be replaced with async/await syntax to comply with
TypeScript guidelines. Refactor each promise entry that uses `.then()` (such as
the chains on the supabase queries for users, orgs, customers,
paying_orgs_for_conversion, onboarded, need_upgrade, plans, and actives, as well
as the readActiveAppsCF call) by wrapping them in async helper functions or
IIFEs that use async/await instead of chaining .then() callbacks. This will make
the code more consistent with the codebase style and modern TypeScript
practices.
🪄 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: 8af64a5f-393b-4551-aeb9-4135512389f5

📥 Commits

Reviewing files that changed from the base of the PR and between c2f3a8d and 5fe173e.

📒 Files selected for processing (8)
  • supabase/functions/_backend/triggers/logsnag_insights.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • supabase/functions/_backend/utils/logsnag.ts
  • supabase/functions/_backend/utils/tracking.ts
  • supabase/functions/triggers/index.ts
  • supabase/migrations/20260615112920_add_global_stats_completed_shards.sql
  • tests/cloudflare-datetime.unit.test.ts
  • tests/logsnag-insights-revenue.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/triggers/logsnag_insights.ts
Comment thread supabase/functions/_backend/triggers/logsnag_insights.ts
Comment thread supabase/functions/_backend/utils/cloudflare.ts
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 649d4c9 to 5c4c8a5 Compare June 15, 2026 12:13

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c4c8a5c71

ℹ️ 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 supabase/functions/_backend/triggers/logsnag_insights.ts
Comment thread supabase/functions/_backend/triggers/logsnag_insights.ts Outdated
@riderx riderx force-pushed the codex/fix-global-stats-logsnag-timeout branch from 5c4c8a5 to ee6af81 Compare June 15, 2026 12:27

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

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/triggers/logsnag_insights.ts (1)

1284-1358: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Use camelCase for local variable names.

Several destructured variables use snake_case instead of camelCase: updates_external, paying_orgs_for_conversion, need_upgrade. While the usage shard (lines 1446-1462) was updated to use camelCase, this section still has snake_case locals.

As per coding guidelines, TypeScript/JavaScript variable names should use camelCase.

♻️ Suggested variable name changes
 const [
   apps,
   updates,
-  updates_external,
+  updatesExternal,
   users,
   orgs,
   stars,
   customers,
-  paying_orgs_for_conversion,
+  payingOrgsForConversion,
   onboarded,
-  need_upgrade,
+  needUpgrade,
   plans,
   actives,
 ] = await Promise.all([

Then update references:

-const not_paying = users - customers.total - plans.Trial
-const org_conversion_rate = calculateConversionRate(paying_orgs_for_conversion, orgs)
+const notPaying = users - customers.total - plans.Trial
+const orgConversionRate = calculateConversionRate(payingOrgsForConversion, orgs)
🤖 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/triggers/logsnag_insights.ts` around lines 1284 -
1358, Rename the destructured local variables in the Promise.all() destructuring
block from snake_case to camelCase to follow TypeScript/JavaScript coding
conventions. Change updates_external to updatesExternal,
paying_orgs_for_conversion to payingOrgsForConversion, and need_upgrade to
needUpgrade. Then search the entire file and update all references to these
variables to use the new camelCase names, particularly in the usage section
mentioned in the comment that already uses camelCase (around lines 1446-1462).

Source: Coding guidelines

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

Outside diff comments:
In `@supabase/functions/_backend/triggers/logsnag_insights.ts`:
- Around line 1284-1358: Rename the destructured local variables in the
Promise.all() destructuring block from snake_case to camelCase to follow
TypeScript/JavaScript coding conventions. Change updates_external to
updatesExternal, paying_orgs_for_conversion to payingOrgsForConversion, and
need_upgrade to needUpgrade. Then search the entire file and update all
references to these variables to use the new camelCase names, particularly in
the usage section mentioned in the comment that already uses camelCase (around
lines 1446-1462).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 133fc1ea-afd0-4be8-b84e-1acdd1dc1a4c

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe173e and ee6af81.

📒 Files selected for processing (9)
  • cloudflare_workers/api/index.ts
  • supabase/functions/_backend/triggers/logsnag_insights.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • supabase/functions/_backend/utils/logsnag.ts
  • supabase/functions/_backend/utils/tracking.ts
  • supabase/functions/triggers/index.ts
  • supabase/migrations/20260615112920_add_global_stats_completed_shards.sql
  • tests/cloudflare-datetime.unit.test.ts
  • tests/logsnag-insights-revenue.unit.test.ts
🔗 Linked repositories identified

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

  • Cap-go/capacitor-updater (manual)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee6af81c6e

ℹ️ 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".

stars: getGithubStars(),
customers: supabase.rpc('get_customer_counts').single().then((res) => {
getGithubStars(),
supabase.rpc('get_customer_counts').single().then((res) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Anchor billing counts to the snapshot date

When a dispatcher retry or manual replay preserves an older date_id, this core shard still reads current billing state: get_customer_counts() filters current stripe_info.status = 'succeeded', and count_all_plans_v2() below also uses current status/NOW() for trials. If subscriptions churn or trials expire after the snapshot day, the retry writes today's paying/monthly/yearly/plan counts into the historical global_stats row, while other shards are now snapshot-bounded, so notifications can report a mixed-date daily snapshot.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28604a4043

ℹ️ 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".

date_id: snapshotDateId,
actives,
] = await Promise.all([
countAllApps(c),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Anchor core totals to the snapshot date

When a dispatcher retry or manual replay preserves an older date_id, these core counters still read current production state and then write it into window.prevDayDateId. If apps, updates, users, or orgs changed after the snapshot day, the retry overwrites the historical global_stats row with today's totals while the rest of the shard is date-bound; use the snapshot end time for these totals or skip rewriting them on dated replays.

Useful? React with 👍 / 👎.

Comment thread supabase/functions/_backend/triggers/logsnag_insights.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8ddf18248

ℹ️ 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 on lines +1575 to +1576
getBundleStorageGb(c),
successRatePromise,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Anchor usage current metrics to the snapshot

When a usage shard for a preserved date_id is replayed late, these calls still read current state: getBundleStorageGb() invokes the current total_bundle_storage_bytes() RPC and getUpdateStats(c) uses the current short now() window in the Cloudflare path (or the current Supabase RPC). The results are then written into window.prevDayDateId, so a queue backlog or manual replay can overwrite a historical snapshot's bundle_storage_gb and success_rate with today's values; pass snapshot bounds or skip rewriting these current-only fields on dated replays.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14dd3d8b18

ℹ️ 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".

.then(res => res.count ?? 0),
getGithubStars(),
getBillingSnapshotCounts(c, window.prevDayEnd),
supabase.rpc('count_all_onboarded').single().then((res) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound core RPC metrics during dated replays

When a dispatcher retry or manual replay preserves an older date_id, this still calls count_all_onboarded() (and the adjacent count_all_need_upgrade()), which read current public.apps/stripe_info without any snapshot bound in supabase/schemas/prod.sql. The shard then writes those values into window.prevDayDateId, so apps or plan states changed after that day produce mixed-date onboarded/need_upgrade metrics even though the surrounding totals are now snapshot-bounded. Fresh evidence: this patch bounded the core totals, but these two RPCs remain current-state.

Useful? React with 👍 / 👎.

@sonarqubecloud

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 788271a5d2

ℹ️ 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 on lines +665 to +666
const last30daysStart = new Date(prevDayEnd.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString()
const last30daysEnd = prevDayEnd.toISOString()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound all build counters to the snapshot end

When a builds shard is replayed for an older date_id, this new snapshot end is only applied to the three last_month queries; the adjacent all-time build_logs and build_requests counts still have no < prevDayEnd bound. If newer builds are created before a delayed retry/manual replay, builds_total, platform totals, and builds_success_* are written into the historical global_stats row with today's totals while the rolling fields are snapshot-bounded.

Useful? React with 👍 / 👎.

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.

2 participants