Skip to content

Commit 27126e6

Browse files
authored
SupabaseConnector upload queue error code fix (#223)
1 parent 8d5b702 commit 27126e6

File tree

9 files changed

+63
-49
lines changed

9 files changed

+63
-49
lines changed

.changeset/famous-fishes-design.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'react-native-supabase-group-chat': patch
3+
'react-native-supabase-todolist': patch
4+
'yjs-react-supabase-text-collab': patch
5+
'django-react-native-todolist': patch
6+
'angular-supabase-todolist': patch
7+
'react-supabase-todolist': patch
8+
'vue-supabase-todolist': patch
9+
'react-multi-client': patch
10+
---
11+
12+
Ensuring that SupabaseConnector's error codes are evaluated when processing upload queue.

demos/angular-supabase-todolist/src/app/supabase.service.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ export class SupabaseService implements PowerSyncBackendConnector {
131131
}
132132

133133
if (result.error) {
134-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
134+
console.error(result.error);
135+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
136+
throw result.error;
135137
}
136138
}
137139

@@ -147,7 +149,7 @@ export class SupabaseService implements PowerSyncBackendConnector {
147149
* If protecting against data loss is important, save the failing records
148150
* elsewhere instead of discarding, and/or notify the user.
149151
*/
150-
console.error(`Data upload error - discarding ${lastOp}`, ex);
152+
console.error('Data upload error - discarding:', lastOp, ex);
151153
await transaction.complete();
152154
} else {
153155
// Error may be retryable - e.g. network error or temporary server error.

demos/django-react-native-todolist/library/django/DjangoConnector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class DjangoConnector implements PowerSyncBackendConnector {
9090
* If protecting against data loss is important, save the failing records
9191
* elsewhere instead of discarding, and/or notify the user.
9292
*/
93-
console.error(`Data upload error - discarding ${lastOp}`, ex);
93+
console.error('Data upload error - discarding:', lastOp, ex);
9494
await transaction.complete();
9595
} else {
9696
// Error may be retryable - e.g. network error or temporary server error.

demos/react-multi-client/src/library/SupabaseConnector.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
104104

105105
if (result.error) {
106106
console.error(result.error);
107-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
107+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
108+
throw result.error;
108109
}
109110
}
110111

@@ -120,7 +121,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
120121
* If protecting against data loss is important, save the failing records
121122
* elsewhere instead of discarding, and/or notify the user.
122123
*/
123-
console.error(`Data upload error - discarding ${lastOp}`, ex);
124+
console.error('Data upload error - discarding:', lastOp, ex);
124125
await transaction.complete();
125126
} else {
126127
// Error may be retryable - e.g. network error or temporary server error.

demos/react-native-supabase-group-chat/src/lib/connector.ts

+27-35
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
1-
import {
2-
AbstractPowerSyncDatabase,
3-
CrudEntry,
4-
PowerSyncBackendConnector,
5-
UpdateType,
6-
} from "@powersync/react-native";
7-
import type { SupabaseClient } from "@supabase/supabase-js";
1+
import { AbstractPowerSyncDatabase, CrudEntry, PowerSyncBackendConnector, UpdateType } from '@powersync/react-native';
2+
import type { SupabaseClient } from '@supabase/supabase-js';
83

9-
import { config } from "./config";
10-
import { supabase } from "./supabase";
4+
import { config } from './config';
5+
import { supabase } from './supabase';
116

127
/// Postgres Response codes that we cannot recover from by retrying.
138
const FATAL_RESPONSE_CODES = [
149
// Class 22 — Data Exception
1510
// Examples include data type mismatch.
16-
new RegExp("^22...$"),
11+
new RegExp('^22...$'),
1712
// Class 23 — Integrity Constraint Violation.
1813
// Examples include NOT NULL, FOREIGN KEY and UNIQUE violations.
19-
new RegExp("^23...$"),
14+
new RegExp('^23...$'),
2015
// INSUFFICIENT PRIVILEGE - typically a row-level security violation
21-
new RegExp("^42501$"),
16+
new RegExp('^42501$')
2217
];
2318

2419
export class Connector implements PowerSyncBackendConnector {
@@ -31,23 +26,21 @@ export class Connector implements PowerSyncBackendConnector {
3126
async fetchCredentials() {
3227
const {
3328
data: { session },
34-
error,
29+
error
3530
} = await this.supabaseClient.auth.getSession();
3631

3732
if (!session || error) {
3833
throw new Error(`Could not fetch Supabase credentials: ${error}`);
3934
}
4035

41-
console.debug("session expires at", session.expires_at);
36+
console.debug('session expires at', session.expires_at);
4237

4338
return {
4439
client: this.supabaseClient,
4540
endpoint: config.powerSyncUrl,
46-
token: session.access_token ?? "",
47-
expiresAt: session.expires_at
48-
? new Date(session.expires_at * 1000)
49-
: undefined,
50-
userID: session.user.id,
41+
token: session.access_token ?? '',
42+
expiresAt: session.expires_at ? new Date(session.expires_at * 1000) : undefined,
43+
userID: session.user.id
5144
};
5245
}
5346

@@ -62,36 +55,35 @@ export class Connector implements PowerSyncBackendConnector {
6255
try {
6356
// Note: If transactional consistency is important, use database functions
6457
// or edge functions to process the entire transaction in a single call.
65-
for (let op of transaction.crud) {
58+
for (const op of transaction.crud) {
6659
lastOp = op;
6760
const table = this.supabaseClient.from(op.table);
61+
let result: any;
6862
switch (op.op) {
69-
case UpdateType.PUT:
63+
case UpdateType.PUT: {
7064
const record = { ...op.opData, id: op.id };
71-
const { error } = await table.upsert(record);
72-
73-
if (error) {
74-
throw new Error(
75-
`Could not upsert data to Supabase ${JSON.stringify(error)}`,
76-
);
77-
}
65+
result = await table.upsert(record);
7866
break;
67+
}
7968
case UpdateType.PATCH:
80-
await table.update(op.opData).eq("id", op.id);
69+
result = await table.update(op.opData).eq('id', op.id);
8170
break;
8271
case UpdateType.DELETE:
83-
await table.delete().eq("id", op.id);
72+
result = await table.delete().eq('id', op.id);
8473
break;
8574
}
75+
76+
if (result.error) {
77+
console.error(result.error);
78+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
79+
throw result.error;
80+
}
8681
}
8782

8883
await transaction.complete();
8984
} catch (ex: any) {
9085
console.debug(ex);
91-
if (
92-
typeof ex.code == "string" &&
93-
FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))
94-
) {
86+
if (typeof ex.code == 'string' && FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))) {
9587
/**
9688
* Instead of blocking the queue with these errors,
9789
* discard the (rest of the) transaction.
@@ -100,7 +92,7 @@ export class Connector implements PowerSyncBackendConnector {
10092
* If protecting against data loss is important, save the failing records
10193
* elsewhere instead of discarding, and/or notify the user.
10294
*/
103-
console.error(`Data upload error - discarding ${lastOp}`, ex);
95+
console.error('Data upload error - discarding:', lastOp, ex);
10496
await transaction.complete();
10597
} else {
10698
// Error may be retryable - e.g. network error or temporary server error.

demos/react-native-supabase-todolist/library/supabase/SupabaseConnector.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ export class SupabaseConnector implements PowerSyncBackendConnector {
9292
}
9393

9494
if (result.error) {
95-
throw new Error(`Could not ${op.op} data to Supabase error: ${JSON.stringify(result)}`);
95+
console.error(result.error);
96+
result.error.message = `Could not ${op.op} data to Supabase error: ${JSON.stringify(result)}`;
97+
throw result.error;
9698
}
9799
}
98100

@@ -108,7 +110,7 @@ export class SupabaseConnector implements PowerSyncBackendConnector {
108110
* If protecting against data loss is important, save the failing records
109111
* elsewhere instead of discarding, and/or notify the user.
110112
*/
111-
console.error(`Data upload error - discarding ${lastOp}`, ex);
113+
console.error('Data upload error - discarding:', lastOp, ex);
112114
await transaction.complete();
113115
} else {
114116
// Error may be retryable - e.g. network error or temporary server error.

demos/react-supabase-todolist/src/library/powersync/SupabaseConnector.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
133133

134134
if (result.error) {
135135
console.error(result.error);
136-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
136+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
137+
throw result.error;
137138
}
138139
}
139140

@@ -149,7 +150,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
149150
* If protecting against data loss is important, save the failing records
150151
* elsewhere instead of discarding, and/or notify the user.
151152
*/
152-
console.error(`Data upload error - discarding ${lastOp}`, ex);
153+
console.error('Data upload error - discarding:', lastOp, ex);
153154
await transaction.complete();
154155
} else {
155156
// Error may be retryable - e.g. network error or temporary server error.

demos/vue-supabase-todolist/src/library/powersync/SupabaseConnector.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
133133

134134
if (result.error) {
135135
console.error(result.error);
136-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
136+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
137+
throw result.error;
137138
}
138139
}
139140

@@ -149,7 +150,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
149150
* If protecting against data loss is important, save the failing records
150151
* elsewhere instead of discarding, and/or notify the user.
151152
*/
152-
console.error(`Data upload error - discarding ${lastOp}`, ex);
153+
console.error('Data upload error - discarding:', lastOp, ex);
153154
await transaction.complete();
154155
} else {
155156
// Error may be retryable - e.g. network error or temporary server error.

demos/yjs-react-supabase-text-collab/src/library/powersync/SupabaseConnector.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,18 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
119119

120120
if (result.error) {
121121
console.error(result.error);
122-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
122+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
123+
throw result.error;
123124
}
124125
}
125126

126127
if (updateBatch.length > 0) {
127128
console.log('inserting batch of size', updateBatch.length);
128129
const result = await this.client.rpc('insert_document_updates', { batch: JSON.stringify(updateBatch) });
129130
if (result.error) {
130-
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
131+
console.error(result.error);
132+
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
133+
throw result.error;
131134
}
132135
}
133136

@@ -142,7 +145,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
142145
* If protecting against data loss is important, save the failing records
143146
* elsewhere instead of discarding, and/or notify the user.
144147
*/
145-
console.error(`Data upload error - discarding ${lastOp}`, ex);
148+
console.error('Data upload error - discarding:', lastOp, ex);
146149
await batch.complete();
147150
} else {
148151
// Error may be retryable - e.g. network error or temporary server error.

0 commit comments

Comments
 (0)