@fachkraftfreund/n8n-nodes-supabase 1.2.24 → 1.2.25
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.
|
@@ -49,6 +49,44 @@ async function executeDatabaseOperation(supabase, operation, itemIndex, hostUrl)
|
|
|
49
49
|
return returnData;
|
|
50
50
|
}
|
|
51
51
|
exports.executeDatabaseOperation = executeDatabaseOperation;
|
|
52
|
+
const BULK_BATCH_SIZE = 500;
|
|
53
|
+
const MAX_RETRIES = 3;
|
|
54
|
+
const RETRY_BASE_DELAY_MS = 1000;
|
|
55
|
+
function isRetryableError(msg) {
|
|
56
|
+
const lower = msg.toLowerCase();
|
|
57
|
+
return (lower.includes('lock timeout') ||
|
|
58
|
+
lower.includes('canceling statement due to lock') ||
|
|
59
|
+
lower.includes('deadlock') ||
|
|
60
|
+
lower.includes('too many connections') ||
|
|
61
|
+
lower.includes('rate limit') ||
|
|
62
|
+
lower.includes('could not serialize access') ||
|
|
63
|
+
lower.includes('connection terminated') ||
|
|
64
|
+
lower.includes('connection reset') ||
|
|
65
|
+
lower.includes('econnreset') ||
|
|
66
|
+
lower.includes('timeout'));
|
|
67
|
+
}
|
|
68
|
+
async function withRetry(fn, label) {
|
|
69
|
+
var _a;
|
|
70
|
+
let lastError;
|
|
71
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
72
|
+
try {
|
|
73
|
+
return await fn();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const msg = (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err);
|
|
77
|
+
if (attempt < MAX_RETRIES && isRetryableError(msg)) {
|
|
78
|
+
const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
|
|
79
|
+
console.log(`[Supabase ${label}] transient error (attempt ${attempt + 1}/${MAX_RETRIES + 1}), retrying in ${delay}ms: ${msg}`);
|
|
80
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
81
|
+
lastError = err instanceof Error ? err : new Error(msg);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw lastError;
|
|
89
|
+
}
|
|
52
90
|
function sanitizeString(value) {
|
|
53
91
|
return value
|
|
54
92
|
.replace(/\x00/g, '')
|
|
@@ -126,16 +164,26 @@ async function handleBulkCreate(supabase, itemCount) {
|
|
|
126
164
|
const table = this.getNodeParameter('table', 0);
|
|
127
165
|
(0, supabaseClient_1.validateTableName)(table);
|
|
128
166
|
const rows = collectRowData(this, itemCount);
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
167
|
+
const returnData = [];
|
|
168
|
+
for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
|
|
169
|
+
const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
|
|
170
|
+
const data = await withRetry(async () => {
|
|
171
|
+
const { data, error } = await supabase
|
|
172
|
+
.from(table)
|
|
173
|
+
.insert(batch)
|
|
174
|
+
.select();
|
|
175
|
+
if (error)
|
|
176
|
+
throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
|
|
177
|
+
return data;
|
|
178
|
+
}, `CREATE ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`);
|
|
179
|
+
if (Array.isArray(data)) {
|
|
180
|
+
for (const row of data)
|
|
181
|
+
returnData.push({ json: row });
|
|
182
|
+
}
|
|
137
183
|
}
|
|
138
|
-
return
|
|
184
|
+
return returnData.length > 0
|
|
185
|
+
? returnData
|
|
186
|
+
: [{ json: { data: [], operation: 'create', table } }];
|
|
139
187
|
}
|
|
140
188
|
async function handleBulkUpsert(supabase, itemCount) {
|
|
141
189
|
const table = this.getNodeParameter('table', 0);
|
|
@@ -145,16 +193,26 @@ async function handleBulkUpsert(supabase, itemCount) {
|
|
|
145
193
|
const options = {};
|
|
146
194
|
if (onConflict)
|
|
147
195
|
options.onConflict = onConflict;
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
const returnData = [];
|
|
197
|
+
for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
|
|
198
|
+
const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
|
|
199
|
+
const data = await withRetry(async () => {
|
|
200
|
+
const { data, error } = await supabase
|
|
201
|
+
.from(table)
|
|
202
|
+
.upsert(batch, options)
|
|
203
|
+
.select();
|
|
204
|
+
if (error)
|
|
205
|
+
throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
|
|
206
|
+
return data;
|
|
207
|
+
}, `UPSERT ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`);
|
|
208
|
+
if (Array.isArray(data)) {
|
|
209
|
+
for (const row of data)
|
|
210
|
+
returnData.push({ json: row });
|
|
211
|
+
}
|
|
156
212
|
}
|
|
157
|
-
return
|
|
213
|
+
return returnData.length > 0
|
|
214
|
+
? returnData
|
|
215
|
+
: [{ json: { data: [], operation: 'upsert', table } }];
|
|
158
216
|
}
|
|
159
217
|
async function handleBulkUpdate(supabase, itemCount) {
|
|
160
218
|
const table = this.getNodeParameter('table', 0);
|
|
@@ -170,16 +228,26 @@ async function handleBulkUpdate(supabase, itemCount) {
|
|
|
170
228
|
throw new Error(`Item ${i} is missing the match column "${matchColumn}"`);
|
|
171
229
|
}
|
|
172
230
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
231
|
+
const returnData = [];
|
|
232
|
+
for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
|
|
233
|
+
const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
|
|
234
|
+
const data = await withRetry(async () => {
|
|
235
|
+
const { data, error } = await supabase
|
|
236
|
+
.from(table)
|
|
237
|
+
.upsert(batch, { onConflict: matchColumn })
|
|
238
|
+
.select();
|
|
239
|
+
if (error)
|
|
240
|
+
throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
|
|
241
|
+
return data;
|
|
242
|
+
}, `UPDATE ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`);
|
|
243
|
+
if (Array.isArray(data)) {
|
|
244
|
+
for (const row of data)
|
|
245
|
+
returnData.push({ json: row });
|
|
246
|
+
}
|
|
181
247
|
}
|
|
182
|
-
return
|
|
248
|
+
return returnData.length > 0
|
|
249
|
+
? returnData
|
|
250
|
+
: [{ json: { data: [], operation: 'update', table } }];
|
|
183
251
|
}
|
|
184
252
|
function getFilters(context, itemIndex) {
|
|
185
253
|
const uiMode = context.getNodeParameter('uiMode', itemIndex, 'simple');
|
package/package.json
CHANGED