sparkforensics-cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/README.md +6 -0
  2. package/bin/sparkforensics-analyze.mjs +113 -48
  3. package/export-template/docs/404.html +25 -0
  4. package/export-template/docs/assets/app.CndaAS6v.js +1 -0
  5. package/export-template/docs/assets/aqe-loop.IwQSATHw.svg +1 -0
  6. package/export-template/docs/assets/aqe-loop.dark.DGbaxqJE.svg +1 -0
  7. package/export-template/docs/assets/broadcast-vs-shuffle.Db4WY1XK.svg +1 -0
  8. package/export-template/docs/assets/broadcast-vs-shuffle.dark.C7Bxs0mG.svg +1 -0
  9. package/export-template/docs/assets/cache-lifecycle.dark.B-hS7AgU.svg +1 -0
  10. package/export-template/docs/assets/cache-lifecycle.rEOVYQNU.svg +1 -0
  11. package/export-template/docs/assets/chunks/@localSearchIndexroot.DNY8bVcl.js +1 -0
  12. package/export-template/docs/assets/chunks/VPLocalSearchBox.yJbZbsEo.js +9 -0
  13. package/export-template/docs/assets/chunks/duplicate-plan-subtree.dark.Cdp70QhV.js +1 -0
  14. package/export-template/docs/assets/chunks/framework.DSg0KOwT.js +20 -0
  15. package/export-template/docs/assets/chunks/retry-escalation-ladder.dark.DHipdJgZ.js +1 -0
  16. package/export-template/docs/assets/chunks/theme.Df2VAG9w.js +2 -0
  17. package/export-template/docs/assets/cold-start-timeline.DxC_Sc7w.svg +1 -0
  18. package/export-template/docs/assets/cold-start-timeline.dark.CZ17YcAG.svg +1 -0
  19. package/export-template/docs/assets/columnar-layout.PghGeOEA.svg +1 -0
  20. package/export-template/docs/assets/columnar-layout.dark.BVNlz0ff.svg +1 -0
  21. package/export-template/docs/assets/container-memory.DIO0AnIm.svg +1 -0
  22. package/export-template/docs/assets/container-memory.dark.CP-5zuCl.svg +1 -0
  23. package/export-template/docs/assets/contributor-guide_architecture_board-widgets.md.B-OsL91z.js +1 -0
  24. package/export-template/docs/assets/contributor-guide_architecture_board-widgets.md.B-OsL91z.lean.js +1 -0
  25. package/export-template/docs/assets/contributor-guide_architecture_detector-contract.md.BOeH4d1J.js +1 -0
  26. package/export-template/docs/assets/contributor-guide_architecture_detector-contract.md.BOeH4d1J.lean.js +1 -0
  27. package/export-template/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.js +1 -0
  28. package/export-template/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.lean.js +1 -0
  29. package/export-template/docs/assets/contributor-guide_architecture_impact-estimation.md.DYCDPgkh.js +1 -0
  30. package/export-template/docs/assets/contributor-guide_architecture_impact-estimation.md.DYCDPgkh.lean.js +1 -0
  31. package/export-template/docs/assets/contributor-guide_architecture_index.md.3TO9ic6w.js +1 -0
  32. package/export-template/docs/assets/contributor-guide_architecture_index.md.3TO9ic6w.lean.js +1 -0
  33. package/export-template/docs/assets/contributor-guide_architecture_overview.md.CehiRmGn.js +1 -0
  34. package/export-template/docs/assets/contributor-guide_architecture_overview.md.CehiRmGn.lean.js +1 -0
  35. package/export-template/docs/assets/contributor-guide_architecture_state-and-history.md.m3S3UdMk.js +1 -0
  36. package/export-template/docs/assets/contributor-guide_architecture_state-and-history.md.m3S3UdMk.lean.js +1 -0
  37. package/export-template/docs/assets/contributor-guide_architecture_widget-rendering.md.DbqPf2OT.js +1 -0
  38. package/export-template/docs/assets/contributor-guide_architecture_widget-rendering.md.DbqPf2OT.lean.js +1 -0
  39. package/export-template/docs/assets/contributor-guide_architecture_worker-protocol.md.B93qJ_tT.js +6 -0
  40. package/export-template/docs/assets/contributor-guide_architecture_worker-protocol.md.B93qJ_tT.lean.js +1 -0
  41. package/export-template/docs/assets/contributor-guide_contributing.md.CvRsdr6J.js +1 -0
  42. package/export-template/docs/assets/contributor-guide_contributing.md.CvRsdr6J.lean.js +1 -0
  43. package/export-template/docs/assets/contributor-guide_development-setup.md.DvAN_9mK.js +12 -0
  44. package/export-template/docs/assets/contributor-guide_development-setup.md.DvAN_9mK.lean.js +1 -0
  45. package/export-template/docs/assets/contributor-guide_testing.md.6rIKqSyY.js +1 -0
  46. package/export-template/docs/assets/contributor-guide_testing.md.6rIKqSyY.lean.js +1 -0
  47. package/export-template/docs/assets/dag-stages.DSz_S937.svg +1 -0
  48. package/export-template/docs/assets/dag-stages.dark.F72UzxH4.svg +1 -0
  49. package/export-template/docs/assets/driver-executor.D5pQ7YN1.svg +1 -0
  50. package/export-template/docs/assets/driver-executor.dark.BmX9cPvh.svg +1 -0
  51. package/export-template/docs/assets/duplicate-plan-subtree.B4cvN6fj.svg +1 -0
  52. package/export-template/docs/assets/duplicate-plan-subtree.dark.Dw8wS0Ag.svg +1 -0
  53. package/export-template/docs/assets/index.md.CHJVslga.js +1 -0
  54. package/export-template/docs/assets/index.md.CHJVslga.lean.js +1 -0
  55. package/export-template/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  56. package/export-template/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  57. package/export-template/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  58. package/export-template/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  59. package/export-template/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  60. package/export-template/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  61. package/export-template/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  62. package/export-template/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  63. package/export-template/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  64. package/export-template/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  65. package/export-template/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  66. package/export-template/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  67. package/export-template/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  68. package/export-template/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  69. package/export-template/docs/assets/join-strategy.C_FvrCEo.svg +1 -0
  70. package/export-template/docs/assets/join-strategy.dark.ChMLnNII.svg +1 -0
  71. package/export-template/docs/assets/memory-borrowing.BqQRJg0u.svg +1 -0
  72. package/export-template/docs/assets/memory-borrowing.dark.Yhh20O9C.svg +1 -0
  73. package/export-template/docs/assets/memory-regions.XHvO7jHG.svg +1 -0
  74. package/export-template/docs/assets/memory-regions.dark.D4TP9_08.svg +1 -0
  75. package/export-template/docs/assets/repartition-vs-coalesce.BovLRrpj.svg +1 -0
  76. package/export-template/docs/assets/repartition-vs-coalesce.dark.BhAczKZQ.svg +1 -0
  77. package/export-template/docs/assets/retry-escalation-ladder.DyTKJJmZ.svg +1 -0
  78. package/export-template/docs/assets/retry-escalation-ladder.dark.BdsabtU3.svg +1 -0
  79. package/export-template/docs/assets/shuffle-map-reduce.KuOEZVmg.svg +1 -0
  80. package/export-template/docs/assets/shuffle-map-reduce.dark.BgQZnFSb.svg +1 -0
  81. package/export-template/docs/assets/spill-classification.BU2euYDO.svg +1 -0
  82. package/export-template/docs/assets/spill-classification.dark.D7i1M40d.svg +1 -0
  83. package/export-template/docs/assets/style.DXOMCXxn.css +1 -0
  84. package/export-template/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.js +1 -0
  85. package/export-template/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.lean.js +1 -0
  86. package/export-template/docs/assets/tuning-reference_aqe.md.BIsCtLzm.js +1 -0
  87. package/export-template/docs/assets/tuning-reference_aqe.md.BIsCtLzm.lean.js +1 -0
  88. package/export-template/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.js +1 -0
  89. package/export-template/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.lean.js +1 -0
  90. package/export-template/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.js +7 -0
  91. package/export-template/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.lean.js +1 -0
  92. package/export-template/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.js +1 -0
  93. package/export-template/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.lean.js +1 -0
  94. package/export-template/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.js +6 -0
  95. package/export-template/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.lean.js +1 -0
  96. package/export-template/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.js +6 -0
  97. package/export-template/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.lean.js +1 -0
  98. package/export-template/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.js +8 -0
  99. package/export-template/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.lean.js +1 -0
  100. package/export-template/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.js +7 -0
  101. package/export-template/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.lean.js +1 -0
  102. package/export-template/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.js +1 -0
  103. package/export-template/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.lean.js +1 -0
  104. package/export-template/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.js +12 -0
  105. package/export-template/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.lean.js +1 -0
  106. package/export-template/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.js +14 -0
  107. package/export-template/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.lean.js +1 -0
  108. package/export-template/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.js +7 -0
  109. package/export-template/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.lean.js +1 -0
  110. package/export-template/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.js +5 -0
  111. package/export-template/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.lean.js +1 -0
  112. package/export-template/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.js +6 -0
  113. package/export-template/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.lean.js +1 -0
  114. package/export-template/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.js +7 -0
  115. package/export-template/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.lean.js +1 -0
  116. package/export-template/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.js +8 -0
  117. package/export-template/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.lean.js +1 -0
  118. package/export-template/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.js +5 -0
  119. package/export-template/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.lean.js +1 -0
  120. package/export-template/docs/assets/tuning-reference_caching.md.B7aQ8asB.js +1 -0
  121. package/export-template/docs/assets/tuning-reference_caching.md.B7aQ8asB.lean.js +1 -0
  122. package/export-template/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.js +1 -0
  123. package/export-template/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.lean.js +1 -0
  124. package/export-template/docs/assets/tuning-reference_config.md.UvveiWG3.js +1 -0
  125. package/export-template/docs/assets/tuning-reference_config.md.UvveiWG3.lean.js +1 -0
  126. package/export-template/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.js +1 -0
  127. package/export-template/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.lean.js +1 -0
  128. package/export-template/docs/assets/tuning-reference_index.md.BQ_NooMV.js +1 -0
  129. package/export-template/docs/assets/tuning-reference_index.md.BQ_NooMV.lean.js +1 -0
  130. package/export-template/docs/assets/tuning-reference_intro.md.CobD-lGB.js +1 -0
  131. package/export-template/docs/assets/tuning-reference_intro.md.CobD-lGB.lean.js +1 -0
  132. package/export-template/docs/assets/tuning-reference_joins.md.BtKs_CuW.js +1 -0
  133. package/export-template/docs/assets/tuning-reference_joins.md.BtKs_CuW.lean.js +1 -0
  134. package/export-template/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.js +1 -0
  135. package/export-template/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.lean.js +1 -0
  136. package/export-template/docs/assets/tuning-reference_metrics.md.mLOh7Apj.js +1 -0
  137. package/export-template/docs/assets/tuning-reference_metrics.md.mLOh7Apj.lean.js +1 -0
  138. package/export-template/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.js +1 -0
  139. package/export-template/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.lean.js +1 -0
  140. package/export-template/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.js +6 -0
  141. package/export-template/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.lean.js +1 -0
  142. package/export-template/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.js +1 -0
  143. package/export-template/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.lean.js +1 -0
  144. package/export-template/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.js +1 -0
  145. package/export-template/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.lean.js +1 -0
  146. package/export-template/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.js +1 -0
  147. package/export-template/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.lean.js +1 -0
  148. package/export-template/docs/assets/udf-execution-models.BUFDICuG.svg +1 -0
  149. package/export-template/docs/assets/udf-execution-models.dark.YTNS6GDq.svg +1 -0
  150. package/export-template/docs/assets/user-guide_alternative-log-retrieval.md.sU3KGarf.js +1 -0
  151. package/export-template/docs/assets/user-guide_alternative-log-retrieval.md.sU3KGarf.lean.js +1 -0
  152. package/export-template/docs/assets/user-guide_getting-started.md.DtEM37MK.js +3 -0
  153. package/export-template/docs/assets/user-guide_getting-started.md.DtEM37MK.lean.js +1 -0
  154. package/export-template/docs/assets/user-guide_mcp-tools.md.C8MiIu7F.js +125 -0
  155. package/export-template/docs/assets/user-guide_mcp-tools.md.C8MiIu7F.lean.js +1 -0
  156. package/export-template/docs/assets/user-guide_run-comparison.md.S0TWWmLY.js +1 -0
  157. package/export-template/docs/assets/user-guide_run-comparison.md.S0TWWmLY.lean.js +1 -0
  158. package/export-template/docs/assets/user-guide_understanding-findings.md.D0R_Y-R2.js +1 -0
  159. package/export-template/docs/assets/user-guide_understanding-findings.md.D0R_Y-R2.lean.js +1 -0
  160. package/export-template/docs/contributor-guide/architecture/board-widgets.html +25 -0
  161. package/export-template/docs/contributor-guide/architecture/detector-contract.html +25 -0
  162. package/export-template/docs/contributor-guide/architecture/drill-down.html +25 -0
  163. package/export-template/docs/contributor-guide/architecture/impact-estimation.html +25 -0
  164. package/export-template/docs/contributor-guide/architecture/index.html +25 -0
  165. package/export-template/docs/contributor-guide/architecture/overview.html +25 -0
  166. package/export-template/docs/contributor-guide/architecture/state-and-history.html +25 -0
  167. package/export-template/docs/contributor-guide/architecture/widget-rendering.html +25 -0
  168. package/export-template/docs/contributor-guide/architecture/worker-protocol.html +30 -0
  169. package/export-template/docs/contributor-guide/contributing.html +25 -0
  170. package/export-template/docs/contributor-guide/development-setup.html +36 -0
  171. package/export-template/docs/contributor-guide/testing.html +25 -0
  172. package/export-template/docs/favicon.svg +4 -0
  173. package/export-template/docs/hashmap.json +1 -0
  174. package/export-template/docs/index.html +25 -0
  175. package/export-template/docs/package.json +1 -0
  176. package/export-template/docs/tuning-reference/anti-patterns.html +25 -0
  177. package/export-template/docs/tuning-reference/aqe.html +25 -0
  178. package/export-template/docs/tuning-reference/bottleneck-broadcast-sizing.html +25 -0
  179. package/export-template/docs/tuning-reference/bottleneck-cold-start.html +31 -0
  180. package/export-template/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +25 -0
  181. package/export-template/docs/tuning-reference/bottleneck-failures.html +30 -0
  182. package/export-template/docs/tuning-reference/bottleneck-gc.html +30 -0
  183. package/export-template/docs/tuning-reference/bottleneck-job-failure-rate.html +32 -0
  184. package/export-template/docs/tuning-reference/bottleneck-memory-utilization.html +31 -0
  185. package/export-template/docs/tuning-reference/bottleneck-retry-waste.html +25 -0
  186. package/export-template/docs/tuning-reference/bottleneck-shuffle.html +36 -0
  187. package/export-template/docs/tuning-reference/bottleneck-skew.html +38 -0
  188. package/export-template/docs/tuning-reference/bottleneck-slow-host.html +31 -0
  189. package/export-template/docs/tuning-reference/bottleneck-small-files.html +29 -0
  190. package/export-template/docs/tuning-reference/bottleneck-spill.html +30 -0
  191. package/export-template/docs/tuning-reference/bottleneck-straggler.html +31 -0
  192. package/export-template/docs/tuning-reference/bottleneck-tiny-tasks.html +32 -0
  193. package/export-template/docs/tuning-reference/bottleneck-utilization.html +29 -0
  194. package/export-template/docs/tuning-reference/caching.html +25 -0
  195. package/export-template/docs/tuning-reference/cluster-config.html +25 -0
  196. package/export-template/docs/tuning-reference/config.html +25 -0
  197. package/export-template/docs/tuning-reference/data-formats.html +25 -0
  198. package/export-template/docs/tuning-reference/index.html +25 -0
  199. package/export-template/docs/tuning-reference/intro.html +25 -0
  200. package/export-template/docs/tuning-reference/joins.html +25 -0
  201. package/export-template/docs/tuning-reference/memory-model.html +25 -0
  202. package/export-template/docs/tuning-reference/metrics.html +25 -0
  203. package/export-template/docs/tuning-reference/partitioning.html +25 -0
  204. package/export-template/docs/tuning-reference/pyspark.html +30 -0
  205. package/export-template/docs/tuning-reference/shuffle.html +25 -0
  206. package/export-template/docs/tuning-reference/spark-architecture.html +25 -0
  207. package/export-template/docs/tuning-reference/table-formats.html +25 -0
  208. package/export-template/docs/user-guide/alternative-log-retrieval.html +25 -0
  209. package/export-template/docs/user-guide/getting-started.html +27 -0
  210. package/export-template/docs/user-guide/mcp-tools.html +149 -0
  211. package/export-template/docs/user-guide/run-comparison.html +25 -0
  212. package/export-template/docs/user-guide/understanding-findings.html +25 -0
  213. package/export-template/docs/vp-icons.css +0 -0
  214. package/export-template/favicon.svg +4 -0
  215. package/export-template/index.html +115 -0
  216. package/export-template/parser-worker-QqyEE4m9.js +64 -0
  217. package/package.json +16 -3
  218. package/vendor-core/analyzer.js +74 -74
  219. package/vendor-core/cli/budgets.js +13 -27
  220. package/vendor-core/cli/collect-run.js +43 -19
  221. package/vendor-core/core-count.js +25 -27
  222. package/vendor-core/core-locality-ratio.js +4 -11
  223. package/vendor-core/core-time-series.js +6 -12
  224. package/vendor-core/core-usage-locality.js +3 -4
  225. package/vendor-core/detectors.js +256 -375
  226. package/vendor-core/docs-config.js +69 -21
  227. package/vendor-core/docs-content/chapters/01-intro.md +32 -0
  228. package/vendor-core/docs-content/chapters/02-spark-architecture.md +76 -0
  229. package/vendor-core/docs-content/chapters/03-memory-model.md +73 -0
  230. package/vendor-core/docs-content/chapters/04-partitioning.md +65 -0
  231. package/vendor-core/docs-content/chapters/05-joins.md +62 -0
  232. package/vendor-core/docs-content/chapters/06-shuffle.md +59 -0
  233. package/vendor-core/docs-content/chapters/07-data-formats.md +81 -0
  234. package/vendor-core/docs-content/chapters/07b-table-formats.md +56 -0
  235. package/vendor-core/docs-content/chapters/08-caching.md +58 -0
  236. package/vendor-core/docs-content/chapters/09-pyspark.md +78 -0
  237. package/vendor-core/docs-content/chapters/10-aqe.md +167 -0
  238. package/vendor-core/docs-content/chapters/11-cluster-config.md +170 -0
  239. package/vendor-core/docs-content/chapters/12-anti-patterns.md +171 -0
  240. package/vendor-core/docs-content/chapters/14-metrics.md +87 -0
  241. package/vendor-core/docs-content/chapters/15-config.md +93 -0
  242. package/vendor-core/docs-content/chapters/nav-index.json +370 -0
  243. package/vendor-core/docs-content/detection/cache.md +6 -0
  244. package/vendor-core/docs-content/detection/cfg.md +15 -0
  245. package/vendor-core/docs-content/detection/chrn.md +7 -0
  246. package/vendor-core/docs-content/detection/cold.md +4 -0
  247. package/vendor-core/docs-content/detection/cstor.md +4 -0
  248. package/vendor-core/docs-content/detection/fail.md +5 -0
  249. package/vendor-core/docs-content/detection/gc.md +4 -0
  250. package/vendor-core/docs-content/detection/host.md +5 -0
  251. package/vendor-core/docs-content/detection/incmp.md +6 -0
  252. package/vendor-core/docs-content/detection/jobs.md +4 -0
  253. package/vendor-core/docs-content/detection/local.md +7 -0
  254. package/vendor-core/docs-content/detection/mem.md +10 -0
  255. package/vendor-core/docs-content/detection/part.md +5 -0
  256. package/vendor-core/docs-content/detection/plan.md +14 -0
  257. package/vendor-core/docs-content/detection/retry.md +4 -0
  258. package/vendor-core/docs-content/detection/sfail.md +5 -0
  259. package/vendor-core/docs-content/detection/shape.md +5 -0
  260. package/vendor-core/docs-content/detection/shfl.md +4 -0
  261. package/vendor-core/docs-content/detection/skew.md +6 -0
  262. package/vendor-core/docs-content/detection/slow.md +6 -0
  263. package/vendor-core/docs-content/detection/spec.md +7 -0
  264. package/vendor-core/docs-content/detection/spill.md +7 -0
  265. package/vendor-core/docs-content/detection/strag.md +5 -0
  266. package/vendor-core/docs-content/detection/tiny.md +4 -0
  267. package/vendor-core/docs-content/detection/util.md +4 -0
  268. package/vendor-core/docs-content/diagrams/aqe-loop.dark.svg +1 -0
  269. package/vendor-core/docs-content/diagrams/aqe-loop.svg +1 -0
  270. package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.dark.svg +1 -0
  271. package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.svg +1 -0
  272. package/vendor-core/docs-content/diagrams/cache-lifecycle.dark.svg +1 -0
  273. package/vendor-core/docs-content/diagrams/cache-lifecycle.svg +1 -0
  274. package/vendor-core/docs-content/diagrams/cold-start-timeline.dark.svg +1 -0
  275. package/vendor-core/docs-content/diagrams/cold-start-timeline.svg +1 -0
  276. package/vendor-core/docs-content/diagrams/columnar-layout.dark.svg +1 -0
  277. package/vendor-core/docs-content/diagrams/columnar-layout.svg +1 -0
  278. package/vendor-core/docs-content/diagrams/container-memory.dark.svg +1 -0
  279. package/vendor-core/docs-content/diagrams/container-memory.svg +1 -0
  280. package/vendor-core/docs-content/diagrams/dag-stages.dark.svg +1 -0
  281. package/vendor-core/docs-content/diagrams/dag-stages.svg +1 -0
  282. package/vendor-core/docs-content/diagrams/driver-executor.dark.svg +1 -0
  283. package/vendor-core/docs-content/diagrams/driver-executor.svg +1 -0
  284. package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.dark.svg +1 -0
  285. package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.svg +1 -0
  286. package/vendor-core/docs-content/diagrams/join-strategy.dark.svg +1 -0
  287. package/vendor-core/docs-content/diagrams/join-strategy.svg +1 -0
  288. package/vendor-core/docs-content/diagrams/memory-borrowing.dark.svg +1 -0
  289. package/vendor-core/docs-content/diagrams/memory-borrowing.svg +1 -0
  290. package/vendor-core/docs-content/diagrams/memory-regions.dark.svg +1 -0
  291. package/vendor-core/docs-content/diagrams/memory-regions.svg +1 -0
  292. package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.dark.svg +1 -0
  293. package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.svg +1 -0
  294. package/vendor-core/docs-content/diagrams/retry-escalation-ladder.dark.svg +1 -0
  295. package/vendor-core/docs-content/diagrams/retry-escalation-ladder.svg +1 -0
  296. package/vendor-core/docs-content/diagrams/shuffle-map-reduce.dark.svg +1 -0
  297. package/vendor-core/docs-content/diagrams/shuffle-map-reduce.svg +1 -0
  298. package/vendor-core/docs-content/diagrams/spill-classification.dark.svg +1 -0
  299. package/vendor-core/docs-content/diagrams/spill-classification.svg +1 -0
  300. package/vendor-core/docs-content/diagrams/udf-execution-models.dark.svg +1 -0
  301. package/vendor-core/docs-content/diagrams/udf-execution-models.svg +1 -0
  302. package/vendor-core/docs-content/tuning/broadcast-sizing.md +78 -0
  303. package/vendor-core/docs-content/tuning/cold-start.md +81 -0
  304. package/vendor-core/docs-content/tuning/duplicate-plan-subtree.md +45 -0
  305. package/vendor-core/docs-content/tuning/failures.md +124 -0
  306. package/vendor-core/docs-content/tuning/gc.md +110 -0
  307. package/vendor-core/docs-content/tuning/job-failure-rate.md +101 -0
  308. package/vendor-core/docs-content/tuning/memory-utilization.md +58 -0
  309. package/vendor-core/docs-content/tuning/retry-waste.md +90 -0
  310. package/vendor-core/docs-content/tuning/shuffle.md +154 -0
  311. package/vendor-core/docs-content/tuning/skew.md +123 -0
  312. package/vendor-core/docs-content/tuning/slow-host.md +117 -0
  313. package/vendor-core/docs-content/tuning/small-files.md +99 -0
  314. package/vendor-core/docs-content/tuning/spill.md +114 -0
  315. package/vendor-core/docs-content/tuning/straggler.md +103 -0
  316. package/vendor-core/docs-content/tuning/tiny-tasks.md +94 -0
  317. package/vendor-core/docs-content/tuning/utilization.md +90 -0
  318. package/vendor-core/docs-site-config.js +10 -17
  319. package/vendor-core/efficiency-model.js +7 -13
  320. package/vendor-core/etl-phases.js +3 -5
  321. package/vendor-core/event-handlers.js +232 -134
  322. package/vendor-core/event-schemas.js +48 -114
  323. package/vendor-core/evidence-availability.js +5 -10
  324. package/vendor-core/evidence-report.js +72 -122
  325. package/vendor-core/export-data.js +48 -0
  326. package/vendor-core/finding-action-label.js +4 -10
  327. package/vendor-core/finding-filter-predicate.js +3 -7
  328. package/vendor-core/finding-generic-recommendation.js +112 -0
  329. package/vendor-core/finding-names.js +51 -0
  330. package/vendor-core/format-utils.js +112 -38
  331. package/vendor-core/impact-band.js +18 -24
  332. package/vendor-core/impact-estimator.js +38 -74
  333. package/vendor-core/ingest.js +7 -13
  334. package/vendor-core/job-groups.js +3 -6
  335. package/vendor-core/list-runs.js +278 -0
  336. package/vendor-core/load-vendored.js +6 -12
  337. package/vendor-core/log-header-peek.js +81 -0
  338. package/vendor-core/lz4-block.js +4 -6
  339. package/vendor-core/mcp-server-factory.js +38 -8
  340. package/vendor-core/mcp-tools.js +105 -76
  341. package/vendor-core/model-assembler.js +8 -16
  342. package/vendor-core/occupancy.js +5 -9
  343. package/vendor-core/parser-worker.js +18 -27
  344. package/vendor-core/plan-dot.js +2 -5
  345. package/vendor-core/plan-duration-attribution.js +78 -29
  346. package/vendor-core/plan-graph-model.js +126 -69
  347. package/vendor-core/plan-node-detail.js +31 -17
  348. package/vendor-core/plan-summary.js +19 -8
  349. package/vendor-core/recommendation-rollup.js +35 -39
  350. package/vendor-core/redact.js +72 -16
  351. package/vendor-core/rolling-log-reassembly.js +4 -6
  352. package/vendor-core/run-comparison.js +65 -70
  353. package/vendor-core/scaling-sim.js +5 -7
  354. package/vendor-core/session-snapshot.js +1 -1
  355. package/vendor-core/shs-fetch.js +4 -6
  356. package/vendor-core/shs-load.js +9 -13
  357. package/vendor-core/shs-request.js +1 -1
  358. package/vendor-core/stage-quantiles.js +14 -0
  359. package/vendor-core/types.js +78 -18
  360. package/vendor-core/wasted-core-hours.js +7 -12
@@ -0,0 +1 @@
1
+ <svg id="gd34" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" viewBox="-8 -17.3125 730.3125 506.7875" role="graphics-document document" aria-roledescription="flowchart-v2" style="max-width: 730.3125px;"><style>#gd34{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;fill:#f4f4f5;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#gd34 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#gd34 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#gd34 .error-icon{fill:#1a232f;}#gd34 .error-text{fill:#e5dcd0;stroke:#e5dcd0;}#gd34 .edge-thickness-normal{stroke-width:1px;}#gd34 .edge-thickness-thick{stroke-width:3.5px;}#gd34 .edge-pattern-solid{stroke-dasharray:0;}#gd34 .edge-thickness-invisible{stroke-width:0;fill:none;}#gd34 .edge-pattern-dashed{stroke-dasharray:3;}#gd34 .edge-pattern-dotted{stroke-dasharray:2;}#gd34 .marker{fill:#a9b5b4;stroke:#a9b5b4;}#gd34 .marker.cross{stroke:#a9b5b4;}#gd34 svg{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;}#gd34 p{margin:0;}#gd34 .label{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;color:#f4f4f5;}#gd34 .cluster-label text{fill:#f4f4f5;}#gd34 .cluster-label span{color:#f4f4f5;}#gd34 .cluster-label span p{background-color:transparent;}#gd34 .label text,#gd34 span{fill:#f4f4f5;color:#f4f4f5;}#gd34 .node rect,#gd34 .node circle,#gd34 .node ellipse,#gd34 .node polygon,#gd34 .node path{fill:#17202c;stroke:#3a4a52;stroke-width:1px;}#gd34 .rough-node .label text,#gd34 .node .label text,#gd34 .image-shape .label,#gd34 .icon-shape .label{text-anchor:middle;}#gd34 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#gd34 .rough-node .label,#gd34 .node .label,#gd34 .image-shape .label,#gd34 .icon-shape .label{text-align:center;}#gd34 .node.clickable{cursor:pointer;}#gd34 .root .anchor path{fill:#a9b5b4!important;stroke-width:0;stroke:#a9b5b4;}#gd34 .arrowheadPath{fill:rgba(255, 255, 255, 0);}#gd34 .edgePath .path{stroke:#a9b5b4;stroke-width:1px;}#gd34 .flowchart-link{stroke:#a9b5b4;fill:none;}#gd34 .edgeLabel{background-color:#11171a;text-align:center;}#gd34 .edgeLabel p{background-color:#11171a;}#gd34 .edgeLabel rect{opacity:0.5;background-color:#11171a;fill:#11171a;}#gd34 .labelBkg{background-color:rgba(17, 23, 26, 0.5);}#gd34 .cluster rect{fill:#11171a;stroke:#233035;stroke-width:1px;}#gd34 .cluster text{fill:#f4f4f5;}#gd34 .cluster span{color:#f4f4f5;}#gd34 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:12px;background:#1a232f;border:1px solid hsl(214.2857142857, 0%, 4.3137254902%);border-radius:2px;pointer-events:none;z-index:100;}#gd34 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#f4f4f5;}#gd34 rect.text{fill:none;stroke-width:0;}#gd34 .icon-shape,#gd34 .image-shape{background-color:#11171a;text-align:center;}#gd34 .icon-shape p,#gd34 .image-shape p{background-color:#11171a;padding:2px;}#gd34 .icon-shape .label rect,#gd34 .image-shape .label rect{opacity:0.5;background-color:#11171a;fill:#11171a;}#gd34 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#gd34 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#gd34 .node .neo-node{stroke:#3a4a52;}#gd34 [data-look=&quot;neo&quot;].node rect,#gd34 [data-look=&quot;neo&quot;].cluster rect,#gd34 [data-look=&quot;neo&quot;].node polygon{stroke:url(#gd34-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd34 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#gd34 [data-look=&quot;neo&quot;].node path{stroke:url(#gd34-gradient);stroke-width:1px;}#gd34 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd34 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#3a4a52;filter:none;}#gd34 [data-look=&quot;neo&quot;].node circle{stroke:url(#gd34-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd34 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#gd34 [data-look=&quot;neo&quot;].icon-shape .icon{fill:url(#gd34-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd34 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:url(#gd34-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd34 :root{--mermaid-font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;}</style><g><marker id="gd34_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd34_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd34_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></path></marker><marker id="gd34_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></polygon></marker><marker id="gd34_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd34_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd34_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd34_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd34_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd34_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd34_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5"></path></marker><marker id="gd34_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5;stroke-dasharray:1,0"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M361.156,126.625L361.156,130.792C361.156,134.958,361.156,143.292,361.156,151.625C361.156,159.958,361.156,168.292,361.156,172.458L361.156,176.625" id="gd34-L_PLAIN_ARROW_0" class=" edge-thickness-invisible edge-pattern-solid" data-edge="true" data-et="edge" data-id="L_PLAIN_ARROW_0" data-points="W3sieCI6MzYxLjE1NjI1LCJ5IjoxMjYuNjI1fSx7IngiOjM2MS4xNTYyNSwieSI6MTUxLjYyNX0seyJ4IjozNjEuMTU2MjUsInkiOjE3Ni42MjV9XQ==" data-look="classic" style=";"></path><path d="M361.156,295.25L361.156,299.417C361.156,303.583,361.156,311.917,361.156,320.25C361.156,328.583,361.156,336.917,361.156,341.083L361.156,345.25" id="gd34-L_ARROW_PANDAS_0" class=" edge-thickness-invisible edge-pattern-solid" data-edge="true" data-et="edge" data-id="L_ARROW_PANDAS_0" data-points="W3sieCI6MzYxLjE1NjI1LCJ5IjoyOTUuMjV9LHsieCI6MzYxLjE1NjI1LCJ5IjozMjAuMjV9LHsieCI6MzYxLjE1NjI1LCJ5IjozNDUuMjV9XQ==" data-look="classic" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_PLAIN_ARROW_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_ARROW_PANDAS_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g></g><g class="nodes"><g class="root" transform="translate(0, 337.25)"><g class="clusters"><g class="cluster " id="gd34-PANDAS" data-look="classic"><rect x="8" y="8" width="706.3125" height="136.225"></rect><g class="cluster-label " transform="translate(262.21484375, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pandas</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (vectorized)</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,76.113L155.96,76.113C176.951,76.113,218.932,76.113,260.247,76.113C301.563,76.113,342.211,76.113,362.535,76.113L382.859,76.113" id="gd34-L_JVM3_PY3_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM3_PY3_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo3Ni4xMTI1fSx7IngiOjI2MC45MTQwNjI1LCJ5Ijo3Ni4xMTI1fSx7IngiOjM4Ni44NTkzNzUsInkiOjc2LjExMjV9XQ==" data-look="classic" marker-end="url(#gd34_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(260.9140625, 76.1125)"><g class="label" data-id="L_JVM3_PY3_0" transform="translate(0, -19.2609375)"><g><rect class="background" x="-88.4453125" y="-0.8515625" width="176.890625" height="40.225"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">whole</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Arrow</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> batches,</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">no</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> per-row</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> handoff</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd34-flowchart-JVM3-4" data-look="classic" transform="translate(90.234375, 76.1125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd34-flowchart-PY3-5" data-look="classic" transform="translate(531.8359375, 76.1125)"><rect class="basic label-container" x="-144.9765625" y="-33.1125" width="289.953125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(pandas</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Series</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> DataFrame)</tspan></tspan></text></g></g></g></g></g><g class="root" transform="translate(99.125, 168.625)"><g class="clusters"><g class="cluster " id="gd34-ARROW" data-look="classic"><rect x="8" y="8" width="508.0625" height="118.625"></rect><g class="cluster-label " transform="translate(146.67578125, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Arrow-optimized</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,67.313L153.98,67.313C172.991,67.313,211.013,67.313,248.368,67.313C285.724,67.313,322.413,67.313,340.757,67.313L359.102,67.313" id="gd34-L_JVM2_PY2_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM2_PY2_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjI0OS4wMzUxNTYyNSwieSI6NjcuMzEyNX0seyJ4IjozNjMuMTAxNTYyNSwieSI6NjcuMzEyNX1d" data-look="classic" marker-end="url(#gd34_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(249.03515625, 67.3125)"><g class="label" data-id="L_JVM2_PY2_0" transform="translate(0, -19.2609375)"><g><rect class="background" x="-76.56640625" y="-0.8515625" width="153.1328125" height="40.225"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Arrow</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> transfer,</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">still</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> row</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> semantics</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd34-flowchart-JVM2-2" data-look="classic" transform="translate(90.234375, 67.3125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd34-flowchart-PY2-3" data-look="classic" transform="translate(420.83203125, 67.3125)"><rect class="basic label-container" x="-57.73046875" y="-24.3125" width="115.4609375" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan></text></g></g></g></g></g><g class="root" transform="translate(87.22265625, 0)"><g class="clusters"><g class="cluster " id="gd34-PLAIN" data-look="classic"><rect x="8" y="8" width="531.8671875" height="118.625"></rect><g class="cluster-label " transform="translate(205.2578125, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Plain</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,67.313L155.964,67.313C176.958,67.313,218.948,67.313,260.271,67.313C301.594,67.313,342.25,67.313,362.578,67.313L382.906,67.313" id="gd34-L_JVM1_PY1_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM1_PY1_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjI2MC45Mzc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjM4Ni45MDYyNSwieSI6NjcuMzEyNX1d" data-look="classic" marker-end="url(#gd34_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(260.9375, 67.3125)"><g class="label" data-id="L_JVM1_PY1_0" transform="translate(0, -28.0609375)"><g><rect class="background" x="-88.46875" y="-0.8515625" width="176.9375" height="57.825"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pickle,</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> row-at-a-time</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(serialization</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> cost</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> per</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">row)</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd34-flowchart-JVM1-0" data-look="classic" transform="translate(90.234375, 67.3125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd34-flowchart-PY1-1" data-look="classic" transform="translate(444.63671875, 67.3125)"><rect class="basic label-container" x="-57.73046875" y="-24.3125" width="115.4609375" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan></text></g></g></g></g></g></g></g></g><defs><filter id="gd34-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><defs><filter id="gd34-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><linearGradient id="gd34-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#3a4a52" stop-opacity="1"></stop><stop offset="100%" stop-color="hsl(200, 0%, 0%)" stop-opacity="1"></stop></linearGradient></svg>
@@ -0,0 +1 @@
1
+ <svg id="gd33" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" viewBox="-8 -17.3125 730.3125 506.7875" role="graphics-document document" aria-roledescription="flowchart-v2" style="max-width: 730.3125px;"><style>#gd33{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;fill:#151b1e;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#gd33 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#gd33 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#gd33 .error-icon{fill:#eef1f6;}#gd33 .error-text{fill:#110e09;stroke:#110e09;}#gd33 .edge-thickness-normal{stroke-width:1px;}#gd33 .edge-thickness-thick{stroke-width:3.5px;}#gd33 .edge-pattern-solid{stroke-dasharray:0;}#gd33 .edge-thickness-invisible{stroke-width:0;fill:none;}#gd33 .edge-pattern-dashed{stroke-dasharray:3;}#gd33 .edge-pattern-dotted{stroke-dasharray:2;}#gd33 .marker{fill:#4f5f60;stroke:#4f5f60;}#gd33 .marker.cross{stroke:#4f5f60;}#gd33 svg{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:16px;}#gd33 p{margin:0;}#gd33 .label{font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;color:#151b1e;}#gd33 .cluster-label text{fill:#151b1e;}#gd33 .cluster-label span{color:#151b1e;}#gd33 .cluster-label span p{background-color:transparent;}#gd33 .label text,#gd33 span{fill:#151b1e;color:#151b1e;}#gd33 .node rect,#gd33 .node circle,#gd33 .node ellipse,#gd33 .node polygon,#gd33 .node path{fill:#ffffff;stroke:#9aa8a3;stroke-width:1px;}#gd33 .rough-node .label text,#gd33 .node .label text,#gd33 .image-shape .label,#gd33 .icon-shape .label{text-anchor:middle;}#gd33 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#gd33 .rough-node .label,#gd33 .node .label,#gd33 .image-shape .label,#gd33 .icon-shape .label{text-align:center;}#gd33 .node.clickable{cursor:pointer;}#gd33 .root .anchor path{fill:#4f5f60!important;stroke-width:0;stroke:#4f5f60;}#gd33 .arrowheadPath{fill:rgba(255, 255, 255, 0);}#gd33 .edgePath .path{stroke:#4f5f60;stroke-width:1px;}#gd33 .flowchart-link{stroke:#4f5f60;fill:none;}#gd33 .edgeLabel{background-color:#eef1f6;text-align:center;}#gd33 .edgeLabel p{background-color:#eef1f6;}#gd33 .edgeLabel rect{opacity:0.5;background-color:#eef1f6;fill:#eef1f6;}#gd33 .labelBkg{background-color:rgba(238, 241, 246, 0.5);}#gd33 .cluster rect{fill:#f2f5f9;stroke:#dbe2df;stroke-width:1px;}#gd33 .cluster text{fill:#151b1e;}#gd33 .cluster span{color:#151b1e;}#gd33 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;font-size:12px;background:#eef1f6;border:1px solid hsl(217.5, 0%, 84.9019607843%);border-radius:2px;pointer-events:none;z-index:100;}#gd33 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#151b1e;}#gd33 rect.text{fill:none;stroke-width:0;}#gd33 .icon-shape,#gd33 .image-shape{background-color:#eef1f6;text-align:center;}#gd33 .icon-shape p,#gd33 .image-shape p{background-color:#eef1f6;padding:2px;}#gd33 .icon-shape .label rect,#gd33 .image-shape .label rect{opacity:0.5;background-color:#eef1f6;fill:#eef1f6;}#gd33 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#gd33 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#gd33 .node .neo-node{stroke:#9aa8a3;}#gd33 [data-look=&quot;neo&quot;].node rect,#gd33 [data-look=&quot;neo&quot;].cluster rect,#gd33 [data-look=&quot;neo&quot;].node polygon{stroke:url(#gd33-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd33 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#gd33 [data-look=&quot;neo&quot;].node path{stroke:url(#gd33-gradient);stroke-width:1px;}#gd33 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd33 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9aa8a3;filter:none;}#gd33 [data-look=&quot;neo&quot;].node circle{stroke:url(#gd33-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd33 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#gd33 [data-look=&quot;neo&quot;].icon-shape .icon{fill:url(#gd33-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd33 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:url(#gd33-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#gd33 :root{--mermaid-font-family:Recursive,ui-sans-serif,system-ui,-apple-system,sans-serif;}</style><g><marker id="gd33_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd33_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker id="gd33_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></path></marker><marker id="gd33_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></polygon></marker><marker id="gd33_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd33_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker id="gd33_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd33_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width:0;stroke-dasharray:1,0"></circle></marker><marker id="gd33_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd33_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker id="gd33_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5"></path></marker><marker id="gd33_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width:2.5;stroke-dasharray:1,0"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path d="M361.156,126.625L361.156,130.792C361.156,134.958,361.156,143.292,361.156,151.625C361.156,159.958,361.156,168.292,361.156,172.458L361.156,176.625" id="gd33-L_PLAIN_ARROW_0" class=" edge-thickness-invisible edge-pattern-solid" data-edge="true" data-et="edge" data-id="L_PLAIN_ARROW_0" data-points="W3sieCI6MzYxLjE1NjI1LCJ5IjoxMjYuNjI1fSx7IngiOjM2MS4xNTYyNSwieSI6MTUxLjYyNX0seyJ4IjozNjEuMTU2MjUsInkiOjE3Ni42MjV9XQ==" data-look="classic" style=";"></path><path d="M361.156,295.25L361.156,299.417C361.156,303.583,361.156,311.917,361.156,320.25C361.156,328.583,361.156,336.917,361.156,341.083L361.156,345.25" id="gd33-L_ARROW_PANDAS_0" class=" edge-thickness-invisible edge-pattern-solid" data-edge="true" data-et="edge" data-id="L_ARROW_PANDAS_0" data-points="W3sieCI6MzYxLjE1NjI1LCJ5IjoyOTUuMjV9LHsieCI6MzYxLjE1NjI1LCJ5IjozMjAuMjV9LHsieCI6MzYxLjE1NjI1LCJ5IjozNDUuMjV9XQ==" data-look="classic" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_PLAIN_ARROW_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g><g class="edgeLabel"><g class="label" data-id="L_ARROW_PANDAS_0" transform="translate(0, -10.4609375)"><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"></tspan></text></g></g><g><rect class="background" style="stroke: none"></rect></g></g><g class="nodes"><g class="root" transform="translate(0, 337.25)"><g class="clusters"><g class="cluster " id="gd33-PANDAS" data-look="classic"><rect x="8" y="8" width="706.3125" height="136.225"></rect><g class="cluster-label " transform="translate(262.21484375, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pandas</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> (vectorized)</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,76.113L155.96,76.113C176.951,76.113,218.932,76.113,260.247,76.113C301.563,76.113,342.211,76.113,362.535,76.113L382.859,76.113" id="gd33-L_JVM3_PY3_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM3_PY3_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo3Ni4xMTI1fSx7IngiOjI2MC45MTQwNjI1LCJ5Ijo3Ni4xMTI1fSx7IngiOjM4Ni44NTkzNzUsInkiOjc2LjExMjV9XQ==" data-look="classic" marker-end="url(#gd33_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(260.9140625, 76.1125)"><g class="label" data-id="L_JVM3_PY3_0" transform="translate(0, -19.2609375)"><g><rect class="background" x="-88.4453125" y="-0.8515625" width="176.890625" height="40.225"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">whole</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Arrow</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> batches,</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">no</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> per-row</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> handoff</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd33-flowchart-JVM3-4" data-look="classic" transform="translate(90.234375, 76.1125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd33-flowchart-PY3-5" data-look="classic" transform="translate(531.8359375, 76.1125)"><rect class="basic label-container" x="-144.9765625" y="-33.1125" width="289.953125" height="66.225"></rect><g class="label" transform="translate(0, -18.1125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(pandas</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Series</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> /</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> DataFrame)</tspan></tspan></text></g></g></g></g></g><g class="root" transform="translate(99.125, 168.625)"><g class="clusters"><g class="cluster " id="gd33-ARROW" data-look="classic"><rect x="8" y="8" width="508.0625" height="118.625"></rect><g class="cluster-label " transform="translate(146.67578125, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Arrow-optimized</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,67.313L153.98,67.313C172.991,67.313,211.013,67.313,248.368,67.313C285.724,67.313,322.413,67.313,340.757,67.313L359.102,67.313" id="gd33-L_JVM2_PY2_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM2_PY2_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjI0OS4wMzUxNTYyNSwieSI6NjcuMzEyNX0seyJ4IjozNjMuMTAxNTYyNSwieSI6NjcuMzEyNX1d" data-look="classic" marker-end="url(#gd33_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(249.03515625, 67.3125)"><g class="label" data-id="L_JVM2_PY2_0" transform="translate(0, -19.2609375)"><g><rect class="background" x="-76.56640625" y="-0.8515625" width="153.1328125" height="40.225"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Arrow</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> transfer,</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">still</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> row</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> semantics</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd33-flowchart-JVM2-2" data-look="classic" transform="translate(90.234375, 67.3125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd33-flowchart-PY2-3" data-look="classic" transform="translate(420.83203125, 67.3125)"><rect class="basic label-container" x="-57.73046875" y="-24.3125" width="115.4609375" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan></text></g></g></g></g></g><g class="root" transform="translate(87.22265625, 0)"><g class="clusters"><g class="cluster " id="gd33-PLAIN" data-look="classic"><rect x="8" y="8" width="531.8671875" height="118.625"></rect><g class="cluster-label " transform="translate(205.2578125, 8)"><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Plain</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> Python</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> UDF</tspan></tspan></text></g></g></g></g><g class="edgePaths"><path d="M134.969,67.313L155.964,67.313C176.958,67.313,218.948,67.313,260.271,67.313C301.594,67.313,342.25,67.313,362.578,67.313L382.906,67.313" id="gd33-L_JVM1_PY1_0" class=" edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" data-edge="true" data-et="edge" data-id="L_JVM1_PY1_0" data-points="W3sieCI6MTM0Ljk2ODc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjI2MC45Mzc1LCJ5Ijo2Ny4zMTI1fSx7IngiOjM4Ni45MDYyNSwieSI6NjcuMzEyNX1d" data-look="classic" marker-end="url(#gd33_flowchart-v2-pointEnd)" style=";"></path></g><g class="edgeLabels"><g class="edgeLabel" transform="translate(260.9375, 67.3125)"><g class="label" data-id="L_JVM1_PY1_0" transform="translate(0, -28.0609375)"><g><rect class="background" x="-88.46875" y="-0.8515625" width="176.9375" height="57.825"></rect><text y="-10.1" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">pickle,</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> row-at-a-time</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">(serialization</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> cost</tspan><tspan font-style="normal" class="text-inner-tspan" font-weight="normal"> per</tspan></tspan><tspan class="text-outer-tspan row" x="0" y="2.1em" dy="1.1em" text-anchor="middle"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">row)</tspan></tspan></text></g></g></g></g><g class="nodes"><g class="node default " id="gd33-flowchart-JVM1-0" data-look="classic" transform="translate(90.234375, 67.3125)"><rect class="basic label-container" x="-44.734375" y="-24.3125" width="89.46875" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">JVM</tspan></tspan></text></g></g></g><g class="node default " id="gd33-flowchart-PY1-1" data-look="classic" transform="translate(444.63671875, 67.3125)"><rect class="basic label-container" x="-57.73046875" y="-24.3125" width="115.4609375" height="48.625"></rect><g class="label" transform="translate(0, -9.3125)"><rect></rect><g><rect class="background" style="stroke: none"></rect><text y="-10.1"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">Python</tspan></tspan></text></g></g></g></g></g></g></g></g><defs><filter id="gd33-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><defs><filter id="gd33-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"></feDropShadow></filter></defs><linearGradient id="gd33-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#9aa8a3" stop-opacity="1"></stop><stop offset="100%" stop-color="hsl(214.2857142857, 0%, 86.2745098039%)" stop-opacity="1"></stop></linearGradient></svg>
@@ -0,0 +1,78 @@
1
+ # Broadcast Sizing
2
+
3
+ <span class="tag">BROADCASTSIZING</span>
4
+
5
+ ## What it is
6
+
7
+ A join has two ways to move data. The [shuffle join](#joins) forces all-to-all traffic across the
8
+ cluster; the broadcast join ships one side out to every executor and skips the [shuffle](#shuffle)
9
+ entirely. `spark.sql.autoBroadcastJoinThreshold` is the size gate that picks between them:
10
+ it sets the largest table, in bytes, that Spark will broadcast when planning a join, and it
11
+ defaults to 10 MB.[^1][^2] Set it to `-1` and automatic broadcasting is off, so Spark always
12
+ shuffles.[^1]
13
+
14
+ <img class="light-only" src="../diagrams/broadcast-vs-shuffle.svg" alt="How the small side estimate against the broadcast threshold picks between broadcasting a copy to every executor for a local join and an all-to-all shuffle join, with the broadcast path's driver collect and executor memory risks noted.">
15
+ <img class="dark-only" src="../diagrams/broadcast-vs-shuffle.dark.svg" alt="How the small side estimate against the broadcast threshold picks between broadcasting a copy to every executor for a local join and an all-to-all shuffle join, with the broadcast path's driver collect and executor memory risks noted.">
16
+
17
+ Given a join's estimated table sizes and that gate, there are two ways the choice can
18
+ go wrong:
19
+
20
+ - **underBroadcast**: one side is small enough to broadcast but is being shuffled anyway. The
21
+ join pays for an all-to-all exchange it could have avoided.
22
+ - **overBroadcast**: a side that is too large is being broadcast, which risks running the
23
+ driver or the executors out of memory.
24
+
25
+ ## How it's detected
26
+
27
+ Each join carries an estimated size for the side in question, which is compared against
28
+ the broadcast threshold:
29
+
30
+ | Finding | Condition | What it means |
31
+ |---|---|---|
32
+ | `underBroadcast` | small side is being shuffled instead of broadcast | a cheap broadcast join was left on the table |
33
+ | `overBroadcast` | an oversized side is being broadcast | the broadcast is a memory hazard |
34
+
35
+ ## Why it matters
36
+
37
+ Broadcasting the small side sends a full copy of that table to every executor. It pays off
38
+ only when network bandwidth and executor memory are not the bottleneck; there, it is far cheaper
39
+ than the shuffle it replaces.[^3] That is the cost an `underBroadcast` finding is
40
+ pointing at: a small dimension going through an all-to-all exchange when a copy on each
41
+ executor would have run fast.
42
+
43
+ `overBroadcast` is the opposite mistake, and it fails hard. Pushing a table that is too large
44
+ through the broadcast path breaks in two ways: too-large task result errors and out-of-memory
45
+ failures, both because the oversized table has to be collected on the driver and shipped whole
46
+ to every executor.[^4] The size gate exists for exactly this reason: a copy on every executor is
47
+ harmless for a small table and a memory hazard for a large one.
48
+
49
+ ## How to fix it
50
+
51
+ For `underBroadcast`, let the small side broadcast. If its estimated size sits above the 10 MB
52
+ default but still fits comfortably in executor memory, raise
53
+ `spark.sql.autoBroadcastJoinThreshold` to cover it, or force the strategy with `broadcast(df)`
54
+ on the small side.[^1][^5]
55
+
56
+ For `overBroadcast`, keep the large side out of the broadcast path. Lower the threshold so the
57
+ oversized table no longer qualifies, or drop the explicit `broadcast()` hint if one is forcing
58
+ it, and let Spark fall back to a shuffle join.
59
+
60
+ ## Confidence
61
+
62
+ Validated for both branches. `underBroadcast` and `overBroadcast` rest on the same
63
+ well-documented mechanism: the broadcast threshold and its two failure modes are standard Spark
64
+ behavior, so neither branch is experimental.
65
+
66
+ ## Limitations / false-positive risk
67
+
68
+ The comparison is only as good as the size estimate behind it. Table sizes come from
69
+ statistics that may be stale or missing, so a join that looks mis-sized can be measuring the wrong number. A
70
+ large broadcast can also be deliberate: an operator who has sized executor memory for it may
71
+ broadcast a table well past the default on purpose.
72
+
73
+
74
+ [^1]: [Configuration (Spark)](https://spark.apache.org/docs/latest/configuration.html)
75
+ [^2]: [Performance Tuning (Spark SQL, DataFrames and Datasets Guide)](https://spark.apache.org/docs/latest/sql-performance-tuning.html)
76
+ [^3]: *Learning Spark, 2nd Edition*, Damji, Wenig, Das, Lee, ch. 7
77
+ [^4]: *High Performance Spark, 2nd Edition*, Karau, Polak & Warren, ch. 6
78
+ [^5]: [The Apache Spark Optimization Checklist](https://luminousmen.com/post/the-apache-spark-optimization-checklist)
@@ -0,0 +1,81 @@
1
+ # Cold Start
2
+ <span class="tag">COLD</span>
3
+
4
+ ## What it is
5
+
6
+ Cold start is the gap between when a Spark application starts and when it actually begins doing
7
+ work: the delay before the [DAGScheduler](#spark-architecture) hands off the first stage for execution. In the event
8
+ log, that moment is marked by `SparkListenerStageSubmitted`, whose payload is a direct
9
+ serialization of a `StageInfo`: the event name plus the `stageInfo` and any properties, with no
10
+ task-level data attached[^1]. That shape matches what the event records. The DAGScheduler
11
+ doesn't pre-schedule the whole DAG upfront (it reacts to stage completions, unlocking child
12
+ stages one at a time), and once it has carved the DAG into stages and handed a `TaskSet` for the
13
+ next stage to the TaskScheduler, only then does the TaskScheduler assign individual tasks to
14
+ executors[^2]. `SparkListenerStageSubmitted` therefore captures the moment the DAGScheduler
15
+ creates/submits that `TaskSet`, before any task has actually been placed on an executor.
16
+
17
+ ## How it's detected
18
+
19
+ `firstStageSubmittedAt − app.startTime` > 30 s → Warning. `firstStageSubmittedAt` is a synthetic
20
+ field: the timestamp of the first `SparkListenerStageSubmitted` event in the event log.
21
+
22
+ <img class="light-only" src="../diagrams/cold-start-timeline.svg" alt="A timeline from app.startTime through the gap where executors are acquired and no tasks run to firstStageSubmittedAt, with that gap marked as the cold-start delay.">
23
+ <img class="dark-only" src="../diagrams/cold-start-timeline.dark.svg" alt="A timeline from app.startTime through the gap where executors are acquired and no tasks run to firstStageSubmittedAt, with that gap marked as the cold-start delay.">
24
+
25
+ ## Why it matters
26
+
27
+ Because the event carries only stage-level metadata and fires ahead of any executor task
28
+ placement[^1][^2], a large gap here reflects time spent before the DAGScheduler could submit
29
+ work at all, not time spent inside tasks. It's a signal about scheduling/startup latency,
30
+ distinct from the per-task metrics that describe work once tasks are actually running on
31
+ executors.
32
+
33
+ ## How to fix it
34
+
35
+ Dynamic-allocation ramp-up isn't directly tied to `firstStageSubmittedAt` timing, but the
36
+ configuration below governs how many executors an application has available at launch, and how
37
+ reliably that number can grow:
38
+
39
+ - Size executors deliberately instead of defaulting to one large executor per node. For a
40
+ 10-node, 16-core/64GB cluster targeting ~5 cores per executor, the standard derivation reserves
41
+ 1 core per node for daemons (15 usable cores/node, 150 total), divides by 5 cores/executor
42
+ (30), and subtracts 1 for the YARN ApplicationMaster: 29 executors × 5 cores × ~18GB[^3].
43
+ Cloudera's equivalent 6-node walkthrough lands on the same shape: 17 executors × 5 cores × 19G,
44
+ rather than one fat executor per node[^4].
45
+ - If dynamic allocation is enabled, its ability to add executors at all depends on one of a few
46
+ supporting mechanisms being turned on. One such mechanism is
47
+ `spark.dynamicAllocation.shuffleTracking.enabled` (default `true` since Spark 3.0), which
48
+ "enables shuffle file tracking for executors, which allows dynamic allocation without the need
49
+ for an external shuffle service," and "will try to keep alive executors that are storing
50
+ shuffle data for active jobs"[^5]. Without shuffle tracking or an external shuffle service
51
+ configured, dynamic allocation cannot be enabled at all[^5].
52
+
53
+ Deliberate executor sizing plus the dynamic-allocation precondition (values from the 16-core/64GB-node derivation above):
54
+
55
+ ```properties
56
+ # ~5 cores per executor instead of one fat executor per node
57
+ spark.executor.cores=5
58
+ spark.executor.memory=18g
59
+
60
+ # Dynamic allocation, with its required shuffle-tracking precondition (default true since Spark 3.0)
61
+ spark.dynamicAllocation.enabled=true
62
+ spark.dynamicAllocation.shuffleTracking.enabled=true
63
+ ```
64
+
65
+ ## Limitations / false-positive risk
66
+
67
+ Some startup latency is unavoidable: a cluster still has to provision before it can run
68
+ anything. The signal measures the gap before the first stage is submitted, and that gap
69
+ can legitimately vary by cluster manager, so a warning here doesn't always mean something
70
+ is wrong.
71
+
72
+
73
+ ## Related
74
+
75
+ - **Executor sizing & dynamic allocation:** [Cluster Tuning](#cluster-config)
76
+
77
+ [^1]: [JsonProtocol.scala](https://raw.githubusercontent.com/apache/spark/v3.5.0/core/src/main/scala/org/apache/spark/util/JsonProtocol.scala)
78
+ [^2]: [Anatomy of a Spark Application](https://luminousmen.com/post/spark-anatomy-of-spark-application)
79
+ [^3]: [Distribution of Executors, Cores and Memory for a Spark Application](https://raw.githubusercontent.com/spoddutur/spark-notes/master/distribution_of_executors_cores_and_memory_for_spark_application.md)
80
+ [^4]: [How to Tune Your Apache Spark Jobs (Part 2)](https://blog.cloudera.com/how-to-tune-your-apache-spark-jobs-part-2/)
81
+ [^5]: [Configuration (Spark)](https://spark.apache.org/docs/latest/configuration.html)
@@ -0,0 +1,45 @@
1
+ # Duplicate Plan Subtree
2
+
3
+ <span class="tag">DUPPLAN</span>
4
+
5
+ ## What it is
6
+
7
+ The physical plan is the form Spark actually runs: after optimizing the logical plan, the engine produces a plan that "specifies how the logical plan will execute on the cluster," compiling the query into a series of RDDs and transformations[^1]. A duplicated subtree is the same source read, together with the operators stacked above it, appearing in more than one branch of that plan. When the same logic sits in two different branches of a pipeline, you are "basically signing up to recompute everything from scratch multiple times," because every action on an uncached DataFrame starts back at the source and rebuilds all the intermediate steps[^2].
8
+
9
+ ## How it's detected
10
+
11
+ Exchanges are the giveaway. A [shuffle](#shuffle) or broadcast is an exchange, and these are the points where Spark's pipelining stops: "Spark operators are often pipelined and executed in parallel processes. However, a shuffle or broadcast exchange breaks this pipeline. We call them materialization points and use the term 'query stages' to denote subsections bounded by these materialization points"[^3]. The signal is the same source-plus-operators subtree feeding more than one branch across those materialization boundaries, which is the shape that gets recomputed unless something intervenes.
12
+
13
+ <img class="light-only" src="../diagrams/duplicate-plan-subtree.svg" alt="Before and after: the same scan and operators feed two branches and run twice, then a single shared node from exchange reuse or a cache feeds both consumers.">
14
+ <img class="dark-only" src="../diagrams/duplicate-plan-subtree.dark.svg" alt="Before and after: the same scan and operators feed two branches and run twice, then a single shared node from exchange reuse or a cache feeds both consumers.">
15
+
16
+ | Signal | What it points to |
17
+ |---|---|
18
+ | Identical scan + operators in two or more branches | Same subtree recomputed per branch |
19
+ | A materialization point (shuffle/broadcast) below the repeat | The pipeline breaks and the branch restarts from source |
20
+
21
+ ## Why it matters
22
+
23
+ Left alone, a repeated subtree is repeated work. Each branch re-reads the source and re-runs everything above it, so a subtree that shows up twice is roughly paid for twice[^2]. That cost lands on exactly the expensive parts of a plan (scans and shuffles) rather than on cheap row-level operators.
24
+
25
+ ## How to fix it
26
+
27
+ Two mechanisms let a plan reuse an identical subtree instead of rebuilding it.
28
+
29
+ - Exchange reuse leans on the fact that a shuffle already writes its output to disk. Spark "always executes shuffles by first having the 'source' tasks ... write shuffle files to their local disks," so "running a new job over data that's already been shuffled does not rerun the 'source' side of the shuffle. Because the shuffle files were already written to disk earlier, Spark knows that it can use them to run the later stages of the job, and it need not redo the earlier ones. In the Spark UI and logs, you will see the pre-shuffle stages marked as 'skipped'"[^1]. When the same materialized exchange feeds two consumers, the second reads those existing shuffle files instead of recomputing the subtree beneath it.
30
+ - `cache()` / `persist()` truncate recomputation by materializing the result and keying future lookups on it. Persisting "means materializing an RDD (usually by storing it in memory on the executors) for reuse during the current job"[^2], and in the Structured API the lookup is plan-keyed: "caching is done based on the physical plan. This means that we effectively store the physical plan as our key ... and perform a lookup prior to the execution of a Structured job"[^1]. On a hit, Spark returns the stored data rather than rebuilding intermediate steps from the source[^2].
31
+
32
+ > **PySpark:** if the repeated subtree is a DataFrame you reuse across branches, `df.cache()` (or `df.persist()`) before the branches split lets both read the materialized result instead of recomputing it.
33
+
34
+ ## Confidence
35
+
36
+ Medium. Inferring a duplicated subtree from the static plan is a medium-confidence signal that requires runtime validation: whether a repeated subtree actually turns into avoided work depends on runtime facts the plan never shows, such as cache hits, retained shuffle files, and eviction[^2].
37
+
38
+ ## Limitations / false-positive risk
39
+
40
+ Two subtrees that look identical in the printed plan are not always the same recomputed work. The cache "is tied to the analyzed logical plan, not the final optimized one," so semantically identical queries with different analyzed plans miss the cache and recompute even though "the optimizer will eventually produce the same physical plan"[^2]. [Caching](#caching) is also lazy and often partial: it happens "only as they are accessed," "the only blocks that get cached are the ones Spark was forced to touch," and Spark gives "no built-in visibility into how much of your DataFrame is actually cached, or how much has already been evicted"[^2]. Plan-level inference can therefore over-count, flagging repetition that either never materializes or is only partly reused.
41
+
42
+
43
+ [^1]: [Spark: The Definitive Guide](https://www.oreilly.com/library/view/spark-the-definitive/9781491912201/), Chambers & Zaharia, chs. 4, 15, 19
44
+ [^2]: [Explaining the mechanics of Spark caching](https://luminousmen.com/post/explaining-the-mechanics-of-spark-caching)
45
+ [^3]: [Adaptive Query Execution: Speeding Up Spark SQL at Runtime](https://www.databricks.com/blog/2020/05/29/adaptive-query-execution-speeding-up-spark-sql-at-runtime.html)
@@ -0,0 +1,124 @@
1
+ # Task Failures
2
+
3
+ <span class="tag">FAIL</span>
4
+
5
+ ## What it is
6
+
7
+ A task failure is any task end whose `Reason` field is something other than success. Spark's
8
+ event log serializes this as the formatted class name of whichever `TaskEndReason` instance was
9
+ assigned to that task attempt[^1], and the canonical set of reasons is `Success`, `FetchFailed`,
10
+ `ExceptionFailure`, `TaskResultLost`, `TaskKilled`, `TaskCommitDenied`, `ExecutorLostFailure`,
11
+ `Resubmitted`, and `UnknownReason`[^1]. Each carries its own extra detail: a `FetchFailed` records
12
+ a reduce ID and message; an `ExceptionFailure` carries the exception's class name, description,
13
+ and stack trace, plus accumulator updates (falling back to reading them out of the task's metrics
14
+ object for logs written by Spark 1.x); a `TaskKilled` records a kill reason; a `TaskCommitDenied`
15
+ records the job ID, partition ID, and attempt number; an `ExecutorLostFailure` records whether the
16
+ loss was caused by the application, the executor ID, and a loss reason; `UnknownReason` carries no
17
+ extra fields[^1].
18
+
19
+ ## How it's detected
20
+
21
+ | failed tasks (share) | Level |
22
+ |---|---|
23
+ | > 5% | Warning |
24
+ | > 20% | Critical |
25
+
26
+ The task-metrics object attached to a `SparkListenerTaskEnd` event is optional in the schema, so
27
+ it isn't guaranteed to be present for every failure. It tends to be populated for reasons where the
28
+ attempt actually ran far enough to produce metrics (an `ExceptionFailure` or `TaskKilled`, for
29
+ instance) but can be absent for a `Resubmitted` attempt that never completed on its executor at
30
+ all[^1]. Detection has to key off the `Reason` field itself rather than assume metrics will always
31
+ be there to inspect.
32
+
33
+ ## Why it matters
34
+
35
+ Some reasons point at something worse than a one-off hiccup. An
36
+ `ExecutorLostFailure` can be the knock-on effect of a container or pod killed for exceeding its
37
+ memory grant: enabling off-heap memory without also raising `spark.executor.memoryOverhead` (or
38
+ setting `spark.executor.pyspark.memory` explicitly) lets real usage exceed what YARN or Kubernetes
39
+ allocated, and the executor is killed with a generic container-memory error rather than a
40
+ Spark-level exception[^2]. Losing an executor mid-shuffle can also cascade into failures elsewhere:
41
+ if dynamic allocation removes an executor before a shuffle it wrote data for has completed, that
42
+ shuffle output is gone, and downstream tasks reading it fail and force an unnecessary recompute[^3].
43
+
44
+ ## How to fix it
45
+
46
+ - Read the `Reason` field before treating all failures the same: an `ExceptionFailure` points at
47
+ application code, and an `ExecutorLostFailure` usually points at memory sizing or infrastructure.
48
+ - For memory-driven `ExecutorLostFailure`s, size `spark.executor.memoryOverhead` (which defaults
49
+ to `max(384 MB, 10% of executor memory)`) to actually cover off-heap and PySpark memory, since
50
+ neither is folded into that default budget automatically[^2].
51
+ - For failures caused by dynamic allocation reclaiming an executor mid-shuffle, enable the external
52
+ [shuffle service](#shuffle) or `spark.dynamicAllocation.shuffleTracking.enabled` so shuffle output can outlive
53
+ the executor that wrote it, instead of being lost and recomputed[^3].
54
+
55
+ Config-only levers for memory-driven `ExecutorLostFailure`s and mid-shuffle executor loss:
56
+
57
+ ```properties
58
+ # Cover off-heap + PySpark memory the default overhead budget does NOT include.
59
+ # Default is max(384m, 10% of executor memory); 2g is an example, size to real off-heap/PySpark use.
60
+ spark.executor.memoryOverhead=2g
61
+
62
+ # Keep shuffle output alive when dynamic allocation reclaims an executor mid-shuffle
63
+ spark.dynamicAllocation.shuffleTracking.enabled=true
64
+ ```
65
+
66
+ ## Failed stage
67
+
68
+ <span class="tag">SFAIL</span>
69
+
70
+ A single task failing and a whole stage failing are handled by different parts of the
71
+ scheduler. Task retries sit in the TaskScheduler; the stage-level verdict belongs to the
72
+ [DAGScheduler](#spark-architecture), which decides whether the job as a whole lives or dies. When a stage fails,
73
+ it is not abandoned on the spot: the DAGScheduler resubmits it as a fresh attempt, and the
74
+ job only fails once the stage cannot make progress after repeated attempts[^5]. So the
75
+ difference between "retried and succeeds" and "aborts the job" is whether a later
76
+ attempt finishes before the attempt budget runs out.
77
+
78
+ <img class="light-only" src="../diagrams/retry-escalation-ladder.svg" alt="The layered budgets a repeated failure climbs through, from task retries to stage resubmission to the executor and application ceilings that abort the job.">
79
+ <img class="dark-only" src="../diagrams/retry-escalation-ladder.dark.svg" alt="The layered budgets a repeated failure climbs through, from task retries to stage resubmission to the executor and application ceilings that abort the job.">
80
+
81
+ That budget is `spark.stage.maxConsecutiveAttempts`, default 4: the number of consecutive
82
+ stage attempts allowed before the stage is aborted[^4]. Fail, resubmit, and complete within
83
+ those attempts and the job stays alive; keep failing until the limit is spent and the job
84
+ aborts.
85
+
86
+ A `FetchFailed` is the classic trigger. It counts against that stage attempt limit, which is
87
+ why `spark.stage.ignoreDecommissionFetchFailure` (default `true` since 3.4.0) exists: it keeps
88
+ a fetch failure caused by an executor being decommissioned from counting toward
89
+ `spark.stage.maxConsecutiveAttempts`, which by implication means ordinary fetch failures
90
+ otherwise do count toward the abort budget[^4].
91
+
92
+ Executor exclusion is the other stage-level lever. The `spark.excludeOnFailure.*` family is
93
+ off by default (`spark.excludeOnFailure.enabled` is `false`) and tracks how many tasks must
94
+ fail on one executor within a stage before that executor is excluded for the stage; it can
95
+ also kill executors excluded on a fetch failure[^4]. If exclusion leaves a TaskSet with no
96
+ schedulable executor left, `spark.scheduler.excludeOnFailure.unschedulableTaskSetTimeout`
97
+ (default 120s) caps how long Spark waits to acquire a new executor before aborting that
98
+ TaskSet[^4].
99
+
100
+ ## Confidence
101
+
102
+ The failed-task-share thresholds and the stage-failure behaviour both map to
103
+ documented Spark scheduler mechanics; no branch here is experimental.
104
+
105
+ ## Limitations / false-positive risk
106
+
107
+ A high failed-task share is a blunt signal. It can be dominated by one systemic cause, such
108
+ as a single lost executor whose tasks all fail at once, rather than many independent
109
+ failures pointing at a code or data problem. Transient failures that a later attempt retries
110
+ and succeeds are also not always harmful: task retries reset their count on success, so a
111
+ job can carry a nonzero failure share and still finish correctly. Read the `Reason`
112
+ distribution before treating the share as a defect.
113
+
114
+
115
+ ## Related
116
+
117
+ - **Executor sizing:** [Cluster Tuning](#cluster-config)
118
+ - **Memory overhead & off-heap:** [Memory Management](#memory-model)
119
+
120
+ [^1]: [JsonProtocol.scala](https://raw.githubusercontent.com/apache/spark/v3.5.0/core/src/main/scala/org/apache/spark/util/JsonProtocol.scala)
121
+ [^2]: [Dive Into Spark Memory](https://luminousmen.com/post/dive-into-spark-memory)
122
+ [^3]: [Job Scheduling (Apache Spark Documentation)](https://spark.apache.org/docs/latest/job-scheduling.html)
123
+ [^4]: [Spark Configuration (Apache Spark Documentation)](https://spark.apache.org/docs/latest/configuration.html)
124
+ [^5]: [Anatomy of a Spark Application](https://luminousmen.com/post/spark-anatomy-of-spark-application)
@@ -0,0 +1,110 @@
1
+ # GC Pressure
2
+
3
+ <span class="tag">GC</span>
4
+
5
+ ## What it is
6
+
7
+ GC pressure describes how much of an executor's time goes to JVM garbage collection instead
8
+ of running task code. Spark tracks this directly in its per-task metrics: `jvmGCTime` sits
9
+ alongside `executorRunTime`, the elapsed wall-clock time the executor spent running the task
10
+ (as opposed to `executorCpuTime`, which measures CPU time specifically).[^1] `jvmGCTime` is not
11
+ additive on top of that duration: it's defined as the elapsed time the JVM spent in garbage
12
+ collection *while executing the task*, so GC pauses fall inside the same wall-clock window
13
+ `executorRunTime` already measures, not outside it. Summing the two would double-count the GC
14
+ pauses and overstate how long the task actually ran.[^1]
15
+
16
+ ## How it's detected
17
+
18
+ Because `jvmGCTime` sits inside `executorRunTime` rather than alongside it, the ratio between
19
+ the two gives a bounded read on how much of a task's wall-clock time went to garbage collection:
20
+
21
+ | gcPct = jvmGCTime / executorRunTime | Level |
22
+ |---|---|
23
+ | > 10% | Warning |
24
+ | > 20% | Critical |
25
+
26
+ ## Why it matters
27
+
28
+ As gcPct climbs past these thresholds, a growing share of every task's wall-clock time is
29
+ consumed by garbage collection instead of actual computation, so the executor's useful
30
+ throughput drops even while it appears busy. Two causes show up repeatedly: oversized
31
+ executors, and an on-heap memory split pushed too far toward execution/storage.
32
+
33
+ Sizing an executor with too many cores relative to its heap is one documented trigger: a
34
+ fat-executor configuration using all 16 cores of a node was observed to not only hurt HDFS
35
+ throughput but also "result in excessive garbage [collection]."[^2]
36
+
37
+ Pushing `spark.memory.fraction` too far toward its upper end has a similar effect through a
38
+ different path. Its complement, `1 − spark.memory.fraction`, is untracked "User Memory"
39
+ reserved for UDFs, Python/Arrow glue, and native library buffers; Spark doesn't manage this
40
+ region at all, and starving it (which is what raising `spark.memory.fraction` toward
41
+ something like 0.9 does) leads to "GC pressure or random OOMs," with no warning before the
42
+ failure.[^3]
43
+
44
+ ## How to fix it
45
+
46
+ - Keep cores per executor down rather than packing an entire node's cores onto one JVM (the
47
+ common ~5-cores-per-executor guideline). That avoids the excessive-garbage-collection
48
+ failure mode documented for fat-executor configurations.[^2]
49
+ - Don't raise `spark.memory.fraction` past a comfortable range chasing more execution/storage
50
+ memory: doing so starves the untracked user-memory region and risks the same
51
+ GC-pressure/OOM failure mode.[^3]
52
+ - Move Tungsten's execution and storage buffers off the JVM heap with
53
+ `spark.memory.offHeap.enabled=true` (and a positive `spark.memory.offHeap.size`, which is
54
+ required whenever off-heap is enabled[^4]). Off-heap buffers are invisible to the garbage
55
+ collector, so fewer and smaller live objects need to be tracked, scanned, and copied on the
56
+ heap, reducing both the frequency and duration of GC pauses.[^3]
57
+ - Tune or switch the GC collector on large heaps. G1GC is Spark's default collector since 4.0
58
+ (which defaults to JDK 17), and large executor heaps may need `-XX:G1HeapRegionSize` raised
59
+ as well.[^5] G1's stop-the-world evacuation and full-GC pauses can still spike under heavy
60
+ old-generation pressure on large heaps: one documented 88GB-heap benchmark saw a job's pause
61
+ spike to nearly 100 seconds under default G1 settings, and only tuning
62
+ `InitiatingHeapOccupancyPercent`, `ConcGCThreads`, and RSet-update parameters brought G1 back
63
+ ahead of Parallel/CMS on both throughput and latency.[^6] ZGC is a documented alternative
64
+ built for pause-time control: it performs its expensive work concurrently, without stopping
65
+ application threads for more than a millisecond, with pause times designed to stay
66
+ independent of heap size from a few hundred megabytes up to 16TB.[^7]
67
+
68
+ Move buffers off-heap and give G1 room on large heaps (sizes are examples; set to your workload):
69
+
70
+ ```properties
71
+ # Move Tungsten execution/storage buffers off the JVM heap so the GC has fewer objects to scan
72
+ spark.memory.offHeap.enabled=true
73
+ spark.memory.offHeap.size=2g # must be > 0 whenever off-heap is enabled; 2g is an example
74
+
75
+ # G1 is the default collector since Spark 4.0 (JDK 17); raise the region size on large heaps
76
+ spark.executor.extraJavaOptions=-XX:+UseG1GC -XX:G1HeapRegionSize=16m # 16m is an example
77
+ ```
78
+
79
+ ## Confidence
80
+
81
+ The warning and critical thresholds on gcPct are validated: they read a bounded ratio of
82
+ `jvmGCTime` to `executorRunTime`, both first-class per-task metrics Spark records directly,[^1]
83
+ and crossing 10% or 20% marks a real, measurable share of wall-clock time lost to garbage
84
+ collection.
85
+
86
+ <span class="tag">EXPERIMENTAL</span> A separate signal treats suspiciously *low* GC time as a
87
+ possible cost-model or noise-floor indicator, and that one is an unsourced heuristic with no
88
+ external basis, so treat it as exploratory rather than a confirmed diagnostic.
89
+
90
+ ## Limitations / false-positive risk
91
+
92
+ A high gcPct can reflect a transient memory spike (a single skewed task, a brief burst of
93
+ large allocations) rather than chronic pressure, so a threshold crossing on one stage does not
94
+ by itself prove a sustained sizing problem; corroborate against the executor's behavior over
95
+ the whole run. The low-GC signal has no external basis at all, and a near-zero reading is at
96
+ least as likely to mean the workload simply is not allocation-heavy as to mean anything is
97
+ wrong.
98
+
99
+ ## Related
100
+
101
+ - **Why it happens:** [Memory Management](#memory-model)
102
+ - **Executor sizing:** [Cluster Tuning](#cluster-config)
103
+
104
+ [^1]: [Monitoring and Instrumentation](https://spark.apache.org/docs/latest/monitoring.html#spark-history-server)
105
+ [^2]: [Distribution of Executors, Cores, and Memory for a Spark Application](https://raw.githubusercontent.com/spoddutur/spark-notes/master/distribution_of_executors_cores_and_memory_for_spark_application.md)
106
+ [^3]: [Diving into Spark Memory Management](https://luminousmen.com/post/dive-into-spark-memory)
107
+ [^4]: [Configuration (Spark)](https://spark.apache.org/docs/latest/configuration.html)
108
+ [^5]: [Spark Tuning Guide](https://spark.apache.org/docs/latest/tuning.html)
109
+ [^6]: [Tuning Java Garbage Collection for Apache Spark Applications](https://www.databricks.com/blog/2015/05/28/tuning-java-garbage-collection-for-spark-applications.html)
110
+ [^7]: [The Z Garbage Collector (ZGC)](https://wiki.openjdk.org/display/zgc)