UNPKG

typescript

Version:

TypeScript is a language for application scale JavaScript development

149 lines 5.44 kB
/** * Client-side collection of per-request timing and transfer measurements. * * When enabled, each request records its round-trip latency and the number of * payload bytes sent and received, accumulated into running totals and a * fixed-size ring buffer of the most recent requests. * * The server measures its own per-request processing time independently. When a * timing snapshot is requested, the client fetches the server's collection via * a `getServerTiming` request and folds it into the returned {@link TimingInfo}, * yielding per-request and total server processing time and an estimated * transport overhead (round-trip minus server processing time). Normal response * messages are left unchanged. */ /** Number of most-recent requests retained in the ring buffer. */ export const RECENT_REQUEST_CAPACITY = 5; function emptyAccumulators() { return { requestCount: 0, roundTripMs: 0, bytesSent: 0, bytesReceived: 0, serverTimeMs: 0, transportOverheadMs: 0, nodesMaterialized: 0, sourceFilesFetched: 0, nodesFetched: 0, }; } /** Returns a snapshot representing a disabled (never-collecting) timing state. */ export function disabledTimingInfo() { return { enabled: false, totals: emptyAccumulators(), recentRequests: [], }; } /** Returns a snapshot representing disabled server-side timing collection. */ export function disabledServerTimingInfo() { return { enabled: false, totals: { requestCount: 0, totalProcessingTimeMs: 0 }, recentRequests: [], }; } /** * Folds a server-side timing snapshot into a client-side snapshot, producing a * combined {@link TimingInfo} with per-request and total server processing time * plus estimated transport overhead. * * Recent requests are paired newest-to-newest and only matched when the method * names agree, so that requests recorded by only one side (e.g. the meta * requests used to fetch timing) do not misalign the two ring buffers. */ export function combineTimingInfo(client, server) { if (!client.enabled) { return client; } const serverTimeMs = server.totals.totalProcessingTimeMs; const totals = { ...client.totals, serverTimeMs, transportOverheadMs: Math.max(0, client.totals.roundTripMs - serverTimeMs), }; const recentRequests = client.recentRequests.map(r => ({ ...r })); const serverRecent = server.recentRequests; const pairs = Math.min(recentRequests.length, serverRecent.length); for (let i = 1; i <= pairs; i++) { const c = recentRequests[recentRequests.length - i]; const s = serverRecent[serverRecent.length - i]; if (c.method === s.method) { c.serverTimeMs = s.processingTimeMs; c.transportOverheadMs = Math.max(0, c.roundTripMs - s.processingTimeMs); } } return { enabled: true, totals, recentRequests, }; } /** * Accumulates request timing samples into running totals and a fixed-size ring * buffer of the most recent requests. */ export class TimingCollector { totals = emptyAccumulators(); // Ring buffer of the most recent requests. `ring` grows to at most // RECENT_REQUEST_CAPACITY; once full, `head` marks the oldest entry. ring = []; head = 0; /** Records a single request's measurements. */ record(sample) { this.totals.requestCount++; this.totals.roundTripMs += sample.roundTripMs; this.totals.bytesSent += sample.bytesSent; this.totals.bytesReceived += sample.bytesReceived; const entry = { method: sample.method, roundTripMs: sample.roundTripMs, bytesSent: sample.bytesSent, bytesReceived: sample.bytesReceived, timestamp: Date.now(), }; if (this.ring.length < RECENT_REQUEST_CAPACITY) { this.ring.push(entry); } else { this.ring[this.head] = entry; this.head = (this.head + 1) % RECENT_REQUEST_CAPACITY; } } /** * Records a single AST node materialization. Called on demand as the consumer * walks a binary source-file response's tree, so it is not tied to any one * request. */ recordMaterialization() { this.totals.nodesMaterialized++; } /** * Records a fetched source file: increments the fetched-file counter and adds * the file's materializable node count to the fetched-node total, which serves * as the denominator for the share of fetched nodes that end up materialized. */ recordSourceFileFetched(materializableNodeCount) { this.totals.sourceFilesFetched++; this.totals.nodesFetched += materializableNodeCount; } /** Returns a snapshot of the collected timing information. */ getInfo() { const recentRequests = []; for (let i = 0; i < this.ring.length; i++) { recentRequests.push(this.ring[(this.head + i) % this.ring.length]); } return { enabled: true, totals: { ...this.totals }, recentRequests, }; } /** Clears all accumulated totals and recent-request history. */ reset() { this.totals = emptyAccumulators(); this.ring = []; this.head = 0; } } //# sourceMappingURL=timing.js.map