[9.0] [APM] Filter out upstream orphans in waterfall (#214704) (#214966)

# Backport

This will backport the following commits from `main` to `9.0`:
- [[APM] Filter out upstream orphans in waterfall
(#214704)](https://github.com/elastic/kibana/pull/214704)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Sergi
Romeu","email":"sergi.romeu@elastic.co"},"sourceCommit":{"committedDate":"2025-03-18T12:06:36Z","message":"[APM]
Filter out upstream orphans in waterfall (#214704)\n\n##
Summary\n\nCloses #212797\n\n\nThis PR filters out upstream orphans in
the waterfall, which was\nconfusing as we were reparenting to the entry
transaction.","sha":"e1f094d1f57037445213e326b6fa01696172d601","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team:obs-ux-infra_services","backport:version","v9.1.0","v8.19.0","v9.0.1"],"title":"[APM]
Filter out upstream orphans in
waterfall","number":214704,"url":"https://github.com/elastic/kibana/pull/214704","mergeCommit":{"message":"[APM]
Filter out upstream orphans in waterfall (#214704)\n\n##
Summary\n\nCloses #212797\n\n\nThis PR filters out upstream orphans in
the waterfall, which was\nconfusing as we were reparenting to the entry
transaction.","sha":"e1f094d1f57037445213e326b6fa01696172d601"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/214704","number":214704,"mergeCommit":{"message":"[APM]
Filter out upstream orphans in waterfall (#214704)\n\n##
Summary\n\nCloses #212797\n\n\nThis PR filters out upstream orphans in
the waterfall, which was\nconfusing as we were reparenting to the entry
transaction.","sha":"e1f094d1f57037445213e326b6fa01696172d601"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Sergi Romeu <sergi.romeu@elastic.co>
This commit is contained in:
Kibana Machine 2025-03-18 14:52:48 +01:00 committed by GitHub
parent bc32304513
commit c5c18bbe5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 14 deletions

View file

@ -808,12 +808,14 @@ describe('waterfall_helpers', () => {
describe('reparentOrphanItems', () => {
const myTransactionItem = {
duration: 150,
doc: {
processor: { event: 'transaction' },
trace: { id: 'myTrace' },
transaction: {
id: 'myTransactionId1',
},
timestamp: { us: 1000000000000050 },
} as WaterfallTransaction,
docType: 'transaction',
id: 'myTransactionId1',
@ -823,6 +825,7 @@ describe('waterfall_helpers', () => {
const traceItems: IWaterfallSpanOrTransaction[] = [
myTransactionItem,
{
duration: 150,
doc: {
processor: { event: 'span' },
span: {
@ -831,24 +834,31 @@ describe('waterfall_helpers', () => {
parent: {
id: 'myTransactionId1',
},
timestamp: {
us: 1000000000000050,
},
} as WaterfallSpan,
docType: 'span',
id: 'mySpanId',
parentId: 'myTransactionId1',
} as IWaterfallSpan,
];
expect(reparentOrphanItems([], traceItems, 'myTransactionId1')).toEqual(traceItems);
expect(reparentOrphanItems([], traceItems, myTransactionItem)).toEqual(traceItems);
});
it('should reparent orphan items to root transaction', () => {
it('should not reparent if orphan starts before or the duration is longer than entry transaction', () => {
const traceItems: IWaterfallSpanOrTransaction[] = [
myTransactionItem,
{
duration: 200,
doc: {
processor: { event: 'span' },
span: {
id: 'myOrphanSpanId',
},
timestamp: {
us: 1000000000000005,
},
parent: {
id: 'myNotExistingTransactionId1',
},
@ -858,7 +868,34 @@ describe('waterfall_helpers', () => {
parentId: 'myNotExistingTransactionId1',
} as IWaterfallSpan,
];
expect(reparentOrphanItems(['myOrphanSpanId'], traceItems, 'myTransactionId1')).toEqual([
expect(reparentOrphanItems(['myOrphanSpanId'], traceItems, myTransactionItem)).toEqual([
myTransactionItem,
]);
});
it('should reparent orphan items to root transaction', () => {
const traceItems: IWaterfallSpanOrTransaction[] = [
myTransactionItem,
{
duration: 100,
doc: {
processor: { event: 'span' },
span: {
id: 'myOrphanSpanId',
},
timestamp: {
us: 1000000000000100,
},
parent: {
id: 'myNotExistingTransactionId1',
},
} as WaterfallSpan,
docType: 'span',
id: 'myOrphanSpanId',
parentId: 'myNotExistingTransactionId1',
} as IWaterfallSpan,
];
expect(reparentOrphanItems(['myOrphanSpanId'], traceItems, myTransactionItem)).toEqual([
myTransactionItem,
{
doc: {
@ -866,10 +903,14 @@ describe('waterfall_helpers', () => {
span: {
id: 'myOrphanSpanId',
},
timestamp: {
us: 1000000000000100,
},
parent: {
id: 'myNotExistingTransactionId1',
},
} as WaterfallSpan,
duration: 100,
docType: 'span',
id: 'myOrphanSpanId',
parentId: 'myTransactionId1',

View file

@ -422,16 +422,34 @@ export function getOrphanItemsIds(
export function reparentOrphanItems(
orphanItemsIds: string[],
waterfallItems: IWaterfallSpanOrTransaction[],
newParentId?: string
entryWaterfallTransaction?: IWaterfallTransaction
) {
const orphanIdsMap = new Set(orphanItemsIds);
return waterfallItems.map((item) => {
return waterfallItems.reduce<IWaterfallSpanOrTransaction[]>((acc, item) => {
if (orphanIdsMap.has(item.id)) {
item.parentId = newParentId;
item.isOrphan = true;
// we need to filter out the orphan item if it's longer or if it has started before the entry transaction
// as this means it's a parent of the entry transaction
const isLongerThanEntryTransaction =
entryWaterfallTransaction && item.duration > entryWaterfallTransaction?.duration;
const hasStartedBeforeEntryTransaction =
entryWaterfallTransaction &&
item.doc.timestamp.us < entryWaterfallTransaction.doc.timestamp.us;
if (isLongerThanEntryTransaction || hasStartedBeforeEntryTransaction) {
return acc;
}
acc.push({
...item,
parentId: entryWaterfallTransaction?.id,
isOrphan: true,
});
} else {
acc.push(item);
}
return item;
});
return acc;
}, []);
}
export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall {
@ -466,11 +484,7 @@ export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall {
const orphanItemsIds = getOrphanItemsIds(waterfallItems, entryWaterfallTransaction?.id);
const childrenByParentId = getChildrenGroupedByParentId(
reparentOrphanItems(
orphanItemsIds,
reparentSpans(waterfallItems),
entryWaterfallTransaction?.id
)
reparentOrphanItems(orphanItemsIds, reparentSpans(waterfallItems), entryWaterfallTransaction)
);
const items = getOrderedWaterfallItems(childrenByParentId, entryWaterfallTransaction);