kopia lustrzana https://github.com/Tldraw/Tldraw
migrations
rodzic
42de5b0449
commit
bf96e1dd4c
|
@ -0,0 +1,26 @@
|
|||
/* eslint-disable camelcase */
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.createTable('outstanding_data_messages', {
|
||||
id: 'id',
|
||||
created_at: {
|
||||
type: 'timestamp',
|
||||
notNull: true,
|
||||
default: pgm.func('current_timestamp'),
|
||||
},
|
||||
length: {
|
||||
type: 'integer',
|
||||
notNull: true,
|
||||
},
|
||||
num_clients: {
|
||||
type: 'integer',
|
||||
notNull: true,
|
||||
},
|
||||
// metadata
|
||||
tldraw_env: {
|
||||
type: 'varchar(255)',
|
||||
notNull: true,
|
||||
},
|
||||
})
|
||||
pgm.createIndex('outstanding_data_messages', 'created_at')
|
||||
}
|
|
@ -18,7 +18,8 @@
|
|||
"test-ci": "lazy inherit",
|
||||
"test": "yarn run -T jest",
|
||||
"test-coverage": "lazy inherit",
|
||||
"lint": "yarn run -T tsx ../../scripts/lint.ts"
|
||||
"lint": "yarn run -T tsx ../../scripts/lint.ts",
|
||||
"migrate": "node-pg-migrate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/auth-helpers-remix": "^0.2.2",
|
||||
|
@ -40,6 +41,7 @@
|
|||
"@cloudflare/workers-types": "^4.20230821.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"lazyrepo": "0.0.0-alpha.27",
|
||||
"node-pg-migrate": "^6.2.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"typescript": "^5.3.3",
|
||||
"wrangler": "3.19.0"
|
||||
|
|
|
@ -12,8 +12,10 @@ import {
|
|||
} from '@tldraw/tlsync'
|
||||
import { assert, assertExists } from '@tldraw/utils'
|
||||
import { IRequest, Router } from 'itty-router'
|
||||
import { Client as PgClient } from 'pg'
|
||||
import Toucan from 'toucan-js'
|
||||
import { AlarmScheduler } from './AlarmScheduler'
|
||||
import { report } from './analytics'
|
||||
import { PERSIST_INTERVAL_MS } from './config'
|
||||
import { getR2KeyForRoom } from './r2'
|
||||
import { Analytics, Environment } from './types'
|
||||
|
@ -56,6 +58,8 @@ export class TLDrawDurableObject extends TLServer {
|
|||
|
||||
_documentInfo: DocumentInfo | null = null
|
||||
|
||||
private readonly analyticsPgClient: PgClient
|
||||
|
||||
constructor(
|
||||
private controller: DurableObjectState,
|
||||
private env: Environment
|
||||
|
@ -74,6 +78,11 @@ export class TLDrawDurableObject extends TLServer {
|
|||
versionCache: env.ROOMS_HISTORY_EPHEMERAL,
|
||||
}
|
||||
|
||||
this.analyticsPgClient = new PgClient({
|
||||
connectionString: env.ANALYTICS_DB_HYPERDRIVE.connectionString,
|
||||
})
|
||||
controller.blockConcurrencyWhile(this.analyticsPgClient.connect)
|
||||
|
||||
controller.blockConcurrencyWhile(async () => {
|
||||
const existingDocumentInfo = (await this.storage.get('documentInfo')) as DocumentInfo | null
|
||||
if (existingDocumentInfo?.version !== CURRENT_DOCUMENT_INFO_VERSION) {
|
||||
|
@ -184,15 +193,19 @@ export class TLDrawDurableObject extends TLServer {
|
|||
delete tombstones[id]
|
||||
})
|
||||
|
||||
const newRoom = new TLSyncRoom(roomState.room.schema, {
|
||||
clock: oldRoom.clock + 1,
|
||||
documents: snapshot.documents.map((d) => ({
|
||||
lastChangedClock: oldRoom.clock + 1,
|
||||
state: d.state,
|
||||
})),
|
||||
schema: snapshot.schema,
|
||||
tombstones,
|
||||
})
|
||||
const newRoom = new TLSyncRoom(
|
||||
roomState.room.schema,
|
||||
{
|
||||
clock: oldRoom.clock + 1,
|
||||
documents: snapshot.documents.map((d) => ({
|
||||
lastChangedClock: oldRoom.clock + 1,
|
||||
state: d.state,
|
||||
})),
|
||||
schema: snapshot.schema,
|
||||
tombstones,
|
||||
},
|
||||
(point) => report(this.analyticsPgClient, this.env.TLDRAW_ENV ?? 'undefined', point)
|
||||
)
|
||||
|
||||
// replace room with new one and kick out all the clients
|
||||
this.setRoomState(this.documentInfo.slug, { ...roomState, room: newRoom })
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { TLAnalyticsPoint } from '@tldraw/tlsync'
|
||||
import { exhaustiveSwitchError } from '@tldraw/utils'
|
||||
import { Client as PgClient } from 'pg'
|
||||
|
||||
const SAMPLE_RATE = 0.05
|
||||
|
||||
export function report(client: PgClient, tldraw_env: string, point: TLAnalyticsPoint) {
|
||||
try {
|
||||
if (shouldSample()) {
|
||||
let promise = null
|
||||
|
||||
switch (point.type) {
|
||||
case 'outstanding_data_messages': {
|
||||
promise = client.query(
|
||||
'INSERT INTO outstanding_data_messages (tldraw_env, length, num_clients) VALUES ($1, $2, $3)',
|
||||
[tldraw_env, point.length, point.num_clients] as any[]
|
||||
)
|
||||
break
|
||||
}
|
||||
default:
|
||||
exhaustiveSwitchError(point.type)
|
||||
}
|
||||
|
||||
promise!.catch((e) => {
|
||||
console.error('Error reporting an analytic point of type', point.type, e)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error reporting an analytic point of type', point.type, e)
|
||||
}
|
||||
}
|
||||
|
||||
function shouldSample() {
|
||||
return Math.random() < SAMPLE_RATE
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
/// <reference types="@cloudflare/workers-types" />
|
||||
import { IRequest, Router, createCors } from 'itty-router'
|
||||
import { Client as PgClient } from 'pg'
|
||||
import { env } from 'process'
|
||||
import Toucan from 'toucan-js'
|
||||
import { createRoom } from './routes/createRoom'
|
||||
import { createRoomSnapshot } from './routes/createRoomSnapshot'
|
||||
|
@ -35,7 +36,6 @@ const router = Router()
|
|||
.get('/r/:roomId', joinExistingRoom)
|
||||
.get('/r/:roomId/history', getRoomHistory)
|
||||
.get('/r/:roomId/history/:timestamp', getRoomHistorySnapshot)
|
||||
.get('/pgtest', pgTest)
|
||||
.post('/r/:roomId/restore', forwardRoomRequest)
|
||||
.all('*', fourOhFour)
|
||||
|
||||
|
@ -99,11 +99,11 @@ async function blockUnknownOrigins(request: Request) {
|
|||
return undefined
|
||||
}
|
||||
|
||||
// const origin = request.headers.get('origin')
|
||||
// if (env.IS_LOCAL !== 'true' && (!origin || !isAllowedOrigin(origin))) {
|
||||
// console.error('Attempting to connect from an invalid origin:', origin, env, request)
|
||||
// return new Response('Not allowed', { status: 403 })
|
||||
// }
|
||||
const origin = request.headers.get('origin')
|
||||
if (env.IS_LOCAL !== 'true' && (!origin || !isAllowedOrigin(origin))) {
|
||||
console.error('Attempting to connect from an invalid origin:', origin, env, request)
|
||||
return new Response('Not allowed', { status: 403 })
|
||||
}
|
||||
|
||||
// origin doesn't match, so we can continue
|
||||
return undefined
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"extends": "../../config/tsconfig.base.json",
|
||||
"include": ["src", "scripts"],
|
||||
"include": ["src", "scripts", "migrations"],
|
||||
"exclude": ["node_modules", "dist", ".tsbuild*"],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export { type TLAnalyticsPoint } from './lib/TLAnalytics'
|
||||
export { TLServer, type DBLoadResult } from './lib/TLServer'
|
||||
export {
|
||||
TLSyncClient,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
export type TLAnalyticsPoint = {
|
||||
type: 'outstanding_data_messages'
|
||||
length: number
|
||||
num_clients: number
|
||||
}
|
|
@ -31,6 +31,7 @@ import {
|
|||
SESSION_REMOVAL_WAIT_TIME,
|
||||
SESSION_START_WAIT_TIME,
|
||||
} from './RoomSession'
|
||||
import { TLAnalyticsPoint } from './TLAnalytics'
|
||||
import {
|
||||
NetworkDiff,
|
||||
ObjectDiff,
|
||||
|
@ -208,7 +209,8 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
|||
|
||||
constructor(
|
||||
public readonly schema: StoreSchema<R, any>,
|
||||
snapshot?: RoomSnapshot
|
||||
snapshot?: RoomSnapshot,
|
||||
readonly reportTLAnalytics?: (point: TLAnalyticsPoint) => void
|
||||
) {
|
||||
assert(
|
||||
isNativeStructuredClone,
|
||||
|
@ -449,6 +451,12 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
|||
|
||||
session.debounceTimer = null
|
||||
|
||||
this.reportTLAnalytics?.({
|
||||
type: 'outstanding_data_messages',
|
||||
length: session.outstandingDataMessages.length,
|
||||
num_clients: this.sessions.size,
|
||||
})
|
||||
|
||||
if (session.outstandingDataMessages.length > 0) {
|
||||
if (session.isV4Client) {
|
||||
// v4 clients don't support the "data" message, so we need to send each message separately
|
||||
|
|
45
yarn.lock
45
yarn.lock
|
@ -7264,6 +7264,7 @@ __metadata:
|
|||
itty-router: "npm:^4.0.13"
|
||||
lazyrepo: "npm:0.0.0-alpha.27"
|
||||
nanoid: "npm:4.0.2"
|
||||
node-pg-migrate: "npm:^6.2.2"
|
||||
pg: "npm:^8.11.3"
|
||||
picocolors: "npm:^1.0.0"
|
||||
react: "npm:^18.2.0"
|
||||
|
@ -8111,7 +8112,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/pg@npm:^8.11.4":
|
||||
"@types/pg@npm:^8.0.0, @types/pg@npm:^8.11.4":
|
||||
version: 8.11.4
|
||||
resolution: "@types/pg@npm:8.11.4"
|
||||
dependencies:
|
||||
|
@ -11279,6 +11280,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decamelize@npm:^5.0.0":
|
||||
version: 5.0.1
|
||||
resolution: "decamelize@npm:5.0.1"
|
||||
checksum: 643e88804c538a334fae303ae1da8b30193b81dad8689643b35e6ab8ab60a3b03492cab6096d8163bd41fd384d969485f0634c000f80af502aa7f4047258d5b4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decimal.js@npm:^10.4.2":
|
||||
version: 10.4.3
|
||||
resolution: "decimal.js@npm:10.4.3"
|
||||
|
@ -18766,7 +18774,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4":
|
||||
"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4, mkdirp@npm:~1.0.0":
|
||||
version: 1.0.4
|
||||
resolution: "mkdirp@npm:1.0.4"
|
||||
bin:
|
||||
|
@ -19196,6 +19204,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-pg-migrate@npm:^6.2.2":
|
||||
version: 6.2.2
|
||||
resolution: "node-pg-migrate@npm:6.2.2"
|
||||
dependencies:
|
||||
"@types/pg": "npm:^8.0.0"
|
||||
decamelize: "npm:^5.0.0"
|
||||
mkdirp: "npm:~1.0.0"
|
||||
yargs: "npm:~17.3.0"
|
||||
peerDependencies:
|
||||
pg: ">=4.3.0 <9.0.0"
|
||||
bin:
|
||||
node-pg-migrate: bin/node-pg-migrate
|
||||
checksum: 5afd5ee8d9097916f294904a166930e5ba96b6d24fc8ecba9b7533e37c508b030b698f1e1c3659e91a717f22fc8177213e0d674e22983bfec83eeb043c30b330
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-releases@npm:^2.0.14":
|
||||
version: 2.0.14
|
||||
resolution: "node-releases@npm:2.0.14"
|
||||
|
@ -25441,7 +25465,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs-parser@npm:^21.1.1":
|
||||
"yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.1.1":
|
||||
version: 21.1.1
|
||||
resolution: "yargs-parser@npm:21.1.1"
|
||||
checksum: 9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e
|
||||
|
@ -25527,6 +25551,21 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs@npm:~17.3.0":
|
||||
version: 17.3.1
|
||||
resolution: "yargs@npm:17.3.1"
|
||||
dependencies:
|
||||
cliui: "npm:^7.0.2"
|
||||
escalade: "npm:^3.1.1"
|
||||
get-caller-file: "npm:^2.0.5"
|
||||
require-directory: "npm:^2.1.1"
|
||||
string-width: "npm:^4.2.3"
|
||||
y18n: "npm:^5.0.5"
|
||||
yargs-parser: "npm:^21.0.0"
|
||||
checksum: 7bb96c62cd56f1290d8a411ddb848f3af147773199c8653be5ee940ddd042391d7e61b0911a98ab6b3d74a92fff52694f4ed246333accf0523c201a2a6649e2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yauzl@npm:^2.3.1":
|
||||
version: 2.10.0
|
||||
resolution: "yauzl@npm:2.10.0"
|
||||
|
|
Ładowanie…
Reference in New Issue