Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export enum Property {
GROUP_COUNT = "Group Count",
GROUP_KEY = "Group Key",
HASH_CONDITION = "Hash Cond",
HASH_BATCHES = "Hash Batches",
HASH_BUCKETS = "Hash Buckets",
HEAP_FETCHES = "Heap Fetches",
INDEX_NAME = "Index Name",
IO_READ_TIME = "I/O Read Time",
Expand All @@ -123,10 +125,13 @@ export enum Property {
NODE_ID = "nodeId",
NODE_TYPE = "Node Type",
OPERATION = "Operation",
ORIGINAL_HASH_BATCHES = "Original Hash Batches",
ORIGINAL_HASH_BUCKETS = "Original Hash Buckets",
OUTPUT = "Output",
PARALLEL_AWARE = "Parallel Aware",
PARENT_RELATIONSHIP = "Parent Relationship",
PARTIAL_MODE = "Partial Mode",
PEAK_MEMORY_USAGE = "Peak Memory Usage",
PEAK_SORT_SPACE_USED = "Peak Sort Space Used",
PEV_PLAN_TAG = "plan_",
PLANNER_ESTIMATE_DIRECTION = "*Planner Row Estimate Direction",
Expand Down
12 changes: 12 additions & 0 deletions src/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ export function formatCost(value: unknown): string {
return (value as number).toLocaleString(undefined, { minimumFractionDigits: 2 })
}

export function formatNumber(value: unknown): string {
if (value === undefined) {
return "N/A"
}
return (value as number).toLocaleString()
}

export function formatRows(value: unknown): string {
if (value === undefined) {
return "N/A"
Expand Down Expand Up @@ -245,6 +252,8 @@ const nodePropFormatters: Partial<Record<Property, Formatter>> = {
[Property.EXCLUSIVE_TEMP_WRITTEN_BLOCKS]: formatBlocksHtml,
[Property.FULL_SORT_GROUPS]: formatSortGroups,
[Property.HEAP_FETCHES]: formatRows,
[Property.HASH_BATCHES]: formatNumber,
[Property.HASH_BUCKETS]: formatNumber,
[Property.IO_READ_TIME]: formatDuration,
[Property.IO_WRITE_TIME]: formatDuration,
[Property.JIT]: formatJit,
Expand All @@ -254,8 +263,11 @@ const nodePropFormatters: Partial<Record<Property, Formatter>> = {
[Property.LOCAL_IO_WRITE_TIME]: formatDuration,
[Property.LOCAL_READ_BLOCKS]: formatBlocksHtml,
[Property.LOCAL_WRITTEN_BLOCKS]: formatBlocksHtml,
[Property.ORIGINAL_HASH_BATCHES]: formatNumber,
[Property.ORIGINAL_HASH_BUCKETS]: formatNumber,
[Property.OUTPUT]: formatList,
[Property.PARALLEL_AWARE]: formatBoolean,
[Property.PEAK_MEMORY_USAGE]: formatKilobytes,
[Property.PLANNER_ESTIMATE_DIRECTION]: formatEstimateDirection,
[Property.PLANNER_ESTIMATE_FACTOR]: formatFactor,
[Property.PLAN_ROWS]: formatRows,
Expand Down
2 changes: 1 addition & 1 deletion src/services/__tests__/12-line-wrapped-plans/01-expect
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"Actual Total Time": 0.062,
"Actual Rows": 2,
"Actual Loops": 1,
"Buckets": 1024,
"Hash Buckets": 1024,
"Plans": [
{
"Node Type": "Seq Scan",
Expand Down
2 changes: 1 addition & 1 deletion src/services/__tests__/12-line-wrapped-plans/02-expect
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"Actual Total Time": 0.062,
"Actual Rows": 2,
"Actual Loops": 1,
"Buckets": 1024,
"Hash Buckets": 1024,
"Plans": [
{
"Node Type": "Seq Scan",
Expand Down
2 changes: 1 addition & 1 deletion src/services/__tests__/12-line-wrapped-plans/05-expect
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"Actual Total Time": 4.997,
"Actual Rows": 10,
"Actual Loops": 3,
"Buckets": 1024,
"Hash Buckets": 1024,
"Shared Hit Blocks": 26,
"Shared Read Blocks": 9,
"Shared Written Blocks": 0,
Expand Down
4 changes: 2 additions & 2 deletions src/services/__tests__/12-line-wrapped-plans/06-expect
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"Actual Total Time": 0.555,
"Actual Rows": 10,
"Actual Loops": 3,
"Buckets": 1024,
"Hash Buckets": 1024,
"Shared Hit Blocks": 33,
"Shared Read Blocks": 0,
"Shared Written Blocks": 0,
Expand Down Expand Up @@ -164,7 +164,7 @@
"Actual Total Time": 1.289,
"Actual Rows": 10,
"Actual Loops": 3,
"Buckets": 1024,
"Hash Buckets": 1024,
"Shared Hit Blocks": 14,
"Shared Read Blocks": 0,
"Shared Written Blocks": 0,
Expand Down
4 changes: 2 additions & 2 deletions src/services/__tests__/12-line-wrapped-plans/07-expect
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"Actual Total Time": 0.4,
"Actual Rows": 10,
"Actual Loops": 3,
"Buckets": 1024,
"Hash Buckets": 1024,
"Shared Hit Blocks": 33,
"Shared Read Blocks": 0,
"Shared Written Blocks": 0,
Expand Down Expand Up @@ -164,7 +164,7 @@
"Actual Total Time": 0.933,
"Actual Rows": 10,
"Actual Loops": 3,
"Buckets": 1024,
"Hash Buckets": 1024,
"Shared Hit Blocks": 14,
"Shared Read Blocks": 0,
"Shared Written Blocks": 0,
Expand Down
23 changes: 23 additions & 0 deletions src/services/__tests__/from-text/45-buckets-expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"Plan": {
"Node Type": "Hash Join",
"Plans": [
{
"Node Type": "Seq Scan"
}, {
"Node Type": "Hash",
"Shared Hit Blocks": 14909,
"Shared Read Blocks": 98728,
"Temp Written Blocks": 88109,
"Hash Buckets": 1048576,
"Hash Batches": 16,
"Peak Memory Usage": 62533,
"Plans": [
{
"Node Type": "Seq Scan"
}
]
}
]
}
}
23 changes: 23 additions & 0 deletions src/services/__tests__/from-text/45-buckets-plan
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=471037.90..1304640.12 rows=14999862 width=114) (actual time=1480.644..9895.695 rows=15001138 loops=1)
Hash Cond: (details.eid = entetes.eid)
Buffers: shared hit=15940 read=282899, temp read=246065 written=246065
I/O Timings: shared read=242.957, temp read=317.342 write=830.025
-> Seq Scan on details (cost=0.00..335215.62 rows=15001362 width=65) (actual time=0.014..1114.539 rows=15001289 loops=1)
Filter: (b IS NOT NULL)
Buffers: shared hit=1031 read=184171
I/O Timings: shared read=157.615
-> Hash (cost=238637.70..238637.70 rows=9999056 width=57) (actual time=1474.384..1474.385 rows=9999900 loops=1)
Buckets: 1048576 Batches: 16 Memory Usage: 62533kB
Buffers: shared hit=14909 read=98728, temp written=88109
I/O Timings: shared read=85.342, temp write=294.483
-> Seq Scan on entetes (cost=0.00..238637.70 rows=9999056 width=57) (actual time=0.016..567.370 rows=9999900 loops=1)
Filter: (a > 100)
Rows Removed by Filter: 101
Buffers: shared hit=14909 read=98728
I/O Timings: shared read=85.342
Planning:
Buffers: shared hit=8
Planning Time: 0.119 ms
Execution Time: 10244.597 ms
24 changes: 24 additions & 0 deletions src/services/__tests__/from-text/46-buckets-expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"Plan": {
"Node Type": "Aggregate",
"Plans": [
{
"Node Type": "Hash Join",
"Plans": [
{
"Node Type": "Seq Scan"
},
{
"Node Type": "Hash",
"Actual Rows": 1001000,
"Hash Buckets": 262144,
"Original Hash Buckets": 262144,
"Hash Batches": 2048,
"Original Hash Batches": 8,
"Peak Memory Usage": 35157
}
]
}
]
}
}
56 changes: 56 additions & 0 deletions src/services/__tests__/from-text/46-buckets-plan
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
-- source: https://www.postgresql.org/message-id/flat/CA%2BhUKGKWWmf%3DWELLG%3DaUGbcugRaSQbtm0tKYiBut-B2rVKX63g%40mail.gmail.com

SET max_parallel_workers_per_gather = 0;
SET synchronize_seqscans = off;
SET work_mem = '4MB';



CREATE TABLE r AS SELECT generate_series(1, 10000000)::int i;
ANALYZE r;



-- 1k uniform keys + 1m duplicates
CREATE TABLE s1k (i int);
INSERT INTO s1k SELECT generate_series(1, 1000)::int i;
ALTER TABLE s1k SET (autovacuum_enabled = off);
ANALYZE s1k;
INSERT INTO s1k SELECT 42 FROM generate_series(1, 1000000);



EXPLAIN ANALYZE SELECT COUNT(*) FROM r JOIN s1k USING (i);
SET
SET
SET
SELECT 10000000
ANALYZE
CREATE TABLE
INSERT 0 1000
ALTER TABLE
ANALYZE
INSERT 0 1000000
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=302291.30..302291.31 rows=1 width=8) (actual time=3656.686..3656.688 rows=1.00 loops=1)
Buffers: shared hit=6734 read=41984 dirtied=13621, temp read=49855 written=49855
-> Hash Join (cost=27826.00..300076.30 rows=886000 width=0) (actual time=146.635..3619.685 rows=1001000.00 loops=1)
Hash Cond: (r.i = s1k.i)
Buffers: shared hit=6734 read=41984 dirtied=13621, temp read=49855 written=49855
-> Seq Scan on r (cost=0.00..144294.76 rows=10000676 width=4) (actual time=0.201..1200.806 rows=10000000.00 loops=1)
Buffers: shared hit=2304 read=41984 dirtied=13621
-> Hash (cost=13290.00..13290.00 rows=886000 width=4) (actual time=145.449..145.450 rows=1001000.00 loops=1)
Buckets: 262144 (originally 262144) Batches: 2048 (originally 8) Memory Usage: 35157kB
Buffers: shared hit=4430, temp written=2930
-> Seq Scan on s1k (cost=0.00..13290.00 rows=886000 width=4) (actual time=0.011..61.783 rows=1001000.00 loops=1)
Buffers: shared hit=4430
Planning:
Buffers: shared hit=83 read=9
Planning Time: 0.495 ms
JIT:
Functions: 10
Options: Inlining false, Optimization false, Expressions true, Deforming true
Timing: Generation 0.492 ms (Deform 0.134 ms), Inlining 0.000 ms, Optimization 0.266 ms, Emission 3.684 ms, Total 4.442 ms
Execution Time: 3674.128 ms
(20 rows)
28 changes: 28 additions & 0 deletions src/services/plan-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,10 @@ export class PlanService {
return
}

if (this.parseBuckets(extraMatches[2], element as Node)) {
return
}

if (this.parseWAL(extraMatches[2], element as Node)) {
return
}
Expand Down Expand Up @@ -1120,6 +1124,30 @@ export class PlanService {
el[_.map([type, method, "blocks"], _.capitalize).join(" ")] = value
}

private parseBuckets(text: string, el: Node): boolean {

enum BucketsMatch {
Buckets = 1,
OriginalBuckets,
Batches,
OriginalBatches,
MemoryUsage,
}

const bucketsRegex = /Buckets:\s+([0-9]*)(?:\s*\(originally\s+([0-9]*)\))*\s+Batches:\s+([0-9]*)(?:\s*\(originally\s+([0-9]*)\))*\s+Memory Usage:\s+([0-9]*)kB.*\s*$/g
const bucketsMatches = bucketsRegex.exec(text)

if (bucketsMatches) {
el[Property.HASH_BUCKETS] = parseInt(bucketsMatches[BucketsMatch.Buckets], 0)
el[Property.HASH_BATCHES] = parseInt(bucketsMatches[BucketsMatch.Batches], 0)
el[Property.ORIGINAL_HASH_BUCKETS] = parseInt(bucketsMatches[BucketsMatch.OriginalBuckets], 0)
el[Property.ORIGINAL_HASH_BATCHES] = parseInt(bucketsMatches[BucketsMatch.OriginalBatches], 0)
el[Property.PEAK_MEMORY_USAGE] = parseInt(bucketsMatches[BucketsMatch.MemoryUsage], 0)
return true
}
return false
}

private getWorker(node: Node, workerNumber: number): Worker | undefined {
return _.find(node[Property.WORKERS], (worker) => {
return worker[Property.WORKER_NUMBER] === workerNumber
Expand Down
Loading