From 7c5d3435f98d9f4e49761e2b32694c03204cbae7 Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Tue, 11 May 2021 09:59:06 +0200 Subject: [PATCH] Tempo/Jaeger: Prevent error when spans are missing (#33880) --- .../datasource/jaeger/graphTransform.test.ts | 55 +++++++++++++++++++ .../datasource/jaeger/graphTransform.ts | 6 +- .../datasource/tempo/graphTransform.test.ts | 19 +++++++ .../datasource/tempo/graphTransform.ts | 6 +- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/jaeger/graphTransform.test.ts b/public/app/plugins/datasource/jaeger/graphTransform.test.ts index 1a58320a546..5b538dc734a 100644 --- a/public/app/plugins/datasource/jaeger/graphTransform.test.ts +++ b/public/app/plugins/datasource/jaeger/graphTransform.test.ts @@ -31,6 +31,13 @@ describe('createGraphFrames', () => { ); expect(frames[1].fields).toMatchObject(toEdgesFrame([[], [], []])); }); + + it('handles missing spans', async () => { + const frames = createGraphFrames(missingSpanResponse); + expect(frames.length).toBe(2); + expect(frames[0].length).toBe(2); + expect(frames[1].length).toBe(0); + }); }); export const singleSpanResponse: TraceResponse = { @@ -64,3 +71,51 @@ export const singleSpanResponse: TraceResponse = { }, warnings: null, }; + +export const missingSpanResponse: TraceResponse = { + traceID: '3fa414edcef6ad90', + spans: [ + { + traceID: '3fa414edcef6ad90', + spanID: '1', + operationName: 'HTTP GET - api_traces_traceid', + references: [], + startTime: 1605873894680409, + duration: 1049141, + tags: [ + { key: 'sampler.type', type: 'string', value: 'probabilistic' }, + { key: 'sampler.param', type: 'float64', value: 1 }, + ], + logs: [], + processID: 'p1', + warnings: null, + flags: 0, + }, + { + traceID: '3fa414edcef6ad90', + spanID: '2', + operationName: 'HTTP GET - api_traces_traceid', + references: [{ refType: 'CHILD_OF', traceID: '3fa414edcef6ad90', spanID: '3' }], + startTime: 1605873894680409, + duration: 1049141, + tags: [ + { key: 'sampler.type', type: 'string', value: 'probabilistic' }, + { key: 'sampler.param', type: 'float64', value: 1 }, + ], + logs: [], + processID: 'p1', + warnings: null, + flags: 0, + }, + ], + processes: { + p1: { + serviceName: 'tempo-querier', + tags: [ + { key: 'cluster', type: 'string', value: 'ops-tools1' }, + { key: 'container', type: 'string', value: 'tempo-query' }, + ], + }, + }, + warnings: null, +}; diff --git a/public/app/plugins/datasource/jaeger/graphTransform.ts b/public/app/plugins/datasource/jaeger/graphTransform.ts index 6f57829d210..f86208f2192 100644 --- a/public/app/plugins/datasource/jaeger/graphTransform.ts +++ b/public/app/plugins/datasource/jaeger/graphTransform.ts @@ -82,7 +82,8 @@ function convertTraceToGraph(data: TraceResponse): { nodes: Node[]; edges: Edge[ }); const parentSpanID = span.references?.find((r) => r.refType === 'CHILD_OF')?.spanID; - if (parentSpanID) { + // Sometimes some span can be missing. Don't add edges for those. + if (parentSpanID && spanMap[parentSpanID].span) { edges.push({ [Fields.id]: parentSpanID + '--' + span.spanID, [Fields.target]: span.spanID, @@ -120,7 +121,8 @@ function findTraceDuration(spans: Span[]): number { } /** - * Returns a map of the spans with children array for easier processing. + * Returns a map of the spans with children array for easier processing. It will also contain empty spans in case + * span is missing but other spans are it's children. */ function makeSpanMap(spans: Span[]): { [id: string]: { span: Span; children: string[] } } { const spanMap: { [id: string]: { span?: Span; children: string[] } } = {}; diff --git a/public/app/plugins/datasource/tempo/graphTransform.test.ts b/public/app/plugins/datasource/tempo/graphTransform.test.ts index d10637183f3..bff6dae7094 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.test.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.test.ts @@ -49,6 +49,13 @@ describe('createGraphFrames', () => { color: 1.000007560204647, }); }); + + it('handles missing spans', async () => { + const frames = createGraphFrames(missingSpanResponse); + expect(frames.length).toBe(2); + expect(frames[0].length).toBe(2); + expect(frames[1].length).toBe(0); + }); }); const singleSpanResponse = new MutableDataFrame({ @@ -62,3 +69,15 @@ const singleSpanResponse = new MutableDataFrame({ { name: 'duration', values: [14.984] }, ], }); + +const missingSpanResponse = new MutableDataFrame({ + fields: [ + { name: 'traceID', values: ['04450900759028499335', '04450900759028499335'] }, + { name: 'spanID', values: ['1', '2'] }, + { name: 'parentSpanID', values: ['', '3'] }, + { name: 'operationName', values: ['store.validateQueryTimeRange', 'store.validateQueryTimeRange'] }, + { name: 'serviceName', values: ['loki-all', 'loki-all'] }, + { name: 'startTime', values: [1619712655875.4539, 1619712655880.4539] }, + { name: 'duration', values: [14.984, 4.984] }, + ], +}); diff --git a/public/app/plugins/datasource/tempo/graphTransform.ts b/public/app/plugins/datasource/tempo/graphTransform.ts index a44f2b78df7..2b2d0482924 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.ts @@ -96,7 +96,8 @@ function convertTraceToGraph(data: DataFrame): { nodes: Node[]; edges: Edge[] } [Fields.color]: selfDuration / traceDuration, }); - if (row.parentSpanID) { + // Sometimes some span can be missing. Don't add edges for those. + if (row.parentSpanID && spanMap[row.parentSpanID].span) { edges.push({ [Fields.id]: row.parentSpanID + '--' + row.spanID, [Fields.target]: row.spanID, @@ -136,7 +137,8 @@ function findTraceDuration(view: DataFrameView): number { } /** - * Returns a map of the spans with children array for easier processing. + * Returns a map of the spans with children array for easier processing. It will also contain empty spans in case + * span is missing but other spans are it's children. */ function makeSpanMap(view: DataFrameView): { [id: string]: { span: Row; children: string[] } } { const spanMap: { [id: string]: { span?: Row; children: string[] } } = {};