diff --git a/src/assets/scss/_plan-node.scss b/src/assets/scss/_plan-node.scss index 36cdb28f..51abdd99 100644 --- a/src/assets/scss/_plan-node.scss +++ b/src/assets/scss/_plan-node.scss @@ -2,6 +2,12 @@ $compact-width: 50px; $bg-color: #fff; +.execution-memory-node { + border-radius: 4px; + padding: 10px 8px; + background-color: #f9f9f9; + margin: 10px 0; +} .plan-node { cursor: default; text-decoration: none; diff --git a/src/components/Plan.vue b/src/components/Plan.vue index d40f50a7..5251cafc 100644 --- a/src/components/Plan.vue +++ b/src/components/Plan.vue @@ -47,6 +47,12 @@ import { type FlexHierarchyPointLink, type FlexHierarchyPointNode, } from "d3-flextree" +import { + faChevronLeft, + faChevronRight, +} from "@fortawesome/free-solid-svg-icons" +import SliceDetail from "@/components/SliceDetail.vue" +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" interface Props { planSource: string @@ -64,6 +70,23 @@ const plan = ref() const planEl = ref() let planStats = reactive({} as IPlanStats) const rootNode = computed(() => plan.value && plan.value.content.Plan) +const Slice = computed(() => plan.value && plan.value.content.Slice) +const showSlice = ref(true) + +// Determine if there is a Data Segments attribute +const hasDataSegments = computed(() => { + if (!rootNode.value || !rootNode.value.Plans) { + return false + } + const plans = rootNode.value.Plans + for (let i = 0; i < plans.length; i++) { + if (plans[i]["Data Slice Count"] !== undefined) { + return true + } + } + return false +}) + const selectedNodeId = ref(NaN) const selectedNode = ref(undefined) const highlightedNodeId = ref(NaN) @@ -80,6 +103,12 @@ const planService = new PlanService() // Vertical padding between 2 nodes in the tree layout const padding = 40 + +// Parallel node digital offset +const numPositionX = 20 +const numPositionY1 = 10 +const numPositionY2 = 35 + const transform = ref("") const scale = ref(1) const edgeWeight = computed(() => { @@ -138,6 +167,8 @@ onBeforeMount(() => { (content["Total Runtime"] as number) || NaN planStats.planningTime = (content["Planning Time"] as number) || NaN + planStats.memoryUsed = (content["Memory used"] as number) || NaN + planStats.optimizer = (content["Optimizer"] as string) || "" planStats.maxRows = content.maxRows || NaN planStats.maxCost = content.maxCost || NaN planStats.maxDuration = content.maxDuration || NaN @@ -614,6 +645,32 @@ function updateNodeSize(node: Node, size: [number, number]) { +
+ + + + +
+
+ +
+
+
+ @@ -645,6 +702,33 @@ function updateNodeSize(node: Node, size: [number, number]) { stroke-linecap="square" fill="none" /> + + + {{ item.data[NodeProp.NODE_COUNT] }} + + + {{ item.data[NodeProp.DATA_SLICE_COUNT] }} + + + +
+
+ + {{ node[NodeProp.SLICE_ID] }} +
+ + {{ "The Slowest: " + node[NodeProp.ACTUAL_TOTAL_TIME] }} +
+
+
+
+ Memory used: + + +
+
+ Optimizer: + + +
+
+
+
+ + + Slice : {{ memoryDetails[SliceProp.SLICE_NUM] }} + +
+
+

+ + + + + + ExecutorMemory +

+
+
+ + Average memory : + {{ + memoryDetails[SliceProp.EXECUTOR_MEMORY]?.[ + ExecutorMemoryEnum.AVERAGE_MEMORY + ] + }} + + + + Number of worker threads : + {{ + memoryDetails[SliceProp.EXECUTOR_MEMORY]?.[ + ExecutorMemoryEnum.NUMBER_OF_WORKER_THREADS + ] + }} + + + + Maximum memory : + + {{ + memoryDetails[SliceProp.EXECUTOR_MEMORY]?.[ + ExecutorMemoryEnum.MAXIMUM_MEMORY + ] + }} + +
+ + + WorkMemory : {{ memoryDetails[SliceProp.WORK_MEMORY] }} + +
+
+ + + diff --git a/src/enums.ts b/src/enums.ts index 3168a382..28ce3bc4 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -57,6 +57,11 @@ export enum NodeProp { ACTUAL_LOOPS = "Actual Loops", STARTUP_COST = "Startup Cost", TOTAL_COST = "Total Cost", + DATA_SLICE_COUNT = "Data Slice Count", + NODE_COUNT = "Node Count", + SLICE_ID = "Slice Id", + SEGMENTS_COUNT = "Segments Count", + DYNAMIC_SCAN_ID = "Dynamic Scan Id", PLANS = "Plans", RELATION_NAME = "Relation Name", SCHEMA = "Schema", @@ -137,6 +142,18 @@ export enum NodeProp { PEV_PLAN_TAG = "plan_", } +export enum SliceProp { + SLICE_NUM = "Slice Num", + EXECUTOR_MEMORY = "ExecutorMemory", + WORK_MEMORY = "WorkMemory", +} + +export enum ExecutorMemoryEnum { + AVERAGE_MEMORY = "Average memory", + NUMBER_OF_WORKER_THREADS = "Number of worker threads", + MAXIMUM_MEMORY = "Maximum memory", +} + export enum PropType { blocks, boolean, diff --git a/src/filters.ts b/src/filters.ts index f07edf3e..98b3b162 100644 --- a/src/filters.ts +++ b/src/filters.ts @@ -44,6 +44,21 @@ export function duration(value: number | undefined): string { return result.slice(0, 2).join(" ") } +export function formatMemoryUsage(memoryInKB: number): string { + const units: string[] = ["B", "KB", "MB", "GB", "TB"] + let unitIndex = 1 // Start with KB, which is the input unit + let value: number = memoryInKB + + // Convert to the appropriate unit + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024 + unitIndex++ + } + + // Format the output to two decimal places + return `${value.toFixed(2)} ${units[unitIndex]}` +} + export function cost(value: number): string { if (value === undefined) { return "N/A" diff --git a/src/interfaces.ts b/src/interfaces.ts index 039cb0dc..8e9515b1 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -28,6 +28,7 @@ export interface IPlanContent { maxIo?: number maxEstimateFactor?: number Triggers?: ITrigger[] + Slice?: Slice[] JIT?: JIT "Query Text"?: string [k: string]: @@ -36,6 +37,7 @@ export interface IPlanContent { | string | IBlocksStats | ITrigger[] + | Slice[] | JIT | undefined } @@ -47,9 +49,23 @@ export interface ITrigger { Calls: string } +export interface Slice { + [SliceProp.SLICE_NUM]: string + [SliceProp.EXECUTOR_MEMORY]?: ExecutorMemory + [SliceProp.WORK_MEMORY]?: string +} + +export interface ExecutorMemory { + [ExecutorMemoryEnum.AVERAGE_MEMORY]?: string + [ExecutorMemoryEnum.NUMBER_OF_WORKER_THREADS]?: string + [ExecutorMemoryEnum.MAXIMUM_MEMORY]?: string +} + export interface IPlanStats { executionTime?: number planningTime?: number + memoryUsed?: number + optimizer?: string maxRows: number maxCost: number maxDuration: number @@ -65,7 +81,12 @@ export type IBlocksStats = { [key in BufferLocation]: number } -import { EstimateDirection, NodeProp } from "@/enums" +import { + EstimateDirection, + NodeProp, + ExecutorMemoryEnum, + SliceProp, +} from "@/enums" // Class to create nodes when parsing text export class Node { diff --git a/src/services/__tests__/from-text/40-expect b/src/services/__tests__/from-text/40-expect new file mode 100644 index 00000000..b2e985f8 --- /dev/null +++ b/src/services/__tests__/from-text/40-expect @@ -0,0 +1,69 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 10, + "Node Type": "Limit", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Node Type": "Limit", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Node Type": "Seq Scan", + "Plan Rows": 41381704, + "Total Cost": 5233.35, + "Relation Name": "fact_pca_yield_unit", + "Startup Cost": 0, + "Plan Width": 181 + } + ], + "Plan Rows": 2, + "Total Cost": 19164.91, + "Startup Cost": 0, + "Plan Width": 181 + } + ], + "Plan Rows": 10, + "Total Cost": 19164.92, + "Data Slice Count": 6, + "Node Count": 1, + "Slice Id": "slice1", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 181 + } + ], + "Plan Rows": 2, + "Total Cost": 19164.92, + "Startup Cost": 0, + "Plan Width": 181 + }, + "Planning Time": 23.128, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "155K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "65K bytes", + "Number of worker threads": "6", + "Maximum memory": "65K bytes" + } + } + ], + "Memory used": 524288, + "Optimizer": "Pivotal Optimizer (GPORCA)", + "Execution Time": 1.72 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/40-plan b/src/services/__tests__/from-text/40-plan new file mode 100644 index 00000000..2be9f5cf --- /dev/null +++ b/src/services/__tests__/from-text/40-plan @@ -0,0 +1,10 @@ +Limit (cost=0.00..19164.92 rows=2 width=181) (actual time=0.438..0.513 rows=10 loops=1) + -> Gather Motion 6:1 (slice1; segments: 6) (cost=0.00..19164.92 rows=10 width=181) (actual time=0.435..0.474 rows=10 loops=1) + -> Limit (cost=0.00..19164.91 rows=2 width=181) (actual time=0.014..0.016 rows=10 loops=1) + -> Seq Scan on fact_pca_yield_unit (cost=0.00..5233.35 rows=41381704 width=181) (actual time=0.012..0.013 rows=10 loops=1) +Planning time: 23.128 ms + (slice0) Executor memory: 155K bytes. + (slice1) Executor memory: 65K bytes avg x 6 workers, 65K bytes max (seg0). +Memory used: 524288kB +Optimizer: Pivotal Optimizer (GPORCA) +Execution time: 1.720 ms \ No newline at end of file diff --git a/src/services/__tests__/from-text/41-expect b/src/services/__tests__/from-text/41-expect new file mode 100644 index 00000000..ce4390c1 --- /dev/null +++ b/src/services/__tests__/from-text/41-expect @@ -0,0 +1,217 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 36.918, + "Actual Total Time": 36.918, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 20, + "Actual Startup Time": 21.891, + "Actual Total Time": 36.879, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 22.008, + "Actual Total Time": 22.008, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "((row_number() OVER (?)) = 1)", + "Node Type": "Result", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "WindowAgg", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Sort", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Nested Loop", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 0.012, + "Actual Total Time": 0.014, + "Node Type": "Seq Scan", + "Plan Rows": 6, + "Total Cost": 431, + "Relation Name": "tmp_transactionid", + "Startup Cost": 0, + "Plan Width": 36 + }, + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Sequence", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Partition Selector for newitcnd_log_info", + "Plan Rows": 5, + "Total Cost": 100, + "Dynamic Scan Id": 1, + "Startup Cost": 10, + "Plan Width": 4, + "Partitions selected": 1 + }, + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "(((transaction_id)::text = (tmp_transactionid.transaction_id)::text) AND (test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))", + "Node Type": "Dynamic Bitmap Heap Scan on newitcnd_log_info", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "BitmapAnd", + "Plans": [ + { + "Actual Loops": 6, + "Actual Rows": 10, + "Actual Startup Time": 0.216, + "Actual Total Time": 0.634, + "Node Type": "Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_transaction_id", + "Plan Rows": 0, + "Total Cost": 0, + "Startup Cost": 0, + "Plan Width": 0, + "Index Cond": "((transaction_id)::text = (tmp_transactionid.transaction_id)::text)" + }, + { + "Actual Loops": 6, + "Actual Rows": 28641, + "Actual Startup Time": 0.917, + "Actual Total Time": 2.842, + "Node Type": "Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_testtime", + "Plan Rows": 0, + "Total Cost": 0, + "Startup Cost": 0, + "Plan Width": 0, + "Index Cond": "(test_time >= '2024-10-01 00:00:00'::timestamp without time zone)" + } + ], + "Plan Rows": 0, + "Total Cost": 0, + "Startup Cost": 0, + "Plan Width": 0 + } + ], + "Plan Rows": 1, + "Total Cost": 431.42, + "Dynamic Scan Id": 1, + "Startup Cost": 0, + "Plan Width": 141, + "Recheck Cond": "(((transaction_id)::text = (tmp_transactionid.transaction_id)::text) AND (test_time >= '2024-10-01 00:00:00'::timestamp without time zone))", + "Heap Blocks": "exact=74281912 lossy=40572984" + } + ], + "Plan Rows": 1, + "Total Cost": 431.42, + "Startup Cost": 0, + "Plan Width": 141 + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 141, + "Join Filter": "true" + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 141, + "Sort Key": [ + "newitcnd_log_info.path", + "newitcnd_log_info.cdt" + ], + "Sort Method": "quicksort", + "Sort Space Used": "660", + "Sort Space Type": "Memory" + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 16, + "Partition By": "newitcnd_log_info.path", + "Order By": "newitcnd_log_info.cdt" + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 8 + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 16 + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Data Slice Count": 20, + "Node Count": 1, + "Slice Id": "slice1", + "Segments Count": 20, + "Startup Cost": 0, + "Plan Width": 16 + } + ], + "Plan Rows": 1, + "Total Cost": 862.42, + "Startup Cost": 0, + "Plan Width": 16 + }, + "Planning Time": 180.573, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "263K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "1599K bytes", + "Number of worker threads": "20", + "Maximum memory": "1599K bytes" + }, + "WorkMemory": "370K bytes" + }, + { + "Slice Num": "2", + "ExecutorMemory": {} + } + ], + "Memory used": 262144, + "Optimizer": "Pivotal Optimizer (GPORCA)", + "Execution Time": 38.771 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/41-plan b/src/services/__tests__/from-text/41-plan new file mode 100644 index 00000000..9ebb8f16 --- /dev/null +++ b/src/services/__tests__/from-text/41-plan @@ -0,0 +1,33 @@ +"Aggregate (cost=0.00..862.42 rows=1 width=16) (actual time=36.918..36.918 rows=1 loops=1)" +" -> Gather Motion 20:1 (slice1; segments: 20) (cost=0.00..862.42 rows=1 width=16) (actual time=21.891..36.879 rows=20 loops=1)" +" -> Aggregate (cost=0.00..862.42 rows=1 width=16) (actual time=22.008..22.008 rows=1 loops=1)" +" -> Result (cost=0.00..862.42 rows=1 width=8) (never executed)" +" Filter: ((row_number() OVER (?)) = 1)" +" -> WindowAgg (cost=0.00..862.42 rows=1 width=16) (never executed)" +" Partition By: newitcnd_log_info.path" +" Order By: newitcnd_log_info.cdt" +" -> Sort (cost=0.00..862.42 rows=1 width=141) (never executed)" +" Sort Key: newitcnd_log_info.path, newitcnd_log_info.cdt" +" Sort Method: quicksort Memory: 660kB" +" -> Nested Loop (cost=0.00..862.42 rows=1 width=141) (never executed)" +" Join Filter: true" +" -> Seq Scan on tmp_transactionid (cost=0.00..431.00 rows=6 width=36) (actual time=0.012..0.014 rows=6 loops=1)" +" -> Sequence (cost=0.00..431.42 rows=1 width=141) (never executed)" +" -> Partition Selector for newitcnd_log_info (dynamic scan id: 1) (cost=10.00..100.00 rows=5 width=4) (never executed)" +" Partitions selected: 1 (out of 5)" +" -> Dynamic Bitmap Heap Scan on newitcnd_log_info (dynamic scan id: 1) (cost=0.00..431.42 rows=1 width=141) (never executed)" +" Recheck Cond: (((transaction_id)::text = (tmp_transactionid.transaction_id)::text) AND (test_time >= '2024-10-01 00:00:00'::timestamp without time zone))" +" Filter: (((transaction_id)::text = (tmp_transactionid.transaction_id)::text) AND (test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))" +" Heap Blocks: exact=74281912 lossy=40572984" +" -> BitmapAnd (cost=0.00..0.00 rows=0 width=0) (never executed)" +" -> Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_transaction_id (cost=0.00..0.00 rows=0 width=0) (actual time=0.216..0.634 rows=10 loops=6)" +" Index Cond: ((transaction_id)::text = (tmp_transactionid.transaction_id)::text)" +" -> Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_testtime (cost=0.00..0.00 rows=0 width=0) (actual time=0.917..2.842 rows=28641 loops=6)" +" Index Cond: (test_time >= '2024-10-01 00:00:00'::timestamp without time zone)" +"Planning time: 180.573 ms" +" (slice0) Executor memory: 263K bytes." +" (slice1) Executor memory: 1599K bytes avg x 20 workers, 1599K bytes max (seg0). Work_mem: 370K bytes max." +" (slice2) " +"Memory used: 262144kB" +"Optimizer: Pivotal Optimizer (GPORCA)" +"Execution time: 38.771 ms" diff --git a/src/services/__tests__/from-text/42-expect b/src/services/__tests__/from-text/42-expect new file mode 100644 index 00000000..97e86d54 --- /dev/null +++ b/src/services/__tests__/from-text/42-expect @@ -0,0 +1,251 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 32.635, + "Actual Total Time": 32.635, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 20, + "Actual Startup Time": 31.899, + "Actual Total Time": 32.61, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 31.628, + "Actual Total Time": 31.629, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "(a.rn = 1)", + "Node Type": "Subquery Scan", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Sort", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "WindowAgg", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Sort", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Redistribute Motion", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Nested Loop", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 0.021, + "Actual Total Time": 0.023, + "Node Type": "Seq Scan", + "Plan Rows": 6, + "Total Cost": 1.06, + "Relation Name": "tmp_transactionid", + "Alias": "b", + "Startup Cost": 0, + "Plan Width": 36 + }, + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Append", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "((test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))", + "Node Type": "Bitmap Heap Scan", + "Plans": [ + { + "Actual Loops": 6, + "Actual Rows": 10, + "Actual Startup Time": 0.073, + "Actual Total Time": 0.19, + "Index Name": "newitcnd_log_info_1_prt_p50_transaction_id_idx", + "Node Type": "Bitmap Index Scan", + "Plan Rows": 6, + "Total Cost": 11.04, + "Startup Cost": 0, + "Plan Width": 0, + "Index Cond": "((transaction_id)::text = (b.transaction_id)::text)" + } + ], + "Plan Rows": 1, + "Total Cost": 1139.53, + "Relation Name": "newitcnd_log_info_1_prt_p50", + "Alias": "a_1", + "Startup Cost": 11.05, + "Plan Width": 186, + "Recheck Cond": "((transaction_id)::text = (b.transaction_id)::text)" + }, + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "((test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))", + "Node Type": "Bitmap Heap Scan", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Index Name": "newitcnd_log_info_1_prt_other_transaction_id_idx", + "Node Type": "Bitmap Index Scan", + "Plan Rows": 14, + "Total Cost": 12.22, + "Startup Cost": 0, + "Plan Width": 0, + "Index Cond": "((transaction_id)::text = (b.transaction_id)::text)" + } + ], + "Plan Rows": 1, + "Total Cost": 2644.97, + "Relation Name": "newitcnd_log_info_1_prt_other", + "Alias": "a_2", + "Startup Cost": 12.22, + "Plan Width": 187, + "Recheck Cond": "((transaction_id)::text = (b.transaction_id)::text)" + } + ], + "Plan Rows": 1, + "Total Cost": 3784.49, + "Startup Cost": 11.05, + "Plan Width": 186 + } + ], + "Plan Rows": 1, + "Total Cost": 22708.17, + "Startup Cost": 11.05, + "Plan Width": 151 + } + ], + "Plan Rows": 1, + "Total Cost": 22708.56, + "Data Slice Count": 20, + "Node Count": 20, + "Slice Id": "slice1", + "Segments Count": 20, + "Startup Cost": 11.05, + "Plan Width": 151, + "Hash Key": "a_1.path" + } + ], + "Plan Rows": 1, + "Total Cost": 22709.02, + "Startup Cost": 22708.97, + "Plan Width": 151, + "Sort Key": [ + "a_1.path", + "a_1.cdt" + ], + "Sort Method": "quicksort", + "Sort Space Used": "660", + "Sort Space Type": "Memory" + } + ], + "Plan Rows": 1, + "Total Cost": 22709.35, + "Startup Cost": 22708.97, + "Plan Width": 151, + "Partition By": "a_1.path", + "Order By": "a_1.cdt" + } + ], + "Plan Rows": 1, + "Total Cost": 22709.81, + "Startup Cost": 22709.77, + "Plan Width": 151, + "Sort Key": [ + "a_1.uut_serial_number" + ], + "Sort Method": "quicksort", + "Sort Space Used": "660", + "Sort Space Type": "Memory" + } + ], + "Plan Rows": 1, + "Total Cost": 22710.06, + "Alias": "a", + "Startup Cost": 22709.77, + "Plan Width": 8 + } + ], + "Plan Rows": 1, + "Total Cost": 22710.07, + "Startup Cost": 22710.06, + "Plan Width": 40 + } + ], + "Plan Rows": 1, + "Total Cost": 22710.28, + "Data Slice Count": 20, + "Node Count": 1, + "Slice Id": "slice2", + "Segments Count": 20, + "Startup Cost": 22710.06, + "Plan Width": 40 + } + ], + "Plan Rows": 1, + "Total Cost": 22710.31, + "Startup Cost": 22710.3, + "Plan Width": 40 + }, + "Planning Time": 2.148, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "342K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "1046K bytes", + "Number of worker threads": "20", + "Maximum memory": "1046K bytes" + }, + "WorkMemory": "25K bytes" + }, + { + "Slice Num": "2", + "ExecutorMemory": { + "Average memory": "128K bytes", + "Number of worker threads": "20", + "Maximum memory": "128K bytes" + }, + "WorkMemory": "65K bytes" + } + ], + "Memory used": 262144, + "Optimizer": "Postgres query optimizer", + "Execution Time": 45.945 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/42-plan b/src/services/__tests__/from-text/42-plan new file mode 100644 index 00000000..3a90f141 --- /dev/null +++ b/src/services/__tests__/from-text/42-plan @@ -0,0 +1,36 @@ +"Aggregate (cost=22710.30..22710.31 rows=1 width=40) (actual time=32.635..32.635 rows=1 loops=1)" +" -> Gather Motion 20:1 (slice2; segments: 20) (cost=22710.06..22710.28 rows=1 width=40) (actual time=31.899..32.610 rows=20 loops=1)" +" -> Aggregate (cost=22710.06..22710.07 rows=1 width=40) (actual time=31.628..31.629 rows=1 loops=1)" +" -> Subquery Scan on a (cost=22709.77..22710.06 rows=1 width=8) (never executed)" +" Filter: (a.rn = 1)" +" -> Sort (cost=22709.77..22709.81 rows=1 width=151) (never executed)" +" Sort Key: a_1.uut_serial_number" +" Sort Method: quicksort Memory: 660kB" +" -> WindowAgg (cost=22708.97..22709.35 rows=1 width=151) (never executed)" +" Partition By: a_1.path" +" Order By: a_1.cdt" +" -> Sort (cost=22708.97..22709.02 rows=1 width=151) (never executed)" +" Sort Key: a_1.path, a_1.cdt" +" Sort Method: quicksort Memory: 660kB" +" -> Redistribute Motion 20:20 (slice1; segments: 20) (cost=11.05..22708.56 rows=1 width=151) (never executed)" +" Hash Key: a_1.path" +" -> Nested Loop (cost=11.05..22708.17 rows=1 width=151) (never executed)" +" -> Seq Scan on tmp_transactionid b (cost=0.00..1.06 rows=6 width=36) (actual time=0.021..0.023 rows=6 loops=1)" +" -> Append (cost=11.05..3784.49 rows=1 width=186) (never executed)" +" -> Bitmap Heap Scan on newitcnd_log_info_1_prt_p50 a_1 (cost=11.05..1139.53 rows=1 width=186) (never executed)" +" Recheck Cond: ((transaction_id)::text = (b.transaction_id)::text)" +" Filter: ((test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))" +" -> Bitmap Index Scan on newitcnd_log_info_1_prt_p50_transaction_id_idx (cost=0.00..11.04 rows=6 width=0) (actual time=0.073..0.190 rows=10 loops=6)" +" Index Cond: ((transaction_id)::text = (b.transaction_id)::text)" +" -> Bitmap Heap Scan on newitcnd_log_info_1_prt_other a_2 (cost=12.22..2644.97 rows=1 width=187) (never executed)" +" Recheck Cond: ((transaction_id)::text = (b.transaction_id)::text)" +" Filter: ((test_time >= '2024-10-01 00:00:00'::timestamp without time zone) AND ((wc)::text = '50'::text))" +" -> Bitmap Index Scan on newitcnd_log_info_1_prt_other_transaction_id_idx (cost=0.00..12.22 rows=14 width=0) (never executed)" +" Index Cond: ((transaction_id)::text = (b.transaction_id)::text)" +"Planning time: 2.148 ms" +" (slice0) Executor memory: 342K bytes." +" (slice1) Executor memory: 1046K bytes avg x 20 workers, 1046K bytes max (seg0). Work_mem: 25K bytes max." +" (slice2) Executor memory: 128K bytes avg x 20 workers, 128K bytes max (seg0). Work_mem: 65K bytes max." +"Memory used: 262144kB" +"Optimizer: Postgres query optimizer" +"Execution time: 45.945 ms" diff --git a/src/services/__tests__/from-text/43-expect b/src/services/__tests__/from-text/43-expect new file mode 100644 index 00000000..d3e0c47d --- /dev/null +++ b/src/services/__tests__/from-text/43-expect @@ -0,0 +1,145 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 10, + "Actual Startup Time": 161.325, + "Actual Total Time": 161.645, + "Node Type": "Limit", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Actual Startup Time": 161.319, + "Actual Total Time": 161.549, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Actual Startup Time": 160.33, + "Actual Total Time": 161.015, + "Node Type": "Limit", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 10, + "Actual Startup Time": 160.327, + "Actual Total Time": 160.351, + "Node Type": "Hash Join", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 39643, + "Actual Startup Time": 0.026, + "Actual Total Time": 7.468, + "Node Type": "Seq Scan", + "Plan Rows": 294152, + "Total Cost": 493.29, + "Relation Name": "fact_sn_info", + "Startup Cost": 0, + "Plan Width": 355 + }, + { + "Actual Loops": 1, + "Actual Rows": 15799, + "Actual Startup Time": 160.382, + "Actual Total Time": 160.382, + "Node Type": "Hash", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 15799, + "Actual Startup Time": 32.855, + "Actual Total Time": 138.65, + "Node Type": "Redistribute Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 15819, + "Actual Startup Time": 34.59, + "Actual Total Time": 57.348, + "Filter": "(cdt > '2024-10-06 10:20:20.090864'::timestamp without time zone)", + "Node Type": "Seq Scan", + "Plan Rows": 15237, + "Total Cost": 455.57, + "Relation Name": "fact_pca_shipping", + "Startup Cost": 0, + "Plan Width": 179 + } + ], + "Plan Rows": 15237, + "Total Cost": 464.1, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice1", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 179, + "Hash Key": "fact_pca_shipping.mcbsno" + } + ], + "Plan Rows": 15237, + "Total Cost": 464.1, + "Startup Cost": 464.1, + "Plan Width": 179 + } + ], + "Plan Rows": 15237, + "Total Cost": 1358.25, + "Startup Cost": 0, + "Plan Width": 534, + "Hash Cond": "((fact_sn_info.mcbsno)::text = (fact_pca_shipping.mcbsno)::text)", + "Extra Text": "(seg3) Hash chain length 1.2 avg, 5 max, using 13370 of 65536 buckets." + } + ], + "Plan Rows": 2, + "Total Cost": 1358.25, + "Startup Cost": 0, + "Plan Width": 534 + } + ], + "Plan Rows": 10, + "Total Cost": 1358.27, + "Data Slice Count": 6, + "Node Count": 1, + "Slice Id": "slice2", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 534 + } + ], + "Plan Rows": 2, + "Total Cost": 1358.27, + "Startup Cost": 0, + "Plan Width": 534 + }, + "Planning Time": 103.949, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "331K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "65K bytes", + "Number of worker threads": "6", + "Maximum memory": "65K bytes" + } + }, + { + "Slice Num": "2", + "ExecutorMemory": { + "Average memory": "4882K bytes", + "Number of worker threads": "6", + "Maximum memory": "4882K bytes" + }, + "WorkMemory": "2714K bytes" + } + ], + "Memory used": 128000, + "Optimizer": "Pivotal Optimizer (GPORCA)", + "Execution Time": 195.179 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/43-plan b/src/services/__tests__/from-text/43-plan new file mode 100644 index 00000000..7944d413 --- /dev/null +++ b/src/services/__tests__/from-text/43-plan @@ -0,0 +1,19 @@ +"Limit (cost=0.00..1358.27 rows=2 width=534) (actual time=161.325..161.645 rows=10 loops=1)" +" -> Gather Motion 6:1 (slice2; segments: 6) (cost=0.00..1358.27 rows=10 width=534) (actual time=161.319..161.549 rows=10 loops=1)" +" -> Limit (cost=0.00..1358.25 rows=2 width=534) (actual time=160.330..161.015 rows=10 loops=1)" +" -> Hash Join (cost=0.00..1358.25 rows=15237 width=534) (actual time=160.327..160.351 rows=10 loops=1)" +" Hash Cond: ((fact_sn_info.mcbsno)::text = (fact_pca_shipping.mcbsno)::text)" +" Extra Text: (seg3) Hash chain length 1.2 avg, 5 max, using 13370 of 65536 buckets." +" -> Seq Scan on fact_sn_info (cost=0.00..493.29 rows=294152 width=355) (actual time=0.026..7.468 rows=39643 loops=1)" +" -> Hash (cost=464.10..464.10 rows=15237 width=179) (actual time=160.382..160.382 rows=15799 loops=1)" +" -> Redistribute Motion 6:6 (slice1; segments: 6) (cost=0.00..464.10 rows=15237 width=179) (actual time=32.855..138.650 rows=15799 loops=1)" +" Hash Key: fact_pca_shipping.mcbsno" +" -> Seq Scan on fact_pca_shipping (cost=0.00..455.57 rows=15237 width=179) (actual time=34.590..57.348 rows=15819 loops=1)" +" Filter: (cdt > '2024-10-06 10:20:20.090864'::timestamp without time zone)" +"Planning time: 103.949 ms" +" (slice0) Executor memory: 331K bytes." +" (slice1) Executor memory: 65K bytes avg x 6 workers, 65K bytes max (seg0)." +" (slice2) Executor memory: 4882K bytes avg x 6 workers, 4882K bytes max (seg0). Work_mem: 2714K bytes max." +"Memory used: 128000kB" +"Optimizer: Pivotal Optimizer (GPORCA)" +"Execution time: 195.179 ms" \ No newline at end of file diff --git a/src/services/__tests__/from-text/44-expect b/src/services/__tests__/from-text/44-expect new file mode 100644 index 00000000..aee83bf7 --- /dev/null +++ b/src/services/__tests__/from-text/44-expect @@ -0,0 +1,174 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 79.748, + "Actual Total Time": 79.749, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 44.948, + "Actual Total Time": 79.702, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 78.823, + "Actual Total Time": 78.824, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Hash Join", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 61, + "Actual Startup Time": 0.822, + "Actual Total Time": 50.64, + "Node Type": "Nested Loop", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 0.012, + "Actual Total Time": 0.019, + "Node Type": "Broadcast Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 0.243, + "Actual Total Time": 0.245, + "Index Name": "idx_dw_dim_cpu_parts_cust_commodity", + "Node Type": "Index Scan", + "Plan Rows": 42, + "Total Cost": 6.04, + "Relation Name": "dim_cpu_parts_cust", + "Startup Cost": 0, + "Plan Width": 15, + "Index Cond": "((commodity)::text = 'LCM'::text)" + } + ], + "Plan Rows": 249, + "Total Cost": 6.07, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice1", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 15 + }, + { + "Actual Loops": 1, + "Actual Rows": 61, + "Actual Startup Time": 0.81, + "Actual Total Time": 50.594, + "Filter": "(((status)::text = '1'::text) AND ((wc)::text = ANY ('{M40,M61,M46,40,60,61,46,83,57}'::text[])))", + "Index Name": "idx_dw_fact_cpu_sno_parts_iecpn", + "Node Type": "Index Scan", + "Plan Rows": 1459, + "Total Cost": 3445.21, + "Relation Name": "fact_cpu_sno_parts", + "Startup Cost": 0, + "Plan Width": 11, + "Index Cond": "((iecpn)::text = (dim_cpu_parts_cust.pn)::text)" + } + ], + "Plan Rows": 361867, + "Total Cost": 3469.02, + "Startup Cost": 0, + "Plan Width": 14, + "Join Filter": "true" + }, + { + "Actual Loops": 1, + "Actual Rows": 1443, + "Actual Startup Time": 2.486, + "Actual Total Time": 2.486, + "Node Type": "Hash", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1443, + "Actual Startup Time": 0.131, + "Actual Total Time": 1.95, + "Index Name": "idx_dw_fact_cpu_sn_fis_udt", + "Node Type": "Index Scan", + "Plan Rows": 890, + "Total Cost": 7.06, + "Relation Name": "fact_cpu_sn", + "Startup Cost": 0, + "Plan Width": 14, + "Index Cond": "((fis_udt >= '2024-10-11 00:00:00'::timestamp without time zone) AND (fis_udt <= '2024-10-15 23:59:59'::timestamp without time zone))" + } + ], + "Plan Rows": 890, + "Total Cost": 7.06, + "Startup Cost": 7.06, + "Plan Width": 14 + } + ], + "Plan Rows": 1016, + "Total Cost": 3605.47, + "Startup Cost": 0, + "Plan Width": 11, + "Hash Cond": "(((fact_cpu_sno_parts.sno)::text = (fact_cpu_sn.sno)::text) AND ((dim_cpu_parts_cust.customer)::text = (fact_cpu_sn.customer)::text))" + } + ], + "Plan Rows": 1, + "Total Cost": 3605.47, + "Startup Cost": 0, + "Plan Width": 8 + } + ], + "Plan Rows": 1, + "Total Cost": 3605.47, + "Data Slice Count": 6, + "Node Count": 1, + "Slice Id": "slice2", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 8 + } + ], + "Plan Rows": 1, + "Total Cost": 3605.47, + "Startup Cost": 0, + "Plan Width": 8 + }, + "Planning Time": 276.698, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "348K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "60K bytes", + "Number of worker threads": "6", + "Maximum memory": "64K bytes" + } + }, + { + "Slice Num": "2", + "ExecutorMemory": { + "Average memory": "4420K bytes", + "Number of worker threads": "6", + "Maximum memory": "4420K bytes" + }, + "WorkMemory": "57K bytes" + } + ], + "Memory used": 128000, + "Optimizer": "Pivotal Optimizer (GPORCA)", + "Execution Time": 99.896 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/44-plan b/src/services/__tests__/from-text/44-plan new file mode 100644 index 00000000..d2f7f4f5 --- /dev/null +++ b/src/services/__tests__/from-text/44-plan @@ -0,0 +1,23 @@ +"Aggregate (cost=0.00..3605.47 rows=1 width=8) (actual time=79.748..79.749 rows=1 loops=1)" +" -> Gather Motion 6:1 (slice2; segments: 6) (cost=0.00..3605.47 rows=1 width=8) (actual time=44.948..79.702 rows=6 loops=1)" +" -> Aggregate (cost=0.00..3605.47 rows=1 width=8) (actual time=78.823..78.824 rows=1 loops=1)" +" -> Hash Join (cost=0.00..3605.47 rows=1016 width=11) (never executed)" +" Hash Cond: (((fact_cpu_sno_parts.sno)::text = (fact_cpu_sn.sno)::text) AND ((dim_cpu_parts_cust.customer)::text = (fact_cpu_sn.customer)::text))" +" -> Nested Loop (cost=0.00..3469.02 rows=361867 width=14) (actual time=0.822..50.640 rows=61 loops=1)" +" Join Filter: true" +" -> Broadcast Motion 6:6 (slice1; segments: 6) (cost=0.00..6.07 rows=249 width=15) (actual time=0.012..0.019 rows=1 loops=1)" +" -> Index Scan using idx_dw_dim_cpu_parts_cust_commodity on dim_cpu_parts_cust (cost=0.00..6.04 rows=42 width=15) (actual time=0.243..0.245 rows=1 loops=1)" +" Index Cond: ((commodity)::text = 'LCM'::text)" +" -> Index Scan using idx_dw_fact_cpu_sno_parts_iecpn on fact_cpu_sno_parts (cost=0.00..3445.21 rows=1459 width=11) (actual time=0.810..50.594 rows=61 loops=1)" +" Index Cond: ((iecpn)::text = (dim_cpu_parts_cust.pn)::text)" +" Filter: (((status)::text = '1'::text) AND ((wc)::text = ANY ('{M40,M61,M46,40,60,61,46,83,57}'::text[])))" +" -> Hash (cost=7.06..7.06 rows=890 width=14) (actual time=2.486..2.486 rows=1443 loops=1)" +" -> Index Scan using idx_dw_fact_cpu_sn_fis_udt on fact_cpu_sn (cost=0.00..7.06 rows=890 width=14) (actual time=0.131..1.950 rows=1443 loops=1)" +" Index Cond: ((fis_udt >= '2024-10-11 00:00:00'::timestamp without time zone) AND (fis_udt <= '2024-10-15 23:59:59'::timestamp without time zone))" +"Planning time: 276.698 ms" +" (slice0) Executor memory: 348K bytes." +" (slice1) Executor memory: 60K bytes avg x 6 workers, 64K bytes max (seg1)." +" (slice2) Executor memory: 4420K bytes avg x 6 workers, 4420K bytes max (seg0). Work_mem: 57K bytes max." +"Memory used: 128000kB" +"Optimizer: Pivotal Optimizer (GPORCA)" +"Execution time: 99.896 ms" \ No newline at end of file diff --git a/src/services/__tests__/from-text/45-expect b/src/services/__tests__/from-text/45-expect new file mode 100644 index 00000000..01a3f06e --- /dev/null +++ b/src/services/__tests__/from-text/45-expect @@ -0,0 +1,230 @@ +{ + "Plan": { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 4.878, + "Actual Total Time": 4.878, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 3.94, + "Actual Total Time": 4.835, + "Node Type": "Gather Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 1, + "Actual Startup Time": 4.192, + "Actual Total Time": 4.193, + "Node Type": "Aggregate", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Filter": "((row_number() OVER (?)) = 1)", + "Node Type": "Result", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "WindowAgg", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Sort", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Hash Join", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 139, + "Actual Startup Time": 0.53, + "Actual Total Time": 1.307, + "Node Type": "Sequence", + "Plans": [ + { + "Actual Loops": 0, + "Actual Rows": 0, + "Actual Total Time": 0, + "Node Type": "Partition Selector for newitcnd_log_info", + "Plan Rows": 17, + "Total Cost": 100, + "Dynamic Scan Id": 1, + "Startup Cost": 10, + "Plan Width": 4, + "Partitions selected": 1 + }, + { + "Actual Loops": 1, + "Actual Rows": 139, + "Actual Startup Time": 0.512, + "Actual Total Time": 1.274, + "Filter": "((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone) AND ((wc)::text = '50'::text))", + "Node Type": "Dynamic Bitmap Heap Scan on newitcnd_log_info", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 139, + "Actual Startup Time": 0.161, + "Actual Total Time": 0.161, + "Node Type": "Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_testtime", + "Plan Rows": 0, + "Total Cost": 0, + "Startup Cost": 0, + "Plan Width": 0, + "Index Cond": "((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone))" + } + ], + "Plan Rows": 377, + "Total Cost": 416.65, + "Dynamic Scan Id": 1, + "Startup Cost": 0, + "Plan Width": 176, + "Recheck Cond": "((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone))", + "Heap Blocks": "exact=59335960 lossy=88209000" + } + ], + "Plan Rows": 377, + "Total Cost": 416.65, + "Startup Cost": 0, + "Plan Width": 176 + }, + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 1.741, + "Actual Total Time": 1.741, + "Node Type": "Hash", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 6, + "Actual Startup Time": 1.121, + "Actual Total Time": 1.733, + "Node Type": "Broadcast Motion", + "Plans": [ + { + "Actual Loops": 1, + "Actual Rows": 2, + "Actual Startup Time": 0.005, + "Actual Total Time": 0.005, + "Node Type": "Seq Scan", + "Plan Rows": 1, + "Total Cost": 431, + "Relation Name": "tmp_transactionid", + "Startup Cost": 0, + "Plan Width": 36 + } + ], + "Plan Rows": 6, + "Total Cost": 431, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice1", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 36 + } + ], + "Plan Rows": 6, + "Total Cost": 431, + "Startup Cost": 431, + "Plan Width": 36 + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 141, + "Hash Cond": "((newitcnd_log_info.transaction_id)::text = (tmp_transactionid.transaction_id)::text)", + "Extra Text": "(seg0) Hash chain length 1.2 avg, 2 max, using 5 of 131072 buckets." + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 141, + "Sort Key": [ + "newitcnd_log_info.path", + "newitcnd_log_info.cdt" + ], + "Sort Method": "quicksort", + "Sort Space Used": "198", + "Sort Space Type": "Memory" + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 16, + "Partition By": "newitcnd_log_info.path", + "Order By": "newitcnd_log_info.cdt" + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 8 + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 16 + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Data Slice Count": 6, + "Node Count": 1, + "Slice Id": "slice2", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 16 + } + ], + "Plan Rows": 1, + "Total Cost": 847.76, + "Startup Cost": 0, + "Plan Width": 16 + }, + "Planning Time": 138.443, + "Slice": [ + { + "Slice Num": "0", + "ExecutorMemory": { + "Average memory": "259K bytes" + } + }, + { + "Slice Num": "1", + "ExecutorMemory": { + "Average memory": "42K bytes", + "Number of worker threads": "6", + "Maximum memory": "42K bytes" + } + }, + { + "Slice Num": "2", + "ExecutorMemory": { + "Average memory": "1935K bytes", + "Number of worker threads": "6", + "Maximum memory": "2106K bytes" + }, + "WorkMemory": "33K bytes" + } + ], + "Memory used": 128000, + "Optimizer": "Pivotal Optimizer (GPORCA)", + "Execution Time": 6.26 +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/45-plan b/src/services/__tests__/from-text/45-plan new file mode 100644 index 00000000..b265cbea --- /dev/null +++ b/src/services/__tests__/from-text/45-plan @@ -0,0 +1,33 @@ +"Aggregate (cost=0.00..847.76 rows=1 width=16) (actual time=4.878..4.878 rows=1 loops=1)" +" -> Gather Motion 6:1 (slice2; segments: 6) (cost=0.00..847.76 rows=1 width=16) (actual time=3.940..4.835 rows=6 loops=1)" +" -> Aggregate (cost=0.00..847.76 rows=1 width=16) (actual time=4.192..4.193 rows=1 loops=1)" +" -> Result (cost=0.00..847.76 rows=1 width=8) (never executed)" +" Filter: ((row_number() OVER (?)) = 1)" +" -> WindowAgg (cost=0.00..847.76 rows=1 width=16) (never executed)" +" Partition By: newitcnd_log_info.path" +" Order By: newitcnd_log_info.cdt" +" -> Sort (cost=0.00..847.76 rows=1 width=141) (never executed)" +" Sort Key: newitcnd_log_info.path, newitcnd_log_info.cdt" +" Sort Method: quicksort Memory: 198kB" +" -> Hash Join (cost=0.00..847.76 rows=1 width=141) (never executed)" +" Hash Cond: ((newitcnd_log_info.transaction_id)::text = (tmp_transactionid.transaction_id)::text)" +" Extra Text: (seg0) Hash chain length 1.2 avg, 2 max, using 5 of 131072 buckets." +" -> Sequence (cost=0.00..416.65 rows=377 width=176) (actual time=0.530..1.307 rows=139 loops=1)" +" -> Partition Selector for newitcnd_log_info (dynamic scan id: 1) (cost=10.00..100.00 rows=17 width=4) (never executed)" +" Partitions selected: 1 (out of 5)" +" -> Dynamic Bitmap Heap Scan on newitcnd_log_info (dynamic scan id: 1) (cost=0.00..416.65 rows=377 width=176) (actual time=0.512..1.274 rows=139 loops=1)" +" Recheck Cond: ((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone))" +" Filter: ((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone) AND ((wc)::text = '50'::text))" +" Heap Blocks: exact=59335960 lossy=88209000" +" -> Dynamic Bitmap Index Scan on idx_inframinio_newitcnd_log_info_testtime (cost=0.00..0.00 rows=0 width=0) (actual time=0.161..0.161 rows=139 loops=1)" +" Index Cond: ((test_time >= '2024-04-30 00:00:00'::timestamp without time zone) AND (test_time <= '2024-05-01 23:59:59'::timestamp without time zone))" +" -> Hash (cost=431.00..431.00 rows=6 width=36) (actual time=1.741..1.741 rows=6 loops=1)" +" -> Broadcast Motion 6:6 (slice1; segments: 6) (cost=0.00..431.00 rows=6 width=36) (actual time=1.121..1.733 rows=6 loops=1)" +" -> Seq Scan on tmp_transactionid (cost=0.00..431.00 rows=1 width=36) (actual time=0.005..0.005 rows=2 loops=1)" +"Planning time: 138.443 ms" +" (slice0) Executor memory: 259K bytes." +" (slice1) Executor memory: 42K bytes avg x 6 workers, 42K bytes max (seg0)." +" (slice2) Executor memory: 1935K bytes avg x 6 workers, 2106K bytes max (seg0). Work_mem: 33K bytes max." +"Memory used: 128000kB" +"Optimizer: Pivotal Optimizer (GPORCA)" +"Execution time: 6.260 ms" \ No newline at end of file diff --git a/src/services/__tests__/from-text/46-expect b/src/services/__tests__/from-text/46-expect new file mode 100644 index 00000000..2d75f5cf --- /dev/null +++ b/src/services/__tests__/from-text/46-expect @@ -0,0 +1,200 @@ +{ + "Plan": { + "Node Type": "Limit", + "Plans": [ + { + "Node Type": "Gather Motion", + "Plans": [ + { + "Node Type": "Result", + "Plans": [ + { + "Filter": "((row_number() OVER (?)) = 1)", + "Node Type": "Result", + "Plans": [ + { + "Node Type": "WindowAgg", + "Plans": [ + { + "Node Type": "Sort", + "Plans": [ + { + "Node Type": "Hash Join", + "Plans": [ + { + "Node Type": "Hash Join", + "Plans": [ + { + "Node Type": "Seq Scan", + "Plan Rows": 294235, + "Total Cost": 493.47, + "Relation Name": "fact_sn_info", + "Startup Cost": 0, + "Plan Width": 221 + }, + { + "Node Type": "Hash", + "Plans": [ + { + "Node Type": "Redistribute Motion", + "Plans": [ + { + "Node Type": "Hash Join", + "Plans": [ + { + "Node Type": "Seq Scan", + "Plan Rows": 41326, + "Total Cost": 438.18, + "Relation Name": "dim_cpu_dn", + "Startup Cost": 0, + "Plan Width": 103 + }, + { + "Node Type": "Hash", + "Plans": [ + { + "Node Type": "Redistribute Motion", + "Plans": [ + { + "Node Type": "Seq Scan", + "Plan Rows": 706, + "Total Cost": 431.07, + "Relation Name": "temp_pca_shipping", + "Startup Cost": 0, + "Plan Width": 95 + } + ], + "Plan Rows": 706, + "Total Cost": 431.68, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice1", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 95, + "Hash Key": "temp_pca_shipping.dn" + } + ], + "Plan Rows": 706, + "Total Cost": 431.68, + "Startup Cost": 431.68, + "Plan Width": 95 + } + ], + "Plan Rows": 1411, + "Total Cost": 1009.9, + "Join Type": "Right", + "Startup Cost": 0, + "Plan Width": 182, + "Hash Cond": "((dim_cpu_dn.dn)::text = (temp_pca_shipping.dn)::text)" + } + ], + "Plan Rows": 1411, + "Total Cost": 1011.9, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice2", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 182, + "Hash Key": "temp_pca_shipping.mcbsno" + } + ], + "Plan Rows": 1411, + "Total Cost": 1011.9, + "Startup Cost": 1011.9, + "Plan Width": 182 + } + ], + "Plan Rows": 1411, + "Total Cost": 1724.95, + "Startup Cost": 0, + "Plan Width": 392, + "Hash Cond": "((fact_sn_info.mcbsno)::text = (temp_pca_shipping.mcbsno)::text)" + }, + { + "Node Type": "Hash", + "Plans": [ + { + "Node Type": "Broadcast Motion", + "Plans": [ + { + "Node Type": "Seq Scan", + "Plan Rows": 21, + "Total Cost": 431, + "Relation Name": "pca_shipctrl_address", + "Startup Cost": 0, + "Plan Width": 20 + } + ], + "Plan Rows": 125, + "Total Cost": 431.03, + "Data Slice Count": 6, + "Node Count": 6, + "Slice Id": "slice3", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 20 + } + ], + "Plan Rows": 125, + "Total Cost": 431.03, + "Startup Cost": 431.03, + "Plan Width": 20 + } + ], + "Plan Rows": 1411, + "Total Cost": 2158.65, + "Join Type": "Left", + "Startup Cost": 0, + "Plan Width": 408, + "Hash Cond": "(temp_pca_shipping.shipto = pca_shipctrl_address.id)" + } + ], + "Plan Rows": 1411, + "Total Cost": 2192.79, + "Startup Cost": 0, + "Plan Width": 408, + "Sort Key": [ + "fact_sn_info.mcbsno", + "temp_pca_shipping.pack_date" + ] + } + ], + "Plan Rows": 1411, + "Total Cost": 2193.36, + "Startup Cost": 0, + "Plan Width": 388, + "Partition By": "fact_sn_info.mcbsno", + "Order By": "temp_pca_shipping.pack_date" + } + ], + "Plan Rows": 565, + "Total Cost": 2193.41, + "Startup Cost": 0, + "Plan Width": 380 + } + ], + "Plan Rows": 565, + "Total Cost": 2193.61, + "Startup Cost": 0, + "Plan Width": 356 + } + ], + "Plan Rows": 3386, + "Total Cost": 2197.18, + "Data Slice Count": 6, + "Node Count": 1, + "Slice Id": "slice4", + "Segments Count": 6, + "Startup Cost": 0, + "Plan Width": 356 + } + ], + "Plan Rows": 565, + "Total Cost": 2198.39, + "Startup Cost": 0, + "Plan Width": 356 + }, + "Optimizer": "Pivotal Optimizer (GPORCA)" +} \ No newline at end of file diff --git a/src/services/__tests__/from-text/46-plan b/src/services/__tests__/from-text/46-plan new file mode 100644 index 00000000..da8c8fe8 --- /dev/null +++ b/src/services/__tests__/from-text/46-plan @@ -0,0 +1,29 @@ +"Limit (cost=0.00..2198.39 rows=565 width=356)" +" -> Gather Motion 6:1 (slice4; segments: 6) (cost=0.00..2197.18 rows=3386 width=356)" +" -> Result (cost=0.00..2193.61 rows=565 width=356)" +" -> Result (cost=0.00..2193.41 rows=565 width=380)" +" Filter: ((row_number() OVER (?)) = 1)" +" -> WindowAgg (cost=0.00..2193.36 rows=1411 width=388)" +" Partition By: fact_sn_info.mcbsno" +" Order By: temp_pca_shipping.pack_date" +" -> Sort (cost=0.00..2192.79 rows=1411 width=408)" +" Sort Key: fact_sn_info.mcbsno, temp_pca_shipping.pack_date" +" -> Hash Left Join (cost=0.00..2158.65 rows=1411 width=408)" +" Hash Cond: (temp_pca_shipping.shipto = pca_shipctrl_address.id)" +" -> Hash Join (cost=0.00..1724.95 rows=1411 width=392)" +" Hash Cond: ((fact_sn_info.mcbsno)::text = (temp_pca_shipping.mcbsno)::text)" +" -> Seq Scan on fact_sn_info (cost=0.00..493.47 rows=294235 width=221)" +" -> Hash (cost=1011.90..1011.90 rows=1411 width=182)" +" -> Redistribute Motion 6:6 (slice2; segments: 6) (cost=0.00..1011.90 rows=1411 width=182)" +" Hash Key: temp_pca_shipping.mcbsno" +" -> Hash Right Join (cost=0.00..1009.90 rows=1411 width=182)" +" Hash Cond: ((dim_cpu_dn.dn)::text = (temp_pca_shipping.dn)::text)" +" -> Seq Scan on dim_cpu_dn (cost=0.00..438.18 rows=41326 width=103)" +" -> Hash (cost=431.68..431.68 rows=706 width=95)" +" -> Redistribute Motion 6:6 (slice1; segments: 6) (cost=0.00..431.68 rows=706 width=95)" +" Hash Key: temp_pca_shipping.dn" +" -> Seq Scan on temp_pca_shipping (cost=0.00..431.07 rows=706 width=95)" +" -> Hash (cost=431.03..431.03 rows=125 width=20)" +" -> Broadcast Motion 6:6 (slice3; segments: 6) (cost=0.00..431.03 rows=125 width=20)" +" -> Seq Scan on pca_shipctrl_address (cost=0.00..431.00 rows=21 width=20)" +"Optimizer: Pivotal Optimizer (GPORCA)" \ No newline at end of file diff --git a/src/services/plan-service.ts b/src/services/plan-service.ts index 12e3937f..eb2e466d 100644 --- a/src/services/plan-service.ts +++ b/src/services/plan-service.ts @@ -6,6 +6,8 @@ import { NodeProp, SortSpaceMemoryProp, WorkerProp, + SliceProp, + ExecutorMemoryEnum, } from "@/enums" import { splitBalanced } from "@/services/help-service" import type { @@ -14,6 +16,7 @@ import type { IPlanContent, IPlanStats, JIT, + Slice, SortGroups, } from "@/interfaces" import { Node, Worker } from "@/interfaces" @@ -484,7 +487,7 @@ export class PlanService { out[out.length - 1] += line } else if ( line.match( - /^(?:Total\s+runtime|Planning\s+time|Execution\s+time|Time|Filter|Output|JIT)/i + /^(?:Total\s+runtime|Planning\s+time|Memory\s+used|\s+\(slice\d+\)|Optimizer|Execution\s+time|Time|Filter|Output|JIT)/i ) ) { out.push(line) @@ -553,35 +556,50 @@ export class PlanService { const emptyLineMatches = new RegExp(emptyLineRegex).exec(line) const headerMatches = new RegExp(headerRegex).exec(line) + // Gather Motion、Broadcast Motion and Redistribute Motion + const motion = + "(?:(\\d+)+:(\\d+)+\\s+\\((slice\\d+);\\s*segments:\\s*(\\d+)+\\))?" + // dynamic scan node + const dynamic = "(\\(dynamic scan id:\\s*(\\d+)\\))?" /* * Groups * 1: prefix * 2: type - * 3: estimated_startup_cost - * 4: estimated_total_cost - * 5: estimated_rows - * 6: estimated_row_width - * 7: actual_time_first - * 8: actual_time_last - * 9: actual_rows - * 10: actual_loops - * 11: actual_rows_ - * 12: actual_loops_ - * 13: never_executed - * 14: estimated_startup_cost - * 15: estimated_total_cost - * 16: estimated_rows - * 17: estimated_row_width - * 18: actual_time_first - * 19: actual_time_last - * 20: actual_rows - * 21: actual_loops + * 3: dynamic_scan + * 4: dynamic_scan_id + * 5: data_slice_count + * 6: node_count + * 7: slice_id + * 8: segments_count + * 9: estimated_startup_cost + * 10: estimated_total_cost + * 11: estimated_rows + * 12: estimated_row_width + * 13: actual_time_first + * 14: actual_time_last + * 15: actual_rows + * 16: actual_loops + * 17: actual_rows_ + * 18: actual_loops_ + * 19: never_executed + * 20: estimated_startup_cost + * 21: estimated_total_cost + * 22: estimated_rows + * 23: estimated_row_width + * 24: actual_time_first + * 25: actual_time_last + * 26: actual_rows + * 27: actual_loops */ const nodeRegex = new RegExp( prefixRegex + typeRegex + "\\s*" + + dynamic + + "\\s*" + + motion + + "\\s*" + nonCapturingGroupOpen + (nonCapturingGroupOpen + estimationRegex + @@ -654,54 +672,80 @@ export class PlanService { const extraRegex = /^(\s*)(\S.*\S)\s*$/g const extraMatches = extraRegex.exec(line) + /* + * Groups + * 1: slice num + * 2: average memory + * 3: number of worker threads + * 4: maximum memory + * 5: worke memory + */ + const sliceRegex = + /\(slice(\d+)\)\s+(?:Executor\s+memory:\s*)?(?:(\d+?K\s+bytes))?(?:\s+avg\s+x\s+(\d+)?\s+workers)?(?:,\s*(\d+?K\s+bytes)\s+max\s+\(seg\d+\))?(?:\.\s+Work_mem:\s*(\d+?K\s+bytes)\s+max\.)?/ + const sliceMathches = sliceRegex.exec(line) + if (emptyLineMatches || headerMatches) { return } else if (nodeMatches && !cteMatches && !subMatches) { //const prefix = nodeMatches[1] - const neverExecuted = nodeMatches[13] + const neverExecuted = nodeMatches[19] const newNode: Node = new Node(nodeMatches[2]) + if (nodeMatches[4]) { + newNode[NodeProp.DYNAMIC_SCAN_ID] = parseInt(nodeMatches[4]) + } + if ( + nodeMatches[5] && + nodeMatches[6] && + nodeMatches[7] && + nodeMatches[8] + ) { + newNode[NodeProp.DATA_SLICE_COUNT] = parseInt(nodeMatches[5]) + newNode[NodeProp.NODE_COUNT] = parseInt(nodeMatches[6]) + newNode[NodeProp.SLICE_ID] = nodeMatches[7] + newNode[NodeProp.SEGMENTS_COUNT] = parseInt(nodeMatches[8]) + } if ( - (nodeMatches[3] && nodeMatches[4]) || - (nodeMatches[14] && nodeMatches[15]) + (nodeMatches[9] && nodeMatches[10]) || + (nodeMatches[20] && nodeMatches[21]) ) { newNode[NodeProp.STARTUP_COST] = parseFloat( - nodeMatches[3] || nodeMatches[14] + nodeMatches[9] || nodeMatches[20] ) newNode[NodeProp.TOTAL_COST] = parseFloat( - nodeMatches[4] || nodeMatches[15] + nodeMatches[10] || nodeMatches[21] ) newNode[NodeProp.PLAN_ROWS] = parseInt( - nodeMatches[5] || nodeMatches[16], + nodeMatches[11] || nodeMatches[22], 0 ) newNode[NodeProp.PLAN_WIDTH] = parseInt( - nodeMatches[6] || nodeMatches[17], + nodeMatches[12] || nodeMatches[23], 0 ) } if ( - (nodeMatches[7] && nodeMatches[8]) || - (nodeMatches[18] && nodeMatches[19]) + (nodeMatches[13] && nodeMatches[14]) || + (nodeMatches[24] && nodeMatches[25]) ) { newNode[NodeProp.ACTUAL_STARTUP_TIME] = parseFloat( - nodeMatches[7] || nodeMatches[18] + nodeMatches[13] || nodeMatches[24] ) newNode[NodeProp.ACTUAL_TOTAL_TIME] = parseFloat( - nodeMatches[8] || nodeMatches[19] + nodeMatches[14] || nodeMatches[25] ) } if ( - (nodeMatches[9] && nodeMatches[10]) || - (nodeMatches[11] && nodeMatches[12]) || - (nodeMatches[20] && nodeMatches[21]) + (nodeMatches[15] && nodeMatches[16]) || + (nodeMatches[17] && nodeMatches[18]) || + (nodeMatches[26] && nodeMatches[27]) ) { newNode[NodeProp.ACTUAL_ROWS] = parseInt( - nodeMatches[9] || nodeMatches[11] || nodeMatches[20], + nodeMatches[15] || nodeMatches[17] || nodeMatches[26], 0 ) newNode[NodeProp.ACTUAL_LOOPS] = parseInt( - nodeMatches[10] || nodeMatches[12] || nodeMatches[21], + nodeMatches[16] || nodeMatches[18] || nodeMatches[27], 0 ) } @@ -841,7 +885,20 @@ export class PlanService { elementsAtDepth.push([depth, element]) } } - } else if (extraMatches) { + } else if (sliceMathches) { + _.remove(elementsAtDepth, (e) => e[0] >= depth || depth == 1) + root.Slice = root.Slice || [] + const sliceDetail: Slice = { + [SliceProp.SLICE_NUM]: sliceMathches[1], + ExecutorMemory: { + [ExecutorMemoryEnum.AVERAGE_MEMORY]: sliceMathches[2], + [ExecutorMemoryEnum.NUMBER_OF_WORKER_THREADS]: sliceMathches[3], + [ExecutorMemoryEnum.MAXIMUM_MEMORY]: sliceMathches[4], + }, + WorkMemory: sliceMathches[5], + } + root.Slice.push(sliceDetail) + } else if (extraMatches && !sliceMathches) { //const prefix = extraMatches[1] // Remove elements from elementsAtDepth for deeper levels