traceTree.autogrouping.spec.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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: parentAutogroupSpans[parentAutogroupSpans.length - 1].span_id,
  116. }),
  117. ],
  118. makeEventTransaction()
  119. );
  120. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  121. expect(tree.build().serialize()).toMatchSnapshot();
  122. });
  123. it('expanding parent autogroup renders head to tail chain', () => {
  124. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  125. TraceTree.FromSpans(
  126. tree.root.children[0].children[0],
  127. parentAutogroupSpans,
  128. makeEventTransaction()
  129. );
  130. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  131. TraceTree.ForEachChild(tree.root, c => {
  132. if (isParentAutogroupedNode(c)) {
  133. tree.expand(c, true);
  134. }
  135. });
  136. expect(tree.build().serialize()).toMatchSnapshot();
  137. });
  138. it('collapsing parent autogroup removes its children', () => {
  139. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  140. TraceTree.FromSpans(
  141. tree.root.children[0].children[0],
  142. parentAutogroupSpans,
  143. makeEventTransaction()
  144. );
  145. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  146. TraceTree.ForEachChild(tree.root, c => {
  147. if (isParentAutogroupedNode(c)) {
  148. tree.expand(c, true);
  149. }
  150. });
  151. expect(tree.build().serialize()).toMatchSnapshot();
  152. TraceTree.ForEachChild(tree.root, c => {
  153. if (isParentAutogroupedNode(c)) {
  154. tree.expand(c, false);
  155. }
  156. });
  157. expect(tree.build().serialize()).toMatchSnapshot();
  158. });
  159. it('can expand and collapse', () => {
  160. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  161. TraceTree.FromSpans(
  162. tree.root.children[0].children[0],
  163. [
  164. makeSpan({op: 'db', description: 'redis', span_id: '0000'}),
  165. makeSpan({
  166. op: 'db',
  167. description: 'redis',
  168. span_id: '0001',
  169. parent_span_id: '0000',
  170. }),
  171. makeSpan({
  172. op: 'db',
  173. description: 'redis',
  174. span_id: '0002',
  175. parent_span_id: '0001',
  176. }),
  177. makeSpan({
  178. op: 'http.request',
  179. description: 'browser',
  180. span_id: '0003',
  181. parent_span_id: '0002',
  182. }),
  183. ],
  184. makeEventTransaction()
  185. );
  186. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  187. const initial = tree.build().serialize();
  188. // Expand autogroup part
  189. TraceTree.ForEachChild(tree.root, c => {
  190. if (isParentAutogroupedNode(c)) {
  191. tree.expand(c, true);
  192. }
  193. });
  194. expect(tree.build().serialize()).toMatchSnapshot();
  195. // Collapse autogroup part
  196. TraceTree.ForEachChild(tree.root, c => {
  197. if (isParentAutogroupedNode(c)) {
  198. tree.expand(c, false);
  199. }
  200. });
  201. // Tree should be back to initial state
  202. expect(tree.build().serialize()).toEqual(initial);
  203. });
  204. it('autogroups siblings when they are children of a parent autogroup chain', () => {
  205. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  206. TraceTree.FromSpans(
  207. tree.root.children[0].children[0],
  208. [
  209. ...parentAutogroupSpans,
  210. ...[1, 2, 3, 4, 5].map(_i =>
  211. makeSpan({
  212. op: 'db',
  213. description: 'sql',
  214. start_timestamp: start,
  215. timestamp: start + 1,
  216. parent_span_id:
  217. parentAutogroupSpans[parentAutogroupSpans.length - 1].span_id,
  218. })
  219. ),
  220. ],
  221. makeEventTransaction()
  222. );
  223. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  224. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  225. expect(tree.build().serialize()).toMatchSnapshot();
  226. });
  227. it('removes collapsed parent autogroup', () => {
  228. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  229. TraceTree.FromSpans(
  230. tree.root.children[0].children[0],
  231. parentAutogroupSpansWithChilden,
  232. makeEventTransaction()
  233. );
  234. const snapshot = tree.build().serialize();
  235. // Add sibling autogroup
  236. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  237. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).not.toBeNull();
  238. // Remove it and assert that the tree is back to the original state
  239. TraceTree.RemoveDirectChildrenAutogroupNodes(tree.root);
  240. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).toBeNull();
  241. expect(tree.build().serialize()).toEqual(snapshot);
  242. expect(tree.build().serialize()).toMatchSnapshot();
  243. });
  244. it('removes expanded parent autogroup', () => {
  245. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  246. TraceTree.FromSpans(
  247. tree.root.children[0].children[0],
  248. parentAutogroupSpansWithChilden,
  249. makeEventTransaction()
  250. );
  251. const snapshot = tree.build().serialize();
  252. // Add sibling autogroup
  253. TraceTree.AutogroupDirectChildrenSpanNodes(tree.root);
  254. TraceTree.ForEachChild(tree.root, c => {
  255. if (isParentAutogroupedNode(c)) {
  256. tree.expand(c, true);
  257. }
  258. });
  259. expect(tree.build().serialize()).toMatchSnapshot();
  260. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).not.toBeNull();
  261. // Remove it and assert that the tree is back to the original state
  262. TraceTree.RemoveDirectChildrenAutogroupNodes(tree.root);
  263. expect(TraceTree.Find(tree.root, c => isParentAutogroupedNode(c))).toBeNull();
  264. TraceTree.invalidate(tree.root, true);
  265. expect(tree.build().serialize()).toEqual(snapshot);
  266. expect(tree.build().serialize()).toMatchSnapshot();
  267. });
  268. });
  269. describe('sibling autogrouping', () => {
  270. it('groups spans with the same op and description', () => {
  271. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  272. TraceTree.FromSpans(
  273. tree.root.children[0].children[0],
  274. siblingAutogroupSpans,
  275. makeEventTransaction()
  276. );
  277. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  278. expect(tree.build().serialize()).toMatchSnapshot();
  279. });
  280. it('does not autogroup if count is less 5', () => {
  281. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  282. TraceTree.FromSpans(
  283. tree.root.children[0].children[0],
  284. siblingAutogroupSpans.slice(0, 4),
  285. makeEventTransaction()
  286. );
  287. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  288. expect(tree.build().serialize()).toMatchSnapshot();
  289. });
  290. it('autogroups multiple consecutive groups', () => {
  291. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  292. TraceTree.FromSpans(
  293. tree.root.children[0].children[0],
  294. [
  295. ...siblingAutogroupSpans,
  296. ...siblingAutogroupSpans.map(s => ({...s, op: 'mysql'})),
  297. ],
  298. makeEventTransaction()
  299. );
  300. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  301. expect(tree.build().serialize()).toMatchSnapshot();
  302. });
  303. it('expanding sibling autogroup renders its children', () => {
  304. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  305. TraceTree.FromSpans(
  306. tree.root.children[0].children[0],
  307. siblingAutogroupSpans,
  308. makeEventTransaction()
  309. );
  310. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  311. TraceTree.ForEachChild(tree.root, c => {
  312. if (isSiblingAutogroupedNode(c)) {
  313. tree.expand(c, true);
  314. }
  315. });
  316. expect(tree.build().serialize()).toMatchSnapshot();
  317. });
  318. it('collapsing sibling autogroup removes its children', () => {
  319. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  320. TraceTree.FromSpans(
  321. tree.root.children[0].children[0],
  322. siblingAutogroupSpans,
  323. makeEventTransaction()
  324. );
  325. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  326. TraceTree.ForEachChild(tree.root, c => {
  327. if (isSiblingAutogroupedNode(c)) {
  328. tree.expand(c, true);
  329. }
  330. });
  331. expect(tree.build().serialize()).toMatchSnapshot();
  332. TraceTree.ForEachChild(tree.root, c => {
  333. if (isSiblingAutogroupedNode(c)) {
  334. tree.expand(c, false);
  335. }
  336. });
  337. expect(tree.build().serialize()).toMatchSnapshot();
  338. });
  339. it('removes sibling autogroup', () => {
  340. const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata);
  341. TraceTree.FromSpans(
  342. tree.root.children[0].children[0],
  343. siblingAutogroupSpans,
  344. makeEventTransaction()
  345. );
  346. const snapshot = tree.build().serialize();
  347. // Add sibling autogroup
  348. TraceTree.AutogroupSiblingSpanNodes(tree.root);
  349. expect(TraceTree.Find(tree.root, c => isSiblingAutogroupedNode(c))).not.toBeNull();
  350. // Remove it and assert that the tree is back to the original state
  351. TraceTree.RemoveSiblingAutogroupNodes(tree.root);
  352. expect(tree.build().serialize()).toEqual(snapshot);
  353. expect(tree.build().serialize()).toMatchSnapshot();
  354. });
  355. it.todo(
  356. 'collects errors, performance issues and profiles from sibling autogroup chain'
  357. );
  358. });
  359. });