traceTree.autogrouping.spec.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import {
  2. makeEventTransaction,
  3. makeSpan,
  4. makeTrace,
  5. makeTransaction,
  6. } from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils';
  7. import {isParentAutogroupedNode, isSiblingAutogroupedNode} from './../traceGuards';
  8. import {TraceTree} from './traceTree';
  9. const start = new Date('2024-02-29T00:00:00Z').getTime() / 1e3;
  10. const traceMetadata = {replay: null, meta: null};
  11. const singleTransactionTrace = makeTrace({
  12. transactions: [
  13. makeTransaction({
  14. start_timestamp: start,
  15. timestamp: start + 2,
  16. children: [],
  17. }),
  18. ],
  19. orphan_errors: [],
  20. });
  21. const siblingAutogroupSpans = [
  22. makeSpan({
  23. op: 'db',
  24. description: 'redis',
  25. start_timestamp: start,
  26. timestamp: start + 1,
  27. }),
  28. makeSpan({
  29. op: 'db',
  30. description: 'redis',
  31. start_timestamp: start,
  32. timestamp: start + 1,
  33. }),
  34. makeSpan({
  35. op: 'db',
  36. description: 'redis',
  37. start_timestamp: start,
  38. timestamp: start + 1,
  39. }),
  40. makeSpan({
  41. op: 'db',
  42. description: 'redis',
  43. start_timestamp: start,
  44. timestamp: start + 1,
  45. }),
  46. makeSpan({
  47. op: 'db',
  48. description: 'redis',
  49. start_timestamp: start,
  50. timestamp: start + 1,
  51. }),
  52. ];
  53. const parentAutogroupSpans = [
  54. makeSpan({op: 'db', description: 'redis', span_id: '0000'}),
  55. makeSpan({op: 'db', description: 'redis', span_id: '0001', parent_span_id: '0000'}),
  56. makeSpan({op: 'db', description: 'redis', span_id: '0002', parent_span_id: '0001'}),
  57. ];
  58. const parentAutogroupSpansWithChilden = [
  59. makeSpan({op: 'db', description: 'redis', span_id: '0000'}),
  60. makeSpan({op: 'db', description: 'redis', span_id: '0001', parent_span_id: '0000'}),
  61. makeSpan({op: 'db', description: 'redis', span_id: '0002', parent_span_id: '0001'}),
  62. makeSpan({op: 'http', description: 'request', span_id: '0003', parent_span_id: '0002'}),
  63. ];
  64. describe('autogrouping', () => {
  65. describe('parent autogrouping', () => {
  66. it('groups parent chain with same op', () => {
  67. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  68. TraceTree.FromSpans(
  69. tree.root.children[0]!.children[0]!,
  70. parentAutogroupSpans,
  71. makeEventTransaction()
  72. );
  73. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  74. expect(tree.build().serialize()).toMatchSnapshot();
  75. });
  76. it('assigns children to tail node', () => {
  77. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  78. TraceTree.FromSpans(
  79. tree.root.children[0]!.children[0]!,
  80. [
  81. makeSpan({op: 'db', description: 'redis', span_id: '0000'}),
  82. makeSpan({
  83. op: 'db',
  84. description: 'redis',
  85. span_id: '0001',
  86. parent_span_id: '0000',
  87. }),
  88. makeSpan({
  89. op: 'db',
  90. description: 'redis',
  91. span_id: '0002',
  92. parent_span_id: '0001',
  93. }),
  94. makeSpan({
  95. op: 'http.request',
  96. description: 'browser',
  97. span_id: '0003',
  98. parent_span_id: '0002',
  99. }),
  100. ],
  101. makeEventTransaction()
  102. );
  103. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  104. expect(tree.build().serialize()).toMatchSnapshot();
  105. });
  106. it('autogrouped chain points to tail', () => {
  107. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  108. TraceTree.FromSpans(
  109. tree.root.children[0]!.children[0]!,
  110. [
  111. ...parentAutogroupSpans,
  112. makeSpan({
  113. op: 'http',
  114. description: 'request',
  115. parent_span_id:
  116. parentAutogroupSpans[parentAutogroupSpans.length - 1]!.span_id,
  117. }),
  118. ],
  119. makeEventTransaction()
  120. );
  121. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  122. expect(tree.build().serialize()).toMatchSnapshot();
  123. });
  124. it('expanding parent autogroup renders head to tail chain', () => {
  125. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  126. TraceTree.FromSpans(
  127. tree.root.children[0]!.children[0]!,
  128. parentAutogroupSpans,
  129. makeEventTransaction()
  130. );
  131. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  132. TraceTree.ForEachChild(tree.root, c => {
  133. if (isParentAutogroupedNode(c)) {
  134. tree.expand(c, true);
  135. }
  136. });
  137. expect(tree.build().serialize()).toMatchSnapshot();
  138. });
  139. it('collapsing parent autogroup removes its children', () => {
  140. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  141. TraceTree.FromSpans(
  142. tree.root.children[0]!.children[0]!,
  143. parentAutogroupSpans,
  144. makeEventTransaction()
  145. );
  146. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  147. TraceTree.ForEachChild(tree.root, c => {
  148. if (isParentAutogroupedNode(c)) {
  149. tree.expand(c, true);
  150. }
  151. });
  152. expect(tree.build().serialize()).toMatchSnapshot();
  153. TraceTree.ForEachChild(tree.root, c => {
  154. if (isParentAutogroupedNode(c)) {
  155. tree.expand(c, false);
  156. }
  157. });
  158. expect(tree.build().serialize()).toMatchSnapshot();
  159. });
  160. it('can expand and collapse', () => {
  161. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  162. TraceTree.FromSpans(
  163. tree.root.children[0]!.children[0]!,
  164. [
  165. makeSpan({op: 'db', description: 'redis', span_id: '0000'}),
  166. makeSpan({
  167. op: 'db',
  168. description: 'redis',
  169. span_id: '0001',
  170. parent_span_id: '0000',
  171. }),
  172. makeSpan({
  173. op: 'db',
  174. description: 'redis',
  175. span_id: '0002',
  176. parent_span_id: '0001',
  177. }),
  178. makeSpan({
  179. op: 'http.request',
  180. description: 'browser',
  181. span_id: '0003',
  182. parent_span_id: '0002',
  183. }),
  184. ],
  185. makeEventTransaction()
  186. );
  187. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  188. const initial = tree.build().serialize();
  189. // Expand autogroup part
  190. TraceTree.ForEachChild(tree.root, c => {
  191. if (isParentAutogroupedNode(c)) {
  192. tree.expand(c, true);
  193. }
  194. });
  195. expect(tree.build().serialize()).toMatchSnapshot();
  196. // Collapse autogroup part
  197. TraceTree.ForEachChild(tree.root, c => {
  198. if (isParentAutogroupedNode(c)) {
  199. tree.expand(c, false);
  200. }
  201. });
  202. // Tree should be back to initial state
  203. expect(tree.build().serialize()).toEqual(initial);
  204. });
  205. it('autogroups siblings when they are children of a parent autogroup chain', () => {
  206. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  207. TraceTree.FromSpans(
  208. tree.root.children[0]!.children[0]!,
  209. [
  210. ...parentAutogroupSpans,
  211. ...[1, 2, 3, 4, 5].map(_i =>
  212. makeSpan({
  213. op: 'db',
  214. description: 'sql',
  215. start_timestamp: start,
  216. timestamp: start + 1,
  217. parent_span_id:
  218. parentAutogroupSpans[parentAutogroupSpans.length - 1]!.span_id,
  219. })
  220. ),
  221. ],
  222. makeEventTransaction()
  223. );
  224. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  225. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  226. expect(tree.build().serialize()).toMatchSnapshot();
  227. });
  228. it('removes collapsed parent autogroup', () => {
  229. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  230. TraceTree.FromSpans(
  231. tree.root.children[0]!.children[0]!,
  232. parentAutogroupSpansWithChilden,
  233. makeEventTransaction()
  234. );
  235. const snapshot = tree.build().serialize();
  236. // Add sibling autogroup
  237. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  238. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).not.toBeNull();
  239. // Remove it and assert that the tree is back to the original state
  240. TraceTree.RemoveDirectChildrenAutogroupNodes(tree.root);
  241. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).toBeNull();
  242. expect(tree.build().serialize()).toEqual(snapshot);
  243. expect(tree.build().serialize()).toMatchSnapshot();
  244. });
  245. it('removes expanded parent autogroup', () => {
  246. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  247. TraceTree.FromSpans(
  248. tree.root.children[0]!.children[0]!,
  249. parentAutogroupSpansWithChilden,
  250. makeEventTransaction()
  251. );
  252. const snapshot = tree.build().serialize();
  253. // Add sibling autogroup
  254. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  255. TraceTree.ForEachChild(tree.root, c => {
  256. if (isParentAutogroupedNode(c)) {
  257. tree.expand(c, true);
  258. }
  259. });
  260. expect(tree.build().serialize()).toMatchSnapshot();
  261. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).not.toBeNull();
  262. // Remove it and assert that the tree is back to the original state
  263. TraceTree.RemoveDirectChildrenAutogroupNodes(tree.root);
  264. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).toBeNull();
  265. TraceTree.invalidate(tree.root, true);
  266. expect(tree.build().serialize()).toEqual(snapshot);
  267. expect(tree.build().serialize()).toMatchSnapshot();
  268. });
  269. });
  270. describe('sibling autogrouping', () => {
  271. it('groups spans with the same op and description', () => {
  272. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  273. TraceTree.FromSpans(
  274. tree.root.children[0]!.children[0]!,
  275. siblingAutogroupSpans,
  276. makeEventTransaction()
  277. );
  278. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  279. expect(tree.build().serialize()).toMatchSnapshot();
  280. });
  281. it('does not autogroup if count is less 5', () => {
  282. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  283. TraceTree.FromSpans(
  284. tree.root.children[0]!.children[0]!,
  285. siblingAutogroupSpans.slice(0, 4),
  286. makeEventTransaction()
  287. );
  288. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  289. expect(tree.build().serialize()).toMatchSnapshot();
  290. });
  291. it('autogroups multiple consecutive groups', () => {
  292. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  293. TraceTree.FromSpans(
  294. tree.root.children[0]!.children[0]!,
  295. [
  296. ...siblingAutogroupSpans,
  297. ...siblingAutogroupSpans.map(s => ({...s, op: 'mysql'})),
  298. ],
  299. makeEventTransaction()
  300. );
  301. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  302. expect(tree.build().serialize()).toMatchSnapshot();
  303. });
  304. it('expanding sibling autogroup renders its children', () => {
  305. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  306. TraceTree.FromSpans(
  307. tree.root.children[0]!.children[0]!,
  308. siblingAutogroupSpans,
  309. makeEventTransaction()
  310. );
  311. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  312. TraceTree.ForEachChild(tree.root, c => {
  313. if (isSiblingAutogroupedNode(c)) {
  314. tree.expand(c, true);
  315. }
  316. });
  317. expect(tree.build().serialize()).toMatchSnapshot();
  318. });
  319. it('collapsing sibling autogroup removes its children', () => {
  320. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  321. TraceTree.FromSpans(
  322. tree.root.children[0]!.children[0]!,
  323. siblingAutogroupSpans,
  324. makeEventTransaction()
  325. );
  326. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  327. TraceTree.ForEachChild(tree.root, c => {
  328. if (isSiblingAutogroupedNode(c)) {
  329. tree.expand(c, true);
  330. }
  331. });
  332. expect(tree.build().serialize()).toMatchSnapshot();
  333. TraceTree.ForEachChild(tree.root, c => {
  334. if (isSiblingAutogroupedNode(c)) {
  335. tree.expand(c, false);
  336. }
  337. });
  338. expect(tree.build().serialize()).toMatchSnapshot();
  339. });
  340. it('removes sibling autogroup', () => {
  341. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  342. TraceTree.FromSpans(
  343. tree.root.children[0]!.children[0]!,
  344. siblingAutogroupSpans,
  345. makeEventTransaction()
  346. );
  347. const snapshot = tree.build().serialize();
  348. // Add sibling autogroup
  349. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  350. expect(TraceTree.Find(tree.root, c => isSiblingAutogroupedNode(c))).not.toBeNull();
  351. // Remove it and assert that the tree is back to the original state
  352. TraceTree.RemoveSiblingAutogroupNodes(tree.root);
  353. expect(tree.build().serialize()).toEqual(snapshot);
  354. expect(tree.build().serialize()).toMatchSnapshot();
  355. });
  356. it.todo(
  357. 'collects errors, performance issues and profiles from sibling autogroup chain'
  358. );
  359. });
  360. });