spanTree.spec.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import {EntrySpans, EventOrGroupType, EventTransaction} from 'sentry/types/event';
  2. import {SpanTree} from './spanTree';
  3. function s(partial: Partial<EntrySpans['data'][0]>): EntrySpans['data'][0] {
  4. return {
  5. timestamp: 0,
  6. start_timestamp: 0,
  7. exclusive_time: 0,
  8. description: '',
  9. op: '',
  10. span_id: '',
  11. parent_span_id: '',
  12. trace_id: '',
  13. hash: '',
  14. data: {},
  15. ...partial,
  16. };
  17. }
  18. function txn(partial: Partial<EventTransaction>): EventTransaction {
  19. return {
  20. id: '',
  21. projectID: '',
  22. user: {},
  23. contexts: {},
  24. entries: [],
  25. errors: [],
  26. dateCreated: '',
  27. startTimestamp: Date.now(),
  28. endTimestamp: Date.now() + 1000,
  29. title: '',
  30. type: EventOrGroupType.TRANSACTION,
  31. culprit: '',
  32. dist: null,
  33. eventID: '',
  34. fingerprints: [],
  35. dateReceived: new Date().toISOString(),
  36. message: '',
  37. metadata: {},
  38. size: 0,
  39. tags: [],
  40. occurrence: null,
  41. location: '',
  42. crashFile: null,
  43. ...partial,
  44. };
  45. }
  46. describe('SpanTree', () => {
  47. it('initializes the root to txn', () => {
  48. const transaction = txn({
  49. title: 'transaction root',
  50. startTimestamp: Date.now(),
  51. endTimestamp: Date.now() + 1000,
  52. });
  53. const tree = new SpanTree(transaction, []);
  54. expect(tree.root.span.start_timestamp).toBe(transaction.startTimestamp);
  55. expect(tree.root.span.timestamp).toBe(transaction.endTimestamp);
  56. });
  57. it('appends to parent that contains span', () => {
  58. const start = Date.now();
  59. const tree = new SpanTree(
  60. txn({
  61. title: 'transaction root',
  62. startTimestamp: start,
  63. endTimestamp: start + 10,
  64. contexts: {trace: {span_id: 'root'}},
  65. }),
  66. [
  67. s({
  68. span_id: '1',
  69. parent_span_id: 'root',
  70. timestamp: start + 10,
  71. start_timestamp: start,
  72. }),
  73. s({
  74. span_id: '2',
  75. parent_span_id: '1',
  76. timestamp: start + 5,
  77. start_timestamp: start,
  78. }),
  79. ]
  80. );
  81. expect(tree.orphanedSpans.length).toBe(0);
  82. expect(tree.root.children[0].span.span_id).toBe('1');
  83. expect(tree.root.children[0].children[0].span.span_id).toBe('2');
  84. });
  85. it('checks for span overlaps that contains span', () => {
  86. const start = Date.now();
  87. const tree = new SpanTree(
  88. txn({
  89. title: 'transaction root',
  90. startTimestamp: start,
  91. endTimestamp: start + 10,
  92. contexts: {trace: {span_id: 'root'}},
  93. }),
  94. [
  95. s({
  96. span_id: '1',
  97. parent_span_id: 'root',
  98. timestamp: start + 10,
  99. start_timestamp: start,
  100. }),
  101. s({
  102. span_id: '2',
  103. parent_span_id: '1',
  104. timestamp: start + 5,
  105. start_timestamp: start,
  106. }),
  107. s({
  108. span_id: '3',
  109. parent_span_id: '1',
  110. timestamp: start + 6,
  111. start_timestamp: start + 1,
  112. }),
  113. ]
  114. );
  115. expect(tree.orphanedSpans.length).toBe(1);
  116. expect(tree.root.children[0].span.span_id).toBe('1');
  117. expect(tree.root.children[0].children[0].span.span_id).toBe('2');
  118. expect(tree.root.children[0].children[1]).toBe(undefined);
  119. });
  120. it('creates missing instrumentation node', () => {
  121. const start = Date.now();
  122. const tree = new SpanTree(
  123. txn({
  124. title: 'transaction root',
  125. startTimestamp: start,
  126. endTimestamp: start + 10,
  127. contexts: {trace: {span_id: 'root'}},
  128. }),
  129. [
  130. s({
  131. span_id: '1',
  132. parent_span_id: 'root',
  133. timestamp: start + 5,
  134. start_timestamp: start,
  135. }),
  136. s({
  137. span_id: '2',
  138. parent_span_id: 'root',
  139. timestamp: start + 10,
  140. start_timestamp: start + 6,
  141. }),
  142. ]
  143. );
  144. expect(tree.orphanedSpans.length).toBe(0);
  145. expect(tree.root.children[0].span.span_id).toBe('1');
  146. expect(tree.root.children[1].span.op).toBe('missing span instrumentation');
  147. expect(tree.root.children[2].span.span_id).toBe('2');
  148. });
  149. it('does not create missing instrumentation if elapsed < threshold', () => {
  150. const start = Date.now();
  151. const tree = new SpanTree(
  152. txn({
  153. title: 'transaction root',
  154. startTimestamp: start,
  155. endTimestamp: start + 10,
  156. contexts: {trace: {span_id: 'root'}},
  157. }),
  158. [
  159. s({
  160. span_id: '1',
  161. parent_span_id: 'root',
  162. timestamp: start + 5,
  163. start_timestamp: start,
  164. }),
  165. s({
  166. span_id: '2',
  167. parent_span_id: 'root',
  168. timestamp: start + 10,
  169. // There is only 50ms difference here, 100ms is the threshold
  170. start_timestamp: start + 5.05,
  171. }),
  172. ]
  173. );
  174. expect(tree.root.children[0].span.span_id).toBe('1');
  175. expect(tree.root.children[1].span.span_id).toBe('2');
  176. });
  177. it('pushes consecutive span', () => {
  178. const start = Date.now();
  179. const tree = new SpanTree(
  180. txn({
  181. title: 'transaction root',
  182. startTimestamp: start,
  183. endTimestamp: start + 1000,
  184. contexts: {trace: {span_id: 'root'}},
  185. }),
  186. [
  187. s({
  188. span_id: '1',
  189. parent_span_id: 'root',
  190. timestamp: start + 0.05,
  191. start_timestamp: start,
  192. }),
  193. s({
  194. span_id: '2',
  195. parent_span_id: 'root',
  196. timestamp: start + 0.08,
  197. start_timestamp: start + 0.05,
  198. }),
  199. ]
  200. );
  201. expect(tree.orphanedSpans.length).toBe(0);
  202. expect(tree.root.children[0].span.span_id).toBe('1');
  203. expect(tree.root.children[1].span.span_id).toBe('2');
  204. });
  205. it('marks span as orphaned if parent_id does not match', () => {
  206. const tree = new SpanTree(
  207. txn({
  208. title: 'transaction root',
  209. startTimestamp: Date.now(),
  210. endTimestamp: Date.now() + 1000,
  211. contexts: {trace: {span_id: 'root'}},
  212. }),
  213. [
  214. s({span_id: '1', parent_span_id: 'root', timestamp: 1, start_timestamp: 0}),
  215. s({
  216. span_id: '2',
  217. parent_span_id: 'orphaned',
  218. timestamp: 1.1,
  219. start_timestamp: 0.1,
  220. }),
  221. ]
  222. );
  223. expect(tree.orphanedSpans[0].span_id).toBe('2');
  224. });
  225. });