UNPKG

@apollo/server

Version:
289 lines 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OurContextualizedStats = exports.OurReport = exports.SizeEstimator = void 0; const usage_reporting_protobuf_1 = require("@apollo/usage-reporting-protobuf"); const durationHistogram_js_1 = require("./durationHistogram.js"); const iterateOverTrace_js_1 = require("./iterateOverTrace.js"); class SizeEstimator { bytes = 0; } exports.SizeEstimator = SizeEstimator; class OurReport { header; tracesPreAggregated = false; constructor(header) { this.header = header; } tracesPerQuery = Object.create(null); endTime = null; operationCount = 0; sizeEstimator = new SizeEstimator(); ensureCountsAreIntegers() { for (const tracesAndStats of Object.values(this.tracesPerQuery)) { tracesAndStats.ensureCountsAreIntegers(); } } addTrace({ statsReportKey, trace, asTrace, referencedFieldsByType, maxTraceBytes = 10 * 1024 * 1024, nonFtv1ErrorPaths, }) { const tracesAndStats = this.getTracesAndStats({ statsReportKey, referencedFieldsByType, }); if (asTrace) { const encodedTrace = usage_reporting_protobuf_1.Trace.encode(trace).finish(); if (!isNaN(maxTraceBytes) && encodedTrace.length > maxTraceBytes) { tracesAndStats.statsWithContext.addTrace(trace, this.sizeEstimator, nonFtv1ErrorPaths); } else { tracesAndStats.trace.push(encodedTrace); this.sizeEstimator.bytes += 2 + encodedTrace.length; } } else { tracesAndStats.statsWithContext.addTrace(trace, this.sizeEstimator, nonFtv1ErrorPaths); } } getTracesAndStats({ statsReportKey, referencedFieldsByType, }) { const existing = this.tracesPerQuery[statsReportKey]; if (existing) { return existing; } this.sizeEstimator.bytes += estimatedBytesForString(statsReportKey); for (const [typeName, referencedFieldsForType] of Object.entries(referencedFieldsByType)) { this.sizeEstimator.bytes += 2 + 2; if (referencedFieldsForType.isInterface) { this.sizeEstimator.bytes += 2; } this.sizeEstimator.bytes += estimatedBytesForString(typeName); for (const fieldName of referencedFieldsForType.fieldNames) { this.sizeEstimator.bytes += estimatedBytesForString(fieldName); } } return (this.tracesPerQuery[statsReportKey] = new OurTracesAndStats(referencedFieldsByType)); } } exports.OurReport = OurReport; class OurTracesAndStats { referencedFieldsByType; constructor(referencedFieldsByType) { this.referencedFieldsByType = referencedFieldsByType; } trace = []; statsWithContext = new StatsByContext(); internalTracesContributingToStats = []; ensureCountsAreIntegers() { this.statsWithContext.ensureCountsAreIntegers(); } } class StatsByContext { map = Object.create(null); toArray() { return Object.values(this.map); } ensureCountsAreIntegers() { for (const contextualizedStats of Object.values(this.map)) { contextualizedStats.ensureCountsAreIntegers(); } } addTrace(trace, sizeEstimator, nonFtv1ErrorPaths) { this.getContextualizedStats(trace, sizeEstimator).addTrace(trace, sizeEstimator, nonFtv1ErrorPaths); } getContextualizedStats(trace, sizeEstimator) { const statsContext = { clientName: trace.clientName, clientVersion: trace.clientVersion, }; const statsContextKey = JSON.stringify(statsContext); const existing = this.map[statsContextKey]; if (existing) { return existing; } sizeEstimator.bytes += 20 + estimatedBytesForString(trace.clientName) + estimatedBytesForString(trace.clientVersion); const contextualizedStats = new OurContextualizedStats(statsContext); this.map[statsContextKey] = contextualizedStats; return contextualizedStats; } } class OurContextualizedStats { context; queryLatencyStats = new OurQueryLatencyStats(); perTypeStat = Object.create(null); constructor(context) { this.context = context; } ensureCountsAreIntegers() { for (const typeStat of Object.values(this.perTypeStat)) { typeStat.ensureCountsAreIntegers(); } } addTrace(trace, sizeEstimator, nonFtv1ErrorPaths = []) { const { fieldExecutionWeight } = trace; if (!fieldExecutionWeight) { this.queryLatencyStats.requestsWithoutFieldInstrumentation++; } this.queryLatencyStats.requestCount++; if (trace.fullQueryCacheHit) { this.queryLatencyStats.cacheLatencyCount.incrementDuration(trace.durationNs); this.queryLatencyStats.cacheHits++; } else { this.queryLatencyStats.latencyCount.incrementDuration(trace.durationNs); } if (!trace.fullQueryCacheHit && trace.cachePolicy?.maxAgeNs != null) { switch (trace.cachePolicy.scope) { case usage_reporting_protobuf_1.Trace.CachePolicy.Scope.PRIVATE: this.queryLatencyStats.privateCacheTtlCount.incrementDuration(trace.cachePolicy.maxAgeNs); break; case usage_reporting_protobuf_1.Trace.CachePolicy.Scope.PUBLIC: this.queryLatencyStats.publicCacheTtlCount.incrementDuration(trace.cachePolicy.maxAgeNs); break; } } if (trace.persistedQueryHit) { this.queryLatencyStats.persistedQueryHits++; } if (trace.persistedQueryRegister) { this.queryLatencyStats.persistedQueryMisses++; } if (trace.forbiddenOperation) { this.queryLatencyStats.forbiddenOperationCount++; } if (trace.registeredOperation) { this.queryLatencyStats.registeredOperationCount++; } let hasError = false; const errorPathStats = new Set(); const traceNodeStats = (node, path) => { if (node.error?.length) { hasError = true; let currPathErrorStats = this.queryLatencyStats.rootErrorStats; path.toArray().forEach((subPath) => { currPathErrorStats = currPathErrorStats.getChild(subPath, sizeEstimator); }); errorPathStats.add(currPathErrorStats); currPathErrorStats.errorsCount += node.error.length; } if (fieldExecutionWeight) { const fieldName = node.originalFieldName || node.responseName; if (node.parentType && fieldName && node.type && node.endTime != null && node.startTime != null && node.endTime >= node.startTime) { const typeStat = this.getTypeStat(node.parentType, sizeEstimator); const fieldStat = typeStat.getFieldStat(fieldName, node.type, sizeEstimator); fieldStat.errorsCount += node.error?.length ?? 0; fieldStat.observedExecutionCount++; fieldStat.estimatedExecutionCount += fieldExecutionWeight; fieldStat.requestsWithErrorsCount += (node.error?.length ?? 0) > 0 ? 1 : 0; fieldStat.latencyCount.incrementDuration(node.endTime - node.startTime, fieldExecutionWeight); } } return false; }; (0, iterateOverTrace_js_1.iterateOverTrace)(trace, traceNodeStats, true); for (const { subgraph, path } of nonFtv1ErrorPaths) { hasError = true; if (path) { let currPathErrorStats = this.queryLatencyStats.rootErrorStats.getChild(`service:${subgraph}`, sizeEstimator); path.forEach((subPath) => { if (typeof subPath === 'string') { currPathErrorStats = currPathErrorStats.getChild(subPath, sizeEstimator); } }); errorPathStats.add(currPathErrorStats); currPathErrorStats.errorsCount += 1; } } for (const errorPath of errorPathStats) { errorPath.requestsWithErrorsCount += 1; } if (hasError) { this.queryLatencyStats.requestsWithErrorsCount++; } } getTypeStat(parentType, sizeEstimator) { const existing = this.perTypeStat[parentType]; if (existing) { return existing; } sizeEstimator.bytes += estimatedBytesForString(parentType); const typeStat = new OurTypeStat(); this.perTypeStat[parentType] = typeStat; return typeStat; } } exports.OurContextualizedStats = OurContextualizedStats; class OurQueryLatencyStats { latencyCount = new durationHistogram_js_1.DurationHistogram(); requestCount = 0; requestsWithoutFieldInstrumentation = 0; cacheHits = 0; persistedQueryHits = 0; persistedQueryMisses = 0; cacheLatencyCount = new durationHistogram_js_1.DurationHistogram(); rootErrorStats = new OurPathErrorStats(); requestsWithErrorsCount = 0; publicCacheTtlCount = new durationHistogram_js_1.DurationHistogram(); privateCacheTtlCount = new durationHistogram_js_1.DurationHistogram(); registeredOperationCount = 0; forbiddenOperationCount = 0; } class OurPathErrorStats { children = Object.create(null); errorsCount = 0; requestsWithErrorsCount = 0; getChild(subPath, sizeEstimator) { const existing = this.children[subPath]; if (existing) { return existing; } const child = new OurPathErrorStats(); this.children[subPath] = child; sizeEstimator.bytes += estimatedBytesForString(subPath) + 4; return child; } } class OurTypeStat { perFieldStat = Object.create(null); getFieldStat(fieldName, returnType, sizeEstimator) { const existing = this.perFieldStat[fieldName]; if (existing) { return existing; } sizeEstimator.bytes += estimatedBytesForString(fieldName) + estimatedBytesForString(returnType) + 10; const fieldStat = new OurFieldStat(returnType); this.perFieldStat[fieldName] = fieldStat; return fieldStat; } ensureCountsAreIntegers() { for (const fieldStat of Object.values(this.perFieldStat)) { fieldStat.ensureCountsAreIntegers(); } } } class OurFieldStat { returnType; errorsCount = 0; observedExecutionCount = 0; estimatedExecutionCount = 0; requestsWithErrorsCount = 0; latencyCount = new durationHistogram_js_1.DurationHistogram(); constructor(returnType) { this.returnType = returnType; } ensureCountsAreIntegers() { this.estimatedExecutionCount = Math.floor(this.estimatedExecutionCount); } } function estimatedBytesForString(s) { return 2 + Buffer.byteLength(s); } //# sourceMappingURL=stats.js.map