From 046da80312f075c56ea8b86e84dce2e611a295a5 Mon Sep 17 00:00:00 2001 From: Maksim Gaponov Date: Fri, 3 Nov 2023 11:51:54 +0000 Subject: [PATCH 1/7] Speed up Trace Statistics view calculation Resolves #1925 Now we precompute child spans for each span to calculate statistics. Previously we filtered all spans to find children for each span. Signed-off-by: Maksim Gaponov --- .../TracePage/TraceStatistics/tableValues.tsx | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index d764de6b9a..2b05b7dd94 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -21,29 +21,39 @@ import colorGenerator from '../../../utils/color-generator'; const serviceName = 'Service Name'; const operationName = 'Operation Name'; +let parentChildOfMap: Record; + +function getChildOfSpans(parentID: string, allSpans: Span[]): Span[] { + if (!parentChildOfMap) { + parentChildOfMap = {}; + allSpans.forEach(s => { + if (s.references) { + // Filter for CHILD_OF we don't want to calculate FOLLOWS_FROM (prod-cons) + const parentIDs = s.references.filter(r => r.refType === 'CHILD_OF').map(r => r.spanID); + parentIDs.forEach((pID: string) => { + parentChildOfMap[pID] = parentChildOfMap[pID] || []; + parentChildOfMap[pID].push(s); + }); + } + }); + } + return parentChildOfMap[parentID] || []; +} + +function getChildOfDrange(parentID: string, allSpans: Span[]) { + const childrenDrange = new DRange(); + getChildOfSpans(parentID, allSpans).forEach(s => { + // -1 otherwise it will take for each child a micro (incluse,exclusive) + childrenDrange.add(s.startTime, s.startTime + (s.duration <= 0 ? 0 : s.duration - 1)); + }); + return childrenDrange; +} + function computeSelfTime(span: Span, allSpans: Span[]): number { - if (!span.hasChildren) return span.duration; - // We want to represent spans as half-open intervals like [startTime, startTime + duration). - // This way the subtraction preserves the right boundaries. However, DRange treats all - // intervals as inclusive. For example, - // range(1, 10).subtract(4, 8) => range([1, 3], [9-10]) - // length=(3-1)+(10-9)=2+1=3 - // In other words, we took an interval of length=10-1=9 and subtracted length=8-4=4. - // We should've ended up with length 9-4=5, but we got 3. - // To work around that, we multiply start/end times by 10 and subtract one from the end. - // So instead of [1-10] we get [10-99]. This makes the intervals work like half-open. - const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1); - // Only keep CHILD_OF spans. FOLLOWS_FROM spans do not block the parent. - const children = allSpans.filter( - each => - span.childSpanIds.includes(each.spanID) && - each.references[0].spanID === span.spanID && - each.references[0].refType === 'CHILD_OF' + const cdr = new DRange(span.startTime, span.startTime + span.duration - 1).intersect( + getChildOfDrange(span.spanID, allSpans) ); - children.forEach(child => { - spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1); - }); - return Math.round(spanRange.length / 10); + return span.duration - cdr.length; } function computeColumnValues(trace: Trace, span: Span, allSpans: Span[], resultValue: any) { @@ -97,6 +107,7 @@ function valueFirstDropdown(selectedTagKey: string, trace: Trace) { let color = ''; let allDiffColumnValues = []; const allSpans = trace.spans; + parentChildOfMap = null; // all possibilities that can be displayed if (selectedTagKey === serviceName) { const temp = _.chain(allSpans) @@ -260,6 +271,7 @@ function buildDetail( isDetail: boolean, trace: Trace ) { + parentChildOfMap = null; const newColumnValues = []; for (let j = 0; j < diffNamesA.length; j++) { let color = ''; @@ -325,6 +337,7 @@ function buildDetail( function generateDetailRest(allColumnValues: ITableSpan[], selectedTagKeySecond: string, trace: Trace) { const allSpans = trace.spans; const newTable = []; + parentChildOfMap = null; for (let i = 0; i < allColumnValues.length; i++) { newTable.push(allColumnValues[i]); if (!allColumnValues[i].isDetail) { From 15b77b19e32e6f2de22f6aa6fe6477c54e07fff9 Mon Sep 17 00:00:00 2001 From: Maksim Gaponov Date: Fri, 17 Nov 2023 18:59:32 +0000 Subject: [PATCH 2/7] Refactored the changes Signed-off-by: Maksim Gaponov --- .../TracePage/TraceStatistics/tableValues.tsx | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index 2b05b7dd94..2ea07b011d 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import memoizeOne from 'memoize-one'; import * as _ from 'lodash'; import DRange from 'drange'; import { Trace, Span } from '../../../types/trace'; @@ -21,23 +22,25 @@ import colorGenerator from '../../../utils/color-generator'; const serviceName = 'Service Name'; const operationName = 'Operation Name'; -let parentChildOfMap: Record; +function parentChildOfMap(allSpans: Span[]): Record { + let parentChildOfMap = {}; + allSpans.forEach(s => { + if (s.references) { + // Filter for CHILD_OF we don't want to calculate FOLLOWS_FROM (prod-cons) + const parentIDs = s.references.filter(r => r.refType === 'CHILD_OF').map(r => r.spanID); + parentIDs.forEach((pID: string) => { + parentChildOfMap[pID] = parentChildOfMap[pID] || []; + parentChildOfMap[pID].push(s); + }); + } + }); + return parentChildOfMap; +} + +const memoizedParentChildOfMap = memoizeOne(parentChildOfMap); function getChildOfSpans(parentID: string, allSpans: Span[]): Span[] { - if (!parentChildOfMap) { - parentChildOfMap = {}; - allSpans.forEach(s => { - if (s.references) { - // Filter for CHILD_OF we don't want to calculate FOLLOWS_FROM (prod-cons) - const parentIDs = s.references.filter(r => r.refType === 'CHILD_OF').map(r => r.spanID); - parentIDs.forEach((pID: string) => { - parentChildOfMap[pID] = parentChildOfMap[pID] || []; - parentChildOfMap[pID].push(s); - }); - } - }); - } - return parentChildOfMap[parentID] || []; + return memoizedParentChildOfMap(allSpans)[parentID] || []; } function getChildOfDrange(parentID: string, allSpans: Span[]) { @@ -50,10 +53,20 @@ function getChildOfDrange(parentID: string, allSpans: Span[]) { } function computeSelfTime(span: Span, allSpans: Span[]): number { - const cdr = new DRange(span.startTime, span.startTime + span.duration - 1).intersect( + // We want to represent spans as half-open intervals like [startTime, startTime + duration). + // This way the subtraction preserves the right boundaries. However, DRange treats all + // intervals as inclusive. For example, + // range(1, 10).subtract(4, 8) => range([1, 3], [9-10]) + // length=(3-1)+(10-9)=2+1=3 + // In other words, we took an interval of length=10-1=9 and subtracted length=8-4=4. + // We should've ended up with length 9-4=5, but we got 3. + // To work around that, we multiply start/end times by 10 and subtract one from the end. + // So instead of [1-10] we get [10-99]. This makes the intervals work like half-open. + if (!span.hasChildren) return span.duration; + const spanRange = new DRange(span.startTime, span.startTime + span.duration - 1).subtract( getChildOfDrange(span.spanID, allSpans) ); - return span.duration - cdr.length; + return spanRange.length; } function computeColumnValues(trace: Trace, span: Span, allSpans: Span[], resultValue: any) { @@ -107,7 +120,6 @@ function valueFirstDropdown(selectedTagKey: string, trace: Trace) { let color = ''; let allDiffColumnValues = []; const allSpans = trace.spans; - parentChildOfMap = null; // all possibilities that can be displayed if (selectedTagKey === serviceName) { const temp = _.chain(allSpans) @@ -271,7 +283,6 @@ function buildDetail( isDetail: boolean, trace: Trace ) { - parentChildOfMap = null; const newColumnValues = []; for (let j = 0; j < diffNamesA.length; j++) { let color = ''; @@ -337,7 +348,6 @@ function buildDetail( function generateDetailRest(allColumnValues: ITableSpan[], selectedTagKeySecond: string, trace: Trace) { const allSpans = trace.spans; const newTable = []; - parentChildOfMap = null; for (let i = 0; i < allColumnValues.length; i++) { newTable.push(allColumnValues[i]); if (!allColumnValues[i].isDetail) { From 6f7cf467e9c5ab039ae5c5ef8bbe52114966b3f5 Mon Sep 17 00:00:00 2001 From: Maksim Gaponov Date: Mon, 20 Nov 2023 10:54:28 +0000 Subject: [PATCH 3/7] Refactored the changes Signed-off-by: Maksim Gaponov --- .../src/components/TracePage/TraceStatistics/tableValues.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index 2ea07b011d..fdc619db27 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -23,7 +23,7 @@ const serviceName = 'Service Name'; const operationName = 'Operation Name'; function parentChildOfMap(allSpans: Span[]): Record { - let parentChildOfMap = {}; + const parentChildOfMap: Record = {}; allSpans.forEach(s => { if (s.references) { // Filter for CHILD_OF we don't want to calculate FOLLOWS_FROM (prod-cons) From 2f2d0b96f0ef9078bd57b0eebe9199b103086d28 Mon Sep 17 00:00:00 2001 From: Maksim Gaponov Date: Tue, 28 Nov 2023 17:20:23 +0000 Subject: [PATCH 4/7] Refactor the changes Signed-off-by: Maksim Gaponov --- .../components/TracePage/TraceStatistics/tableValues.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index fdc619db27..221acbbc5d 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -47,7 +47,7 @@ function getChildOfDrange(parentID: string, allSpans: Span[]) { const childrenDrange = new DRange(); getChildOfSpans(parentID, allSpans).forEach(s => { // -1 otherwise it will take for each child a micro (incluse,exclusive) - childrenDrange.add(s.startTime, s.startTime + (s.duration <= 0 ? 0 : s.duration - 1)); + childrenDrange.add(10 * s.startTime, 10 * (s.startTime + Math.max(s.duration, 0)) - 1); }); return childrenDrange; } @@ -63,10 +63,10 @@ function computeSelfTime(span: Span, allSpans: Span[]): number { // To work around that, we multiply start/end times by 10 and subtract one from the end. // So instead of [1-10] we get [10-99]. This makes the intervals work like half-open. if (!span.hasChildren) return span.duration; - const spanRange = new DRange(span.startTime, span.startTime + span.duration - 1).subtract( + const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1).subtract( getChildOfDrange(span.spanID, allSpans) ); - return spanRange.length; + return Math.round(spanRange.length / 10); } function computeColumnValues(trace: Trace, span: Span, allSpans: Span[], resultValue: any) { From 8d3593def845382e2cadfc5cebabc147a062099c Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 29 Nov 2023 09:20:23 +0000 Subject: [PATCH 5/7] Refactor the changes Co-authored-by: Yuri Shkuro Signed-off-by: Maksim --- .../components/TracePage/TraceStatistics/tableValues.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index 221acbbc5d..d2410af70d 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -63,8 +63,10 @@ function computeSelfTime(span: Span, allSpans: Span[]): number { // To work around that, we multiply start/end times by 10 and subtract one from the end. // So instead of [1-10] we get [10-99]. This makes the intervals work like half-open. if (!span.hasChildren) return span.duration; - const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1).subtract( - getChildOfDrange(span.spanID, allSpans) + const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1); + const children = getChildOfSpans(span.spanID, allSpans); + children.forEach(child => { + spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1); ); return Math.round(spanRange.length / 10); } From ba8fde97c7d439c3a43faf416f23a28465545e1c Mon Sep 17 00:00:00 2001 From: Maksim Gaponov Date: Wed, 29 Nov 2023 09:25:57 +0000 Subject: [PATCH 6/7] Refactor the changes Signed-off-by: Maksim Gaponov --- .../TracePage/TraceStatistics/tableValues.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index d2410af70d..d56aa51515 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -43,15 +43,6 @@ function getChildOfSpans(parentID: string, allSpans: Span[]): Span[] { return memoizedParentChildOfMap(allSpans)[parentID] || []; } -function getChildOfDrange(parentID: string, allSpans: Span[]) { - const childrenDrange = new DRange(); - getChildOfSpans(parentID, allSpans).forEach(s => { - // -1 otherwise it will take for each child a micro (incluse,exclusive) - childrenDrange.add(10 * s.startTime, 10 * (s.startTime + Math.max(s.duration, 0)) - 1); - }); - return childrenDrange; -} - function computeSelfTime(span: Span, allSpans: Span[]): number { // We want to represent spans as half-open intervals like [startTime, startTime + duration). // This way the subtraction preserves the right boundaries. However, DRange treats all @@ -65,8 +56,8 @@ function computeSelfTime(span: Span, allSpans: Span[]): number { if (!span.hasChildren) return span.duration; const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1); const children = getChildOfSpans(span.spanID, allSpans); - children.forEach(child => { - spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1); + children.forEach(child => + spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1) ); return Math.round(spanRange.length / 10); } From 0074d0f1572d6413332652fa3e75fc53e26fef5f Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Wed, 29 Nov 2023 11:53:58 -0500 Subject: [PATCH 7/7] undo no-op changes Signed-off-by: Yuri Shkuro --- .../components/TracePage/TraceStatistics/tableValues.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx index d56aa51515..62d84f02e9 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/tableValues.tsx @@ -44,6 +44,7 @@ function getChildOfSpans(parentID: string, allSpans: Span[]): Span[] { } function computeSelfTime(span: Span, allSpans: Span[]): number { + if (!span.hasChildren) return span.duration; // We want to represent spans as half-open intervals like [startTime, startTime + duration). // This way the subtraction preserves the right boundaries. However, DRange treats all // intervals as inclusive. For example, @@ -53,12 +54,11 @@ function computeSelfTime(span: Span, allSpans: Span[]): number { // We should've ended up with length 9-4=5, but we got 3. // To work around that, we multiply start/end times by 10 and subtract one from the end. // So instead of [1-10] we get [10-99]. This makes the intervals work like half-open. - if (!span.hasChildren) return span.duration; const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1); const children = getChildOfSpans(span.spanID, allSpans); - children.forEach(child => - spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1) - ); + children.forEach(child => { + spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1); + }); return Math.round(spanRange.length / 10); }