| 1 | import type { Env } from "./env"; |
| 2 | import type { ReportSample } from "./group"; |
| 3 | import { |
| 4 | groupDiagnosticSummary, |
| 5 | type DiagnosticsQueryObserver, |
| 6 | type GroupDiagnosticSummary, |
| 7 | } from "./diagnostics_v2"; |
| 8 | |
| 9 | const REPORT_COLUMNS = ` |
| 10 | id, version, os, arch, message, device, created_at, source, label, error_type, error_message, error_family, |
| 11 | top_frame, build_commit, channel, language, view, breadcrumbs, component_stack, stack, |
| 12 | occurred_at, webview2, web_runtime, event_id, incident_id, diagnostics`; |
| 13 | |
| 14 | // Keep the detail page bounded even when a group has a large lifetime count. |
| 15 | // The first retained sample preserves the earliest context; the latest samples |
| 16 | // show the current failure shape. UNION removes the duplicate when a group has |
| 17 | // fewer rows than the requested latest sample window. |
| 18 | export function groupReportsStatement(db: D1Database, fingerprint: string, latestSamples: number): D1PreparedStatement { |
| 19 | return db.prepare( |
| 20 | `WITH first_sample AS ( |
| 21 | SELECT ${REPORT_COLUMNS} |
| 22 | FROM reports INDEXED BY reports_fingerprint_id WHERE fingerprint = ?1 ORDER BY id ASC LIMIT 1 |
| 23 | ), latest_samples AS ( |
| 24 | SELECT ${REPORT_COLUMNS} |
| 25 | FROM reports INDEXED BY reports_fingerprint_id WHERE fingerprint = ?1 ORDER BY id DESC LIMIT ?2 |
| 26 | ), retained AS ( |
| 27 | SELECT * FROM first_sample |
| 28 | UNION |
| 29 | SELECT * FROM latest_samples |
| 30 | ) |
| 31 | SELECT ${REPORT_COLUMNS} FROM retained ORDER BY id DESC`, |
| 32 | ).bind(fingerprint, latestSamples); |
| 33 | } |
| 34 | |
| 35 | export async function loadD1GroupReports( |
| 36 | env: Env, |
| 37 | fingerprint: string, |
| 38 | latestSamples: number, |
| 39 | observe: DiagnosticsQueryObserver, |
| 40 | ): Promise<{ reports: ReportSample[]; unavailable: boolean }> { |
| 41 | try { |
| 42 | const started = performance.now(); |
| 43 | const stored = await groupReportsStatement(env.DB, fingerprint, latestSamples).all<ReportSample>(); |
| 44 | observe("group_samples", performance.now() - started, stored.results?.length ?? 0); |
| 45 | return { reports: stored.results, unavailable: false }; |
| 46 | } catch (error) { |
| 47 | console.error("group raw sample query failed", error); |
| 48 | return { reports: [], unavailable: true }; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | export async function loadGroupDiagnostics( |
| 53 | env: Env, |
| 54 | fingerprint: string, |
| 55 | observe: DiagnosticsQueryObserver, |
| 56 | ): Promise<{ summary?: GroupDiagnosticSummary; unavailable: boolean }> { |
| 57 | try { |
| 58 | return { summary: await groupDiagnosticSummary(env, fingerprint, observe), unavailable: false }; |
| 59 | } catch (error) { |
| 60 | console.error("group diagnostic summary query failed", error); |
| 61 | return { unavailable: true }; |
| 62 | } |
| 63 | } |
| 64 |