Skip to content

Commit 19bdd5c

Browse files
ImTotemclaude
andcommitted
perf(workflow): only sync tables with actual PG modifications
Use pg_stat_user_tables mod counts (insert+update+delete) to detect changes. Store counts in workflow static data between executions. Only Clear+Write tables where mod_count actually changed. No changes = 1 PG query + 0 Sheets API calls (was 2×N calls) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 732a44c commit 19bdd5c

File tree

1 file changed

+56
-45
lines changed

1 file changed

+56
-45
lines changed

workflows/pg_sheets_sync.json

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,69 +18,67 @@
1818
{
1919
"parameters": {
2020
"operation": "executeQuery",
21-
"query": "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name",
21+
"query": "SELECT t.table_name, COALESCE(s.n_tup_ins + s.n_tup_upd + s.n_tup_del, 0)::text as mod_count FROM information_schema.tables t LEFT JOIN pg_stat_user_tables s ON s.relname = t.table_name WHERE t.table_schema = 'public' AND t.table_type = 'BASE TABLE' ORDER BY t.table_name",
2222
"options": {}
2323
},
2424
"id": "pg-list-tables",
25-
"name": "List PG Tables",
25+
"name": "List Tables + Mod Counts",
2626
"type": "n8n-nodes-base.postgres",
2727
"typeVersion": 2.6,
2828
"position": [220, 0]
2929
},
30-
{
31-
"parameters": {"options": {}},
32-
"id": "split-batches",
33-
"name": "Loop Over Tables",
34-
"type": "n8n-nodes-base.splitInBatches",
35-
"typeVersion": 3,
36-
"position": [440, 0]
37-
},
38-
{
39-
"parameters": {
40-
"operation": "create",
41-
"documentId": {"__rl": true, "mode": "id", "value": "={{ $env.GOOGLE_SHEETS_ID }}"},
42-
"title": "={{ $json.table_name }}"
43-
},
44-
"id": "sheets-create",
45-
"name": "Ensure Sheet Exists",
46-
"type": "n8n-nodes-base.googleSheets",
47-
"typeVersion": 4.7,
48-
"position": [660, 0],
49-
"onError": "continueRegularOutput"
50-
},
5130
{
5231
"parameters": {
53-
"operation": "executeQuery",
54-
"query": "=SELECT * FROM {{ $('Loop Over Tables').item.json.table_name }}",
55-
"options": {}
32+
"jsCode": "const staticData = $getWorkflowStaticData('global');\nif (!staticData.mods) staticData.mods = {};\n\nconst changed = [];\nfor (const item of $input.all()) {\n const name = item.json.table_name;\n const mods = item.json.mod_count;\n if (staticData.mods[name] !== mods) {\n staticData.mods[name] = mods;\n changed.push({ json: { table_name: name } });\n }\n}\n\nif (changed.length === 0) {\n return [{ json: { table_name: '__none__', skipped: true } }];\n}\nreturn changed;"
5633
},
57-
"id": "pg-dump-table",
58-
"name": "Dump Table",
59-
"type": "n8n-nodes-base.postgres",
60-
"typeVersion": 2.6,
61-
"position": [880, 0]
34+
"id": "filter-changed",
35+
"name": "Filter Changed Tables",
36+
"type": "n8n-nodes-base.code",
37+
"typeVersion": 2,
38+
"position": [440, 0]
6239
},
6340
{
6441
"parameters": {
6542
"conditions": {
6643
"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"},
6744
"conditions": [
6845
{
69-
"id": "has-rows",
70-
"leftValue": "={{ $input.all().length }}",
71-
"rightValue": 0,
72-
"operator": {"type": "number", "operation": "gt"}
46+
"id": "not-skipped",
47+
"leftValue": "={{ $json.skipped }}",
48+
"rightValue": true,
49+
"operator": {"type": "boolean", "operation": "notEquals"}
7350
}
7451
],
7552
"combinator": "and"
7653
},
7754
"options": {}
7855
},
79-
"id": "if-has-data",
80-
"name": "Has Data?",
56+
"id": "if-has-changes",
57+
"name": "Any Changes?",
8158
"type": "n8n-nodes-base.if",
8259
"typeVersion": 2.3,
83-
"position": [1100, 0]
60+
"position": [660, 0]
61+
},
62+
{
63+
"parameters": {"options": {}},
64+
"id": "split-batches",
65+
"name": "Loop Over Tables",
66+
"type": "n8n-nodes-base.splitInBatches",
67+
"typeVersion": 3,
68+
"position": [880, -100]
69+
},
70+
{
71+
"parameters": {
72+
"operation": "create",
73+
"documentId": {"__rl": true, "mode": "id", "value": "={{ $env.GOOGLE_SHEETS_ID }}"},
74+
"title": "={{ $json.table_name }}"
75+
},
76+
"id": "sheets-create",
77+
"name": "Ensure Sheet Exists",
78+
"type": "n8n-nodes-base.googleSheets",
79+
"typeVersion": 4.7,
80+
"position": [1100, -100],
81+
"onError": "continueRegularOutput"
8482
},
8583
{
8684
"parameters": {
@@ -94,6 +92,18 @@
9492
"typeVersion": 4.7,
9593
"position": [1320, -100]
9694
},
95+
{
96+
"parameters": {
97+
"operation": "executeQuery",
98+
"query": "=SELECT * FROM {{ $('Loop Over Tables').item.json.table_name }}",
99+
"options": {}
100+
},
101+
"id": "pg-dump-table",
102+
"name": "Dump Table",
103+
"type": "n8n-nodes-base.postgres",
104+
"typeVersion": 2.6,
105+
"position": [1540, -100]
106+
},
97107
{
98108
"parameters": {
99109
"operation": "append",
@@ -109,17 +119,18 @@
109119
"name": "Write to Sheet",
110120
"type": "n8n-nodes-base.googleSheets",
111121
"typeVersion": 4.7,
112-
"position": [1540, -100]
122+
"position": [1760, -100]
113123
}
114124
],
115125
"connections": {
116-
"Every 5 Minutes": {"main": [[{"node": "List PG Tables", "type": "main", "index": 0}]]},
117-
"List PG Tables": {"main": [[{"node": "Loop Over Tables", "type": "main", "index": 0}]]},
126+
"Every 5 Minutes": {"main": [[{"node": "List Tables + Mod Counts", "type": "main", "index": 0}]]},
127+
"List Tables + Mod Counts": {"main": [[{"node": "Filter Changed Tables", "type": "main", "index": 0}]]},
128+
"Filter Changed Tables": {"main": [[{"node": "Any Changes?", "type": "main", "index": 0}]]},
129+
"Any Changes?": {"main": [[{"node": "Loop Over Tables", "type": "main", "index": 0}], []]},
118130
"Loop Over Tables": {"main": [[], [{"node": "Ensure Sheet Exists", "type": "main", "index": 0}]]},
119-
"Ensure Sheet Exists": {"main": [[{"node": "Dump Table", "type": "main", "index": 0}]]},
120-
"Dump Table": {"main": [[{"node": "Has Data?", "type": "main", "index": 0}]]},
121-
"Has Data?": {"main": [[{"node": "Clear Sheet", "type": "main", "index": 0}], [{"node": "Loop Over Tables", "type": "main", "index": 0}]]},
122-
"Clear Sheet": {"main": [[{"node": "Write to Sheet", "type": "main", "index": 0}]]},
131+
"Ensure Sheet Exists": {"main": [[{"node": "Clear Sheet", "type": "main", "index": 0}]]},
132+
"Clear Sheet": {"main": [[{"node": "Dump Table", "type": "main", "index": 0}]]},
133+
"Dump Table": {"main": [[{"node": "Write to Sheet", "type": "main", "index": 0}]]},
123134
"Write to Sheet": {"main": [[{"node": "Loop Over Tables", "type": "main", "index": 0}]]}
124135
},
125136
"settings": {"executionOrder": "v1"}

0 commit comments

Comments
 (0)