Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor]: Enhancement to selfTime calculation logic in TraceStatistics view #1901

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ span A is on critical path(+++++) |
import trace from '../../TraceStatistics/tableValuesTestTrace/traceWithSingleChildSpanLongerThanParent.json';

const transformedTrace = transformTraceData(trace);
const traceStart = 1679437737490189;
const traceStart = 100;

const criticalPathSections = [
{
spanId: '006c3cf93508f205',
section_start: traceStart,
section_end: traceStart + 36,
section_end: traceStart + 40,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,12 +506,12 @@ describe('check self time', () => {
'Service Name',
transformedTraceWithOverlappingChildrenLongerThanParent
);
expect(serviceOne.selfTotal).toBe(10.33);
expect(serviceOne.selfTotal).toBe(0.03);
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
});

it('span with two children that do not overlap and one is longer than its parent', () => {
const [serviceOne] = getColumnValues('Service Name', transformedTraceWithTwoNonOverlappingChildren);
expect(serviceOne.selfTotal).toBe(1.51);
expect(serviceOne.selfTotal).toBe(0.03);
});

it('span with two overlapping children where one is longer than its parent', () => {
Expand All @@ -521,7 +521,7 @@ describe('check self time', () => {

it('span with single child span longer than its parent', () => {
const [serviceOne] = getColumnValues('Service Name', transformedTraceWithSingleChildSpanLongerThanParent);
expect(serviceOne.selfTotal).toBe(1.34);
expect(serviceOne.selfTotal).toBe(0.04);
});

it('span with three children shorter than their parent, two of which overlap', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,84 +13,14 @@
// limitations under the License.

import * as _ from 'lodash';
import DRange from 'drange';
import { Trace, Span } from '../../../types/trace';
import { ITableSpan } from './types';
import colorGenerator from '../../../utils/color-generator';

const serviceName = 'Service Name';
const operationName = 'Operation Name';

/**
* Return the lowest startTime.
*/
function getLowestStartTime(allOverlay: Span[]) {
let result;
const temp = _.minBy(allOverlay, function calc(a) {
return a.relativeStartTime;
});
if (temp !== undefined) {
result = { duration: temp.duration, lowestStartTime: temp.relativeStartTime };
} else {
result = { duration: -1, lowestStartTime: -1 };
}
return result;
}

/**
* Determines whether the cut spans belong together and then calculates the duration.
*/
function getDuration(lowestStartTime: number, duration: number, allOverlay: Span[]) {
let durationChange = duration;
let didDelete = false;
for (let i = 0; i < allOverlay.length; i++) {
if (lowestStartTime + durationChange >= allOverlay[i].relativeStartTime) {
if (lowestStartTime + durationChange < allOverlay[i].relativeStartTime + allOverlay[i].duration) {
const tempDuration =
allOverlay[i].relativeStartTime + allOverlay[i].duration - lowestStartTime + durationChange;
durationChange = tempDuration;
}
allOverlay.splice(i, 1);
didDelete = true;
break;
}
}
const result = { allOverlay, duration: durationChange, didDelete };
return result;
}

/**
* Return the selfTime of overlay spans.
*/
function onlyOverlay(allOverlay: Span[], allChildren: Span[], tempSelf: number, span: Span) {
let tempSelfChange = tempSelf;
let duration = 0;
let resultGetDuration = { allOverlay, duration, didDelete: false };
const noOverlay = _.difference(allChildren, allOverlay);
let lowestStartTime = 0;
let totalDuration = 0;
const result = getLowestStartTime(allOverlay);
lowestStartTime = result.lowestStartTime;
duration = result.duration;

do {
resultGetDuration = getDuration(lowestStartTime, duration, resultGetDuration.allOverlay);
if (!resultGetDuration.didDelete && resultGetDuration.allOverlay.length > 0) {
totalDuration = resultGetDuration.duration;
const temp = getLowestStartTime(resultGetDuration.allOverlay);
lowestStartTime = temp.lowestStartTime;
duration = temp.duration;
}
} while (resultGetDuration.allOverlay.length > 1);
duration = resultGetDuration.duration + totalDuration;
// no cut is observed
for (let i = 0; i < noOverlay.length; i++) {
duration += noOverlay[i].duration;
}
tempSelfChange += span.duration - duration;

return tempSelfChange;
}

/**
* Used to calculated the content.
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
*/
Expand All @@ -106,125 +36,23 @@ function calculateContent(trace: Trace, span: Span, allSpans: Span[], resultValu
}
// selfTime
let tempSelf = 0;
let longerAsParent = false;
let kinderSchneiden = false;
let allOverlay = [];
const longerAsParentSpan = [];
if (span.hasChildren) {
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
const allChildren = [] as any;
for (let i = 0; i < allSpans.length; i++) {
// i am a child?
if (allSpans[i].references.length === 1) {
if (span.spanID === allSpans[i].references[0].spanID) {
allChildren.push(allSpans[i]);
}
}
}
// i only have one child
if (allChildren.length === 1) {
if (
span.relativeStartTime + span.duration >=
allChildren[0].relativeStartTime + allChildren[0].duration
) {
tempSelf = span.duration - allChildren[0].duration;
} else {
tempSelf = allChildren[0].relativeStartTime - span.relativeStartTime;
}
} else {
// is the child longer as parent
for (let i = 0; i < allChildren.length; i++) {
if (
span.duration + span.relativeStartTime <
allChildren[i].duration + allChildren[i].relativeStartTime
) {
longerAsParent = true;
longerAsParentSpan.push(allChildren[i]);
}
}
// Do the children overlap?
for (let i = 0; i < allChildren.length; i++) {
for (let j = 0; j < allChildren.length; j++) {
// aren't they the same kids?
if (allChildren[i].spanID !== allChildren[j].spanID) {
// if yes the children cut themselves or lie into each other
if (
allChildren[i].relativeStartTime <= allChildren[j].relativeStartTime &&
allChildren[i].relativeStartTime + allChildren[i].duration >= allChildren[j].relativeStartTime
) {
kinderSchneiden = true;
allOverlay.push(allChildren[i]);
allOverlay.push(allChildren[j]);
}
}
}
}
allOverlay = [...new Set(allOverlay)];
// diff options
if (!longerAsParent && !kinderSchneiden) {
tempSelf = span.duration;
for (let i = 0; i < allChildren.length; i++) {
tempSelf -= allChildren[i].duration;
}
} else if (longerAsParent && kinderSchneiden) {
// cut only longerAsParent
if (_.isEmpty(_.xor(allOverlay, longerAsParentSpan))) {
// find ealiesr longerasParent
const earliestLongerAsParent = _.minBy(longerAsParentSpan, function calc(a) {
return a.relativeStartTime;
});
// remove all children wo are longer as Parent
const allChildrenWithout = _.difference(allChildren, longerAsParentSpan);
tempSelf = earliestLongerAsParent.relativeStartTime - span.relativeStartTime;
for (let i = 0; i < allChildrenWithout.length; i++) {
tempSelf -= allChildrenWithout[i].duration;
}
} else {
const overlayOnly = _.difference(allOverlay, longerAsParentSpan);
const allChildrenWithout = _.difference(allChildren, longerAsParentSpan);
const earliestLongerAsParent = _.minBy(longerAsParentSpan, function calc(a) {
return a.relativeStartTime;
});

// overlay between longerAsParent and overlayOnly
const overlayWithout = [];
for (let i = 0; i < overlayOnly.length; i++) {
if (!earliestLongerAsParent.relativeStartTime <= overlayOnly[i].relativeStartTime) {
overlayWithout.push(overlayOnly[i]);
}
}
for (let i = 0; i < overlayWithout.length; i++) {
if (
overlayWithout[i].relativeStartTime + overlayWithout[i].duration >
earliestLongerAsParent.relativeStartTime
) {
overlayWithout[i].duration -=
overlayWithout[i].relativeStartTime +
overlayWithout[i].duration -
earliestLongerAsParent.relativeStartTime;
if (overlayWithout[i].duration < 0) {
overlayWithout[i].duration = 0;
}
}
}

tempSelf = onlyOverlay(overlayWithout, allChildrenWithout, tempSelf, span);
const diff = span.relativeStartTime + span.duration - earliestLongerAsParent.relativeStartTime;
tempSelf = Math.max(0, tempSelf - diff);
}
} else if (longerAsParent) {
// span is longer as Parent
tempSelf = longerAsParentSpan[0].relativeStartTime - span.relativeStartTime;
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i].spanID !== longerAsParentSpan[0].spanID) {
tempSelf -= allChildren[i].duration;
}
}
} else {
// Overlay
tempSelf = onlyOverlay(allOverlay, allChildren, tempSelf, span);
}
}
// no children
const spanRange = new DRange(10 * span.startTime, 10 * (span.startTime + span.duration) - 1);
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
// Filter for CHILD_OF we don't want to calculate FOLLOWS_FROM
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
const children = allSpans.filter(
each =>
span.childSpanIds.includes(each.spanID) &&
each.references[0].spanID === span.spanID &&
each.references[0].refType === 'CHILD_OF'
);
children.forEach(child => {
spanRange.subtract(10 * child.startTime, 10 * (child.startTime + child.duration) - 1);
});
const selfTime = spanRange
.subranges()
.map(r => r.high - r.low)
.reduce((sum, v) => sum + v, 0);
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
tempSelf += Math.round(selfTime / 10);
} else {
tempSelf += span.duration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"flags": 1,
"operationName": "send",
"references": [],
"startTime": 1679437737490189,
"duration": 36,
"startTime": 100,
"duration": 40,
"tags": [
{
"key": "span.kind",
Expand All @@ -32,8 +32,8 @@
"spanID": "006c3cf93508f205"
}
],
"startTime": 1679437737490205,
"duration": 8,
"startTime": 115,
"duration": 10,
"tags": [
{
"key": "span.kind",
Expand Down Expand Up @@ -62,8 +62,8 @@
"spanID": "006c3cf93508f205"
}
],
"startTime": 1679437737500529,
"duration": 79182,
"startTime": 200,
"duration": 300,
"tags": [
{
"key": "span.kind",
Expand Down Expand Up @@ -92,8 +92,8 @@
"spanID": "006c3cf93508f205"
}
],
"startTime": 1679437737500706,
"duration": 78941,
"startTime": 300,
"duration": 100,
"tags": [
{
"key": "span.kind",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"flags": 1,
"operationName": "send",
"references": [],
"startTime": 1679437737490189,
"duration": 36,
"startTime": 100,
"duration": 40,
"tags": [
{
"key": "span.kind",
Expand All @@ -32,8 +32,8 @@
"spanID": "006c3cf93508f205"
}
],
"startTime": 1679437737491529,
"duration": 79182,
"startTime": 200,
"duration": 300,
GLVSKiriti marked this conversation as resolved.
Show resolved Hide resolved
"tags": [
{
"key": "span.kind",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"flags": 1,
"operationName": "send",
"references": [],
"startTime": 1679437737490189,
"duration": 36,
"startTime": 100,
"duration": 40,
"tags": [
{
"key": "span.kind",
Expand All @@ -32,7 +32,7 @@
"spanID": "006c3cf93508f205"
yurishkuro marked this conversation as resolved.
Show resolved Hide resolved
}
],
"startTime": 1679437737490192,
"startTime": 110,
"duration": 10,
"tags": [
{
Expand Down Expand Up @@ -62,8 +62,8 @@
"spanID": "006c3cf93508f205"
}
],
"startTime": 1679437737491706,
"duration": 78941,
"startTime": 500,
"duration": 100,
"tags": [
{
"key": "span.kind",
Expand Down
Loading