pull/3231/head
Dan Groshev 2024-03-21 17:36:16 +00:00
rodzic 42de5b0449
commit bf96e1dd4c
Nie znaleziono w bazie danych klucza dla tego podpisu
10 zmienionych plików z 150 dodań i 21 usunięć

Wyświetl plik

@ -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')
}

Wyświetl plik

@ -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"

Wyświetl plik

@ -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 })

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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

Wyświetl plik

@ -1,6 +1,6 @@
{
"extends": "../../config/tsconfig.base.json",
"include": ["src", "scripts"],
"include": ["src", "scripts", "migrations"],
"exclude": ["node_modules", "dist", ".tsbuild*"],
"compilerOptions": {
"noEmit": true,

Wyświetl plik

@ -1,3 +1,4 @@
export { type TLAnalyticsPoint } from './lib/TLAnalytics'
export { TLServer, type DBLoadResult } from './lib/TLServer'
export {
TLSyncClient,

Wyświetl plik

@ -0,0 +1,5 @@
export type TLAnalyticsPoint = {
type: 'outstanding_data_messages'
length: number
num_clients: number
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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"