/** * Sync pipeline metrics — CloudWatch custom metrics for the event-driven push * receive loop (project event-driven-sync-menubar US-011). * * Publishes one `hq-cloud.sync.p95_latency_seconds` datum per successfully * processed push event to the `HQPro/Sync` namespace, with a `TenantId` * dimension so the dashboard widget can be filtered per tenant. CloudWatch * aggregates p50/p95/p99 across the time window via the dashboard's * `Statistics` setting; the receive-loop side just emits one raw value per * event. * * Best-effort emission * ──────────────────── * - module-level singleton CloudWatchClient with a `_setSyncCloudWatchClient` * test seam * - `publishSyncLatencyMetric` catches all errors and NEVER throws — a * CloudWatch outage MUST NOT crash the sync receive loop. The cadence * safety net still picks up missed work and metric blanks are recoverable; * a crashed loop is not. * - explicit `Unit` + `Timestamp` + `Dimensions` per datum * * Why latency (and not also "events received" / "events failed")? * ─────────────────────────────────────────────────────────────── * US-011 AC#1 calls for the p95 latency metric specifically. The receive-loop's * existing `processedCount` / log lines cover the count/failure dimensions for * now; widening to additional metric names happens in a follow-up if the * operator dashboard grows. * * Adapted from indigoai-us/hq-pro PR #112 (src/sync/metrics.ts) into * @indigoai-us/hq-cloud (Path B). */ import { CloudWatchClient } from "@aws-sdk/client-cloudwatch"; import type { Logger } from "pino"; /** * CloudWatch metric namespace for the sync pipeline. Matches the server-side * namespace (hq-pro PR #112) so the dashboard + alarm cover the client path. */ export declare const SYNC_METRIC_NAMESPACE = "HQPro/Sync"; /** * Metric name for per-event sync latency in seconds. The dashboard widget * applies `Statistics: ["p95"]` to aggregate across the time window — the * receive loop just emits one raw `Seconds` value per processed event. * * Name chosen to match the PRD's alarm threshold (`p95 > 10s`). */ export declare const SYNC_LATENCY_METRIC_NAME = "hq-cloud.sync.p95_latency_seconds"; /** * One latency observation. `latencySeconds` is the wall-clock duration from * save-on-A to visible-on-B (or, on the receive loop, the `syncFn(ctx)` * duration); we ONLY publish on the success path so failed syncs don't skew * p95 toward infinity. * * `relativePath` and `sequenceNumber` are NOT used as CloudWatch dimensions * (cardinality explosion) — they're captured here for the optional debug log * emitted on failure so operators can correlate back to the 3-log chain when * investigating a spike. */ export interface SyncLatencyMetric { tenantId: string; relativePath: string; sequenceNumber: number; latencySeconds: number; timestamp: Date; } export interface SyncMetricCredentials { accessKeyId: string; secretAccessKey: string; sessionToken?: string; expiration?: string; } type CloudWatchClientConfig = NonNullable[0]>; type CloudWatchClientLike = Pick; /** * Replace the CloudWatch client (for testing). * @internal */ export declare function _setSyncCloudWatchClient(client: CloudWatchClient): void; export interface CreateSyncLatencyMetricPublisherOptions { region: string; credentials: SyncMetricCredentials; /** Override CloudWatch construction (tests). */ buildClient?: (config: CloudWatchClientConfig) => CloudWatchClientLike; } export declare function createSyncLatencyMetricPublisher(opts: CreateSyncLatencyMetricPublisherOptions): (metric: SyncLatencyMetric) => Promise; export interface PublishSyncLatencyMetricOptions { /** Override the CloudWatch client (tests). Defaults to the module singleton. */ client?: CloudWatchClientLike; /** Optional pino logger for emission failures. */ logger?: Logger; } /** * Publish a single latency datum to CloudWatch. * * Best-effort: any error from the SDK is CAUGHT and logged. A CloudWatch outage * MUST NOT crash the sync receive loop — the cadence safety net still picks up * missed work, and metric blanks are recoverable; a crashed loop is not. * * Dimension: `TenantId` only. The `relativePath`/`sequenceNumber` fields on the * input are intentionally NOT promoted to dimensions (cardinality), but they * ride along on the failure log so an operator can correlate a missed datum to * its 3-log chain entry. */ export declare function publishSyncLatencyMetric(metric: SyncLatencyMetric, opts?: PublishSyncLatencyMetricOptions): Promise; export {}; //# sourceMappingURL=metrics.d.ts.map