traceOf

tcmts.cli.traced.traceOf · #4kcsux3j · term · runs on the server

uses tcmts.cli.main.versionsOf, which uses tcmts.share.NameTrees, which uses tcmts.store.unpacked, which uses node:zlib

The spans of one row of traceTotals (byNameNow) since since: of kind, a definition's as named now, each version of it alike (label its name now), or, for a page, a view or a statement (no hash), those of that name. Their numbers; each version side by side, newest first, with the entry that made it, and what changed from the one before (and how it runs for it, if that's news); where its calls came from in the browser, as pages waited for them (a server function's); its slowest traces and its latest. Undefined if nothing's recorded.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { changesBetween, versionsOf, versionVerdict } from "#codebase/tcmts/cli/main";
import { percentileOf, spanOf, statsOf } from "#codebase/tcmts/cli/traced";
import type { Of, Version } from "#codebase/tcmts/cli/traced";
import type { DatabaseSync } from "node:sqlite";

export function traceOf(
  db: DatabaseSync,
  cb: Codebase,
  label: string,
  kind: string,
  since: number,
):
  | {
      definition: boolean;
      n: number;
      errors: number;
      p50: number;
      p95: number;
      max: number;
      versions: {
        hash: string;
        now: boolean;
        n: number;
        errors: number;
        p50: number;
        p95: number;
        since?: Version;
      }[];
      change?: { changed: string[]; verdict?: { worse: boolean; said: string } };
      callers: { caller?: Of; n: number; errors: number; p95: number }[];
      slowest: {
        trace: string;
        start: number;
        dur: number;
        error: string | null;
        hash: string | null;
      }[];
      latest: {
        trace: string;
        start: number;
        dur: number;
        error: string | null;
        hash: string | null;
      }[];
    }
  | undefined {
  const hashes = (
    db
      .prepare("SELECT DISTINCT hash FROM spans WHERE hash IS NOT NULL AND kind = ? AND start >= ?")
      .all(kind, since) as { hash: string }[]
  )
    .map((r) => r.hash)
    .filter((h) => {
      const o = spanOf(cb, { hash: h, name: "" });
      return o.label === label && (!o.gone || o.earlier);
    });
  const [where, args]: [string, (string | number)[]] = hashes.length
    ? [
        `hash IN (${hashes.map(() => "?").join(", ")}) AND kind = ? AND start >= ?`,
        [...hashes, kind, since],
      ]
    : ["hash IS NULL AND name = ? AND kind = ? AND start >= ?", [label, kind, since]];
  const all = db
    .prepare(
      `SELECT count(*) AS n, sum(error IS NOT NULL) AS errors, max(dur) AS max FROM spans WHERE ${where}`,
    )
    .get(...args) as { n: number; errors: number | null; max: number | null };
  if (!all.n) return undefined;
  // (newest first, as history has them; one it doesn't, after)
  const history = hashes.length ? versionsOf(cb, label) : [];
  const order = (h: string) => {
    const i = history.findIndex((v) => v.hash === h);
    return i < 0 ? history.length : i;
  };
  const now = hashes.find((h) => cb.store.pathsOf(h).includes(label));
  const versions = [...hashes]
    .sort((a, b) => order(a) - order(b))
    .map((h) => {
      const w = "hash = ? AND kind = ? AND start >= ?";
      const a = [h, kind, since];
      const s = statsOf(db, w, a);
      const v = history.find((x) => x.hash === h);
      return {
        hash: h,
        now: h === now,
        n: s.n,
        errors: s.errors,
        p50: percentileOf(db, w, a, s.n, 0.5),
        p95: s.p95,
        ...(v ? { since: v } : {}),
      };
    });
  const [newer, older] = versions;
  const verdict = newer && older ? versionVerdict(older, newer) : undefined;
  const change =
    newer && older
      ? { changed: changesBetween(cb, older.hash, newer.hash), ...(verdict ? { verdict } : {}) }
      : undefined;
  const callers: { caller?: Of; n: number; errors: number; p95: number }[] = [];
  if (hashes.length && kind === "call") {
    const browser = `hash IN (${hashes.map(() => "?").join(", ")}) AND kind = 'client' AND start >= ?`;
    const fromOf = `json_extract(attrs, '$."tcm.from"')`;
    for (const c of db
      .prepare(
        `SELECT ${fromOf} AS caller, count(*) AS n, sum(error IS NOT NULL) AS errors FROM spans WHERE ${browser} GROUP BY caller ORDER BY n DESC LIMIT 10`,
      )
      .all(...hashes, since) as { caller: string | null; n: number; errors: number }[]) {
      const w = `${browser} AND ${c.caller === null ? `${fromOf} IS NULL` : `${fromOf} = ?`}`;
      const a = [...hashes, since, ...(c.caller === null ? [] : [c.caller])];
      callers.push({
        ...(c.caller
          ? { caller: spanOf(cb, { hash: c.caller, name: `#${c.caller.slice(0, 8)}` }) }
          : {}),
        n: c.n,
        errors: c.errors,
        p95: percentileOf(db, w, a, c.n, 0.95),
      });
    }
  }
  const spans = (order_: string, limit: number) =>
    db
      .prepare(
        `SELECT trace, start, dur, error, hash FROM spans WHERE ${where} ORDER BY ${order_} LIMIT ?`,
      )
      .all(...args, limit) as {
      trace: string;
      start: number;
      dur: number;
      error: string | null;
      hash: string | null;
    }[];
  return {
    definition: hashes.length > 0,
    n: all.n,
    errors: all.errors ?? 0,
    p50: percentileOf(db, where, args, all.n, 0.5),
    p95: percentileOf(db, where, args, all.n, 0.95),
    max: all.max ?? 0,
    versions,
    ...(change ? { change } : {}),
    callers,
    slowest: spans("dur DESC", 5),
    latest: spans("start DESC", 30),
  };
}

uses

used by