Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SupabaseConnector upload queue error code fix #223

Merged
merged 2 commits into from
Jul 11, 2024
Merged
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
12 changes: 12 additions & 0 deletions .changeset/famous-fishes-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'react-native-supabase-group-chat': patch
'react-native-supabase-todolist': patch
'yjs-react-supabase-text-collab': patch
'django-react-native-todolist': patch
'angular-supabase-todolist': patch
'react-supabase-todolist': patch
'vue-supabase-todolist': patch
'react-multi-client': patch
---

Ensuring that SupabaseConnector's error codes are evaluated when processing upload queue.
6 changes: 4 additions & 2 deletions demos/angular-supabase-todolist/src/app/supabase.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ export class SupabaseService implements PowerSyncBackendConnector {
}

if (result.error) {
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
console.error(result.error);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

Expand All @@ -147,7 +149,7 @@ export class SupabaseService implements PowerSyncBackendConnector {
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class DjangoConnector implements PowerSyncBackendConnector {
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
5 changes: 3 additions & 2 deletions demos/react-multi-client/src/library/SupabaseConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i

if (result.error) {
console.error(result.error);
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

Expand All @@ -120,7 +121,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
62 changes: 27 additions & 35 deletions demos/react-native-supabase-group-chat/src/lib/connector.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import {
AbstractPowerSyncDatabase,
CrudEntry,
PowerSyncBackendConnector,
UpdateType,
} from "@powersync/react-native";
import type { SupabaseClient } from "@supabase/supabase-js";
import { AbstractPowerSyncDatabase, CrudEntry, PowerSyncBackendConnector, UpdateType } from '@powersync/react-native';
import type { SupabaseClient } from '@supabase/supabase-js';

import { config } from "./config";
import { supabase } from "./supabase";
import { config } from './config';
import { supabase } from './supabase';

/// Postgres Response codes that we cannot recover from by retrying.
const FATAL_RESPONSE_CODES = [
// Class 22 — Data Exception
// Examples include data type mismatch.
new RegExp("^22...$"),
new RegExp('^22...$'),
// Class 23 — Integrity Constraint Violation.
// Examples include NOT NULL, FOREIGN KEY and UNIQUE violations.
new RegExp("^23...$"),
new RegExp('^23...$'),
// INSUFFICIENT PRIVILEGE - typically a row-level security violation
new RegExp("^42501$"),
new RegExp('^42501$')
];

export class Connector implements PowerSyncBackendConnector {
Expand All @@ -31,23 +26,21 @@ export class Connector implements PowerSyncBackendConnector {
async fetchCredentials() {
const {
data: { session },
error,
error
} = await this.supabaseClient.auth.getSession();

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

console.debug("session expires at", session.expires_at);
console.debug('session expires at', session.expires_at);

return {
client: this.supabaseClient,
endpoint: config.powerSyncUrl,
token: session.access_token ?? "",
expiresAt: session.expires_at
? new Date(session.expires_at * 1000)
: undefined,
userID: session.user.id,
token: session.access_token ?? '',
expiresAt: session.expires_at ? new Date(session.expires_at * 1000) : undefined,
userID: session.user.id
};
}

Expand All @@ -62,36 +55,35 @@ export class Connector implements PowerSyncBackendConnector {
try {
// Note: If transactional consistency is important, use database functions
// or edge functions to process the entire transaction in a single call.
for (let op of transaction.crud) {
for (const op of transaction.crud) {
lastOp = op;
const table = this.supabaseClient.from(op.table);
let result: any;
switch (op.op) {
case UpdateType.PUT:
case UpdateType.PUT: {
const record = { ...op.opData, id: op.id };
const { error } = await table.upsert(record);

if (error) {
throw new Error(
`Could not upsert data to Supabase ${JSON.stringify(error)}`,
);
}
result = await table.upsert(record);
break;
}
case UpdateType.PATCH:
await table.update(op.opData).eq("id", op.id);
result = await table.update(op.opData).eq('id', op.id);
break;
case UpdateType.DELETE:
await table.delete().eq("id", op.id);
result = await table.delete().eq('id', op.id);
break;
}

if (result.error) {
console.error(result.error);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

await transaction.complete();
} catch (ex: any) {
console.debug(ex);
if (
typeof ex.code == "string" &&
FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))
) {
if (typeof ex.code == 'string' && FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))) {
/**
* Instead of blocking the queue with these errors,
* discard the (rest of the) transaction.
Expand All @@ -100,7 +92,7 @@ export class Connector implements PowerSyncBackendConnector {
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ export class SupabaseConnector implements PowerSyncBackendConnector {
}

if (result.error) {
throw new Error(`Could not ${op.op} data to Supabase error: ${JSON.stringify(result)}`);
console.error(result.error);
result.error.message = `Could not ${op.op} data to Supabase error: ${JSON.stringify(result)}`;
throw result.error;
}
}

Expand All @@ -108,7 +110,7 @@ export class SupabaseConnector implements PowerSyncBackendConnector {
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i

if (result.error) {
console.error(result.error);
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

Expand All @@ -149,7 +150,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i

if (result.error) {
console.error(result.error);
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

Expand All @@ -149,7 +150,7 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i
* If protecting against data loss is important, save the failing records
* elsewhere instead of discarding, and/or notify the user.
*/
console.error(`Data upload error - discarding ${lastOp}`, ex);
console.error('Data upload error - discarding:', lastOp, ex);
await transaction.complete();
} else {
// Error may be retryable - e.g. network error or temporary server error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,18 @@ export class SupabaseConnector extends BaseObserver<SupabaseConnectorListener> i

if (result.error) {
console.error(result.error);
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

if (updateBatch.length > 0) {
console.log('inserting batch of size', updateBatch.length);
const result = await this.client.rpc('insert_document_updates', { batch: JSON.stringify(updateBatch) });
if (result.error) {
throw new Error(`Could not update Supabase. Received error: ${result.error.message}`);
console.error(result.error);
result.error.message = `Could not update Supabase. Received error: ${result.error.message}`;
throw result.error;
}
}

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