Revert "feat: migrate data persistence from Supabase to Postgres"
Some checks failed
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests / End to end tests (push) Has been cancelled
Publish Canary Packages / Publish Canary Packages (push) Has been cancelled
Publish VS Code Extension / Publish VS Code Extension (push) Has been cancelled

This reverts commit 43419581be.
This commit is contained in:
Kumi 2024-07-17 09:11:54 +02:00
parent 69a88c8981
commit c0a9644296
Signed by: kumi
GPG key ID: ECBCC9082395383F
3 changed files with 398 additions and 423 deletions

View file

@ -1,7 +1,7 @@
/// <reference no-default-lib="true"/> /// <reference no-default-lib="true"/>
/// <reference types="@cloudflare/workers-types" /> /// <reference types="@cloudflare/workers-types" />
import { Client as PostgresClient } from 'pg' import { SupabaseClient } from '@supabase/supabase-js'
import { import {
READ_ONLY_LEGACY_PREFIX, READ_ONLY_LEGACY_PREFIX,
READ_ONLY_PREFIX, READ_ONLY_PREFIX,
@ -11,18 +11,19 @@ import {
} from '@tldraw/dotcom-shared' } from '@tldraw/dotcom-shared'
import { import {
RoomSnapshot, RoomSnapshot,
TLCloseEventCode,
TLSocketRoom, TLSocketRoom,
type PersistedRoomSnapshotForSupabase, type PersistedRoomSnapshotForSupabase,
} from '@tldraw/sync-core' } from '@tldraw/sync-core'
import { TLRecord } from '@tldraw/tlschema' import { TLRecord } from '@tldraw/tlschema'
import { assertExists, exhaustiveSwitchError } from '@tldraw/utils' import { assert, assertExists, exhaustiveSwitchError } from '@tldraw/utils'
import { createPersistQueue, createSentry } from '@tldraw/worker-shared' import { createPersistQueue, createSentry } from '@tldraw/worker-shared'
import { IRequest, Router } from 'itty-router' import { IRequest, Router } from 'itty-router'
import { AlarmScheduler } from './AlarmScheduler' import { AlarmScheduler } from './AlarmScheduler'
import { PERSIST_INTERVAL_MS } from './config' import { PERSIST_INTERVAL_MS } from './config'
import { getR2KeyForRoom } from './r2' import { getR2KeyForRoom } from './r2'
import { Analytics, DBLoadResult, Environment, TLServerEvent } from './types' import { Analytics, DBLoadResult, Environment, TLServerEvent } from './types'
import { createPostgresClient } from './utils/createPostgresClient' import { createSupabaseClient } from './utils/createSupabaseClient'
import { getSlug } from './utils/roomOpenMode' import { getSlug } from './utils/roomOpenMode'
import { throttle } from './utils/throttle' import { throttle } from './utils/throttle'
@ -118,7 +119,7 @@ export class TLDrawDurableObject {
storage: DurableObjectStorage storage: DurableObjectStorage
// For persistence // For persistence
postgresClient: PostgresClient | null supabaseClient: SupabaseClient | void
// For analytics // For analytics
measure: Analytics | undefined measure: Analytics | undefined
@ -126,7 +127,7 @@ export class TLDrawDurableObject {
// For error tracking // For error tracking
sentryDSN: string | undefined sentryDSN: string | undefined
readonly postgresTable: string readonly supabaseTable: string
readonly r2: { readonly r2: {
readonly rooms: R2Bucket readonly rooms: R2Bucket
readonly versionCache: R2Bucket readonly versionCache: R2Bucket
@ -142,9 +143,9 @@ export class TLDrawDurableObject {
this.storage = state.storage this.storage = state.storage
this.sentryDSN = env.SENTRY_DSN this.sentryDSN = env.SENTRY_DSN
this.measure = env.MEASURE this.measure = env.MEASURE
this.postgresClient = createPostgresClient(env) this.supabaseClient = createSupabaseClient(env)
this.postgresTable = env.TLDRAW_ENV === 'production' ? 'drawings' : 'drawings_staging' this.supabaseTable = env.TLDRAW_ENV === 'production' ? 'drawings' : 'drawings_staging'
this.r2 = { this.r2 = {
rooms: env.ROOMS, rooms: env.ROOMS,
versionCache: env.ROOMS_HISTORY_EPHEMERAL, versionCache: env.ROOMS_HISTORY_EPHEMERAL,
@ -348,7 +349,7 @@ export class TLDrawDurableObject {
} }
} }
// Load the room's drawing data. First we check the R2 bucket, then we fallback to Postgres (legacy). // Load the room's drawing data. First we check the R2 bucket, then we fallback to supabase (legacy).
async loadFromDatabase(persistenceKey: string): Promise<DBLoadResult> { async loadFromDatabase(persistenceKey: string): Promise<DBLoadResult> {
try { try {
const key = getR2KeyForRoom(persistenceKey) const key = getR2KeyForRoom(persistenceKey)
@ -358,18 +359,26 @@ export class TLDrawDurableObject {
return { type: 'room_found', snapshot: await roomFromBucket.json() } return { type: 'room_found', snapshot: await roomFromBucket.json() }
} }
// if we don't have a room in the bucket, try to load from Postgres // if we don't have a room in the bucket, try to load from supabase
if (!this.postgresClient) return { type: 'room_not_found' } if (!this.supabaseClient) return { type: 'room_not_found' }
await this.postgresClient.connect() const { data, error } = await this.supabaseClient
const result = await this.postgresClient.query('SELECT * FROM ' + this.postgresTable + ' WHERE slug = $1', [persistenceKey]) .from(this.supabaseTable)
await this.postgresClient.end() .select('*')
.eq('slug', persistenceKey)
if (result.rows.length === 0) { if (error) {
this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_load_from_db' })
console.error('failed to retrieve document', persistenceKey, error)
return { type: 'error', error: new Error(error.message) }
}
// if it didn't find a document, data will be an empty array
if (data.length === 0) {
return { type: 'room_not_found' } return { type: 'room_not_found' }
} }
const roomFromPostgres = result.rows[0] as PersistedRoomSnapshotForSupabase const roomFromSupabase = data[0] as PersistedRoomSnapshotForSupabase
return { type: 'room_found', snapshot: roomFromPostgres.drawing } return { type: 'room_found', snapshot: roomFromSupabase.drawing }
} catch (error) { } catch (error) {
this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_load_from_db' }) this.logEvent({ type: 'room', roomId: persistenceKey, name: 'failed_load_from_db' })
@ -400,7 +409,7 @@ export class TLDrawDurableObject {
// just in case there's any possibility of setting up a neverending queue // just in case there's any possibility of setting up a neverending queue
}, PERSIST_INTERVAL_MS / 2) }, PERSIST_INTERVAL_MS / 2)
// Save the room to Postgres // Save the room to supabase
async persistToDatabase() { async persistToDatabase() {
await this._persistQueue() await this._persistQueue()
} }

View file

@ -3,7 +3,9 @@ import { notFound } from '@tldraw/worker-shared'
import { IRequest } from 'itty-router' import { IRequest } from 'itty-router'
import { getR2KeyForSnapshot } from '../r2' import { getR2KeyForSnapshot } from '../r2'
import { Environment } from '../types' import { Environment } from '../types'
import { createPostgresClient, noPostgresSorry } from '../utils/createPostgresClient' import { createSupabaseClient, noSupabaseSorry } from '../utils/createSupabaseClient'
import { getSnapshotsTable } from '../utils/getSnapshotsTable'
import { R2Snapshot } from './createRoomSnapshot'
function generateReponse(roomId: string, data: RoomSnapshot) { function generateReponse(roomId: string, data: RoomSnapshot) {
return new Response( return new Response(
@ -37,23 +39,22 @@ export async function getRoomSnapshot(request: IRequest, env: Environment): Prom
} }
} }
// If we can't find the snapshot in R2 then fallback to Postgres // If we can't find the snapshot in R2 then fallback to Supabase
// Create a Postgres client // Create a supabase client
const postgresClient = createPostgresClient(env) const supabase = createSupabaseClient(env)
if (!postgresClient) return noPostgresSorry() if (!supabase) return noSupabaseSorry()
try { // Get the snapshot from the table
await postgresClient.connect() const supabaseTable = getSnapshotsTable(env)
const result = await postgresClient.query('SELECT drawing FROM snapshots WHERE slug = $1 LIMIT 1', [roomId]) const result = await supabase
await postgresClient.end() .from(supabaseTable)
.select('drawing')
.eq('slug', roomId)
.maybeSingle()
const data = result.data?.drawing as RoomSnapshot
if (result.rows.length === 0) return notFound() if (!data) return notFound()
const data = result.rows[0].drawing as RoomSnapshot
// Send back the snapshot! // Send back the snapshot!
return generateReponse(roomId, data) return generateReponse(roomId, data)
} catch (err) {
console.error('Error querying Postgres', err)
return new Response(JSON.stringify({ error: true, message: 'Error querying Postgres' }), { status: 500 })
}
} }

View file

@ -1,35 +0,0 @@
import { Client } from 'pg'
import { Environment } from '../types'
export function createPostgresClient(env: Environment) {
if (env.POSTGRES_HOST && env.POSTGRES_USER && env.POSTGRES_PASSWORD && env.POSTGRES_DB) {
var client = new Client({
host: env.POSTGRES_HOST,
port: env.POSTGRES_PORT ? parseInt(env.POSTGRES_PORT) : 5432,
user: env.POSTGRES_USER,
password: env.POSTGRES_PASSWORD,
database: env.POSTGRES_DB,
})
client.connect()
client.query(`
CREATE TABLE IF NOT EXISTS snapshots (
id SERIAL PRIMARY KEY,
slug VARCHAR(255) UNIQUE NOT NULL,
drawing JSONB NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
`)
return client
} else {
console.warn('No Postgres credentials, loading from Postgres disabled')
return null
}
}
export function noPostgresSorry() {
return new Response(JSON.stringify({ error: true, message: 'Could not create Postgres client' }))
}