diff --git a/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts b/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts index 8cad7356e..ed6a37d8e 100644 --- a/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts +++ b/apps/dotcom-worker/src/lib/TLDrawDurableObject.ts @@ -5,12 +5,13 @@ import { SupabaseClient } from '@supabase/supabase-js' import { RoomSnapshot, TLServer, + TLServerEvent, TLSyncRoom, type DBLoadResult, type PersistedRoomSnapshotForSupabase, type RoomState, } from '@tldraw/tlsync' -import { assert, assertExists } from '@tldraw/utils' +import { assert, assertExists, exhaustiveSwitchError } from '@tldraw/utils' import { IRequest, Router } from 'itty-router' import Toucan from 'toucan-js' import { AlarmScheduler } from './AlarmScheduler' @@ -255,38 +256,42 @@ export class TLDrawDurableObject extends TLServer { return new Response(null, { status: 101, webSocket: clientWebSocket }) } - logEvent( - event: - | { - type: 'client' - roomId: string - name: string - clientId: string - instanceId: string - localClientId: string - } - | { - type: 'room' - roomId: string - name: string - } + private writeEvent( + name: string, + { blobs, indexes, doubles }: { blobs?: string[]; indexes?: [string]; doubles?: number[] } ) { + this.measure?.writeDataPoint({ + blobs: [name, this.env.WORKER_NAME ?? 'development-tldraw-multiplayer', ...(blobs ?? [])], + doubles, + indexes, + }) + } + + logEvent(event: TLServerEvent) { switch (event.type) { case 'room': { - this.measure?.writeDataPoint({ - blobs: [event.name, event.roomId], // we would add user/connection ids here if we could - }) - + // we would add user/connection ids here if we could + this.writeEvent(event.name, { blobs: [event.roomId] }) break } case 'client': { - this.measure?.writeDataPoint({ - blobs: [event.name, event.roomId, event.clientId, event.instanceId], // we would add user/connection ids here if we could + // we would add user/connection ids here if we could + this.writeEvent(event.name, { + blobs: [event.roomId, event.clientId, event.instanceId], indexes: [event.localClientId], }) - break } + case 'send_message': { + this.writeEvent(event.type, { + blobs: [event.roomId, event.messageType], + doubles: [event.messageLength], + }) + break + } + default: { + exhaustiveSwitchError(event) + } } } diff --git a/apps/dotcom-worker/src/lib/types.ts b/apps/dotcom-worker/src/lib/types.ts index b454677d6..3505ad5c6 100644 --- a/apps/dotcom-worker/src/lib/types.ts +++ b/apps/dotcom-worker/src/lib/types.ts @@ -26,4 +26,5 @@ export interface Environment { TLDRAW_ENV: string | undefined SENTRY_DSN: string | undefined IS_LOCAL: string | undefined + WORKER_NAME: string | undefined } diff --git a/packages/tlsync/src/index.ts b/packages/tlsync/src/index.ts index 57f5aaab9..fd95e08d4 100644 --- a/packages/tlsync/src/index.ts +++ b/packages/tlsync/src/index.ts @@ -1,4 +1,4 @@ -export { TLServer, type DBLoadResult } from './lib/TLServer' +export { TLServer, type DBLoadResult, type TLServerEvent } from './lib/TLServer' export { TLSyncClient, type TLPersistentClientSocket, diff --git a/packages/tlsync/src/lib/ServerSocketAdapter.ts b/packages/tlsync/src/lib/ServerSocketAdapter.ts index c91e75223..8407dfcc7 100644 --- a/packages/tlsync/src/lib/ServerSocketAdapter.ts +++ b/packages/tlsync/src/lib/ServerSocketAdapter.ts @@ -3,18 +3,25 @@ import ws from 'ws' import { TLRoomSocket } from './TLSyncRoom' import { TLSocketServerSentEvent } from './protocol' +type ServerSocketAdapterOptions = { + readonly ws: WebSocket | ws.WebSocket + readonly logSendMessage: (type: string, size: number) => void +} + /** @public */ export class ServerSocketAdapter implements TLRoomSocket { - constructor(public readonly ws: WebSocket | ws.WebSocket) {} + constructor(public readonly opts: ServerSocketAdapterOptions) {} // eslint-disable-next-line no-restricted-syntax get isOpen(): boolean { - return this.ws.readyState === 1 // ready state open + return this.opts.ws.readyState === 1 // ready state open } // see TLRoomSocket for details on why this accepts a union and not just arrays sendMessage(msg: TLSocketServerSentEvent) { - this.ws.send(JSON.stringify(msg)) + const message = JSON.stringify(msg) + this.opts.logSendMessage(msg.type, message.length) + this.opts.ws.send(message) } close() { - this.ws.close() + this.opts.ws.close() } } diff --git a/packages/tlsync/src/lib/TLServer.ts b/packages/tlsync/src/lib/TLServer.ts index 5389a3410..ecafaab18 100644 --- a/packages/tlsync/src/lib/TLServer.ts +++ b/packages/tlsync/src/lib/TLServer.ts @@ -20,6 +20,32 @@ export type DBLoadResult = type: 'room_not_found' } +export type TLServerEvent = + | { + type: 'client' + name: 'room_create' | 'room_reopen' | 'enter' | 'leave' | 'last_out' + roomId: string + clientId: string + instanceId: string + localClientId: string + } + | { + type: 'room' + name: + | 'failed_load_from_db' + | 'failed_persist_to_db' + | 'room_empty' + | 'fail_persist' + | 'room_start' + roomId: string + } + | { + type: 'send_message' + roomId: string + messageType: string + messageLength: number + } + /** * This class manages rooms for a websocket server. * @@ -116,7 +142,19 @@ export abstract class TLServer { const clientId = nanoid() const [roomState, roomOpenKind] = await this.getInitialRoomState(persistenceKey) - roomState.room.handleNewSession(sessionKey, new ServerSocketAdapter(socket)) + roomState.room.handleNewSession( + sessionKey, + new ServerSocketAdapter({ + ws: socket, + logSendMessage: (messageType, messageLength) => + this.logEvent({ + type: 'send_message', + roomId: persistenceKey, + messageType, + messageLength, + }), + }) + ) if (roomOpenKind === 'new' || roomOpenKind === 'reopen') { // Record that the room is now active @@ -223,22 +261,7 @@ export abstract class TLServer { * @param event - The event to log. * @public */ - abstract logEvent( - event: - | { - type: 'client' - roomId: string - name: string - clientId: string - instanceId: string - localClientId: string - } - | { - type: 'room' - roomId: string - name: string - } - ): void + abstract logEvent(event: TLServerEvent): void /** * Get a room by its id. diff --git a/scripts/deploy.ts b/scripts/deploy.ts index d550ac988..4c39f387d 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -185,6 +185,7 @@ name = "${previewId}-tldraw-assets"` let didUpdateTlsyncWorker = false async function deployTlsyncWorker({ dryRun }: { dryRun: boolean }) { + const workerId = `${previewId ?? env.TLDRAW_ENV}-tldraw-multiplayer` if (previewId && !didUpdateTlsyncWorker) { appendFileSync( join(worker, 'wrangler.toml'), @@ -212,6 +213,8 @@ name = "${previewId}-tldraw-multiplayer"` `TLDRAW_ENV:${env.TLDRAW_ENV}`, '--var', `APP_ORIGIN:${env.APP_ORIGIN}`, + '--var', + `WORKER_NAME:${workerId}`, ], { pwd: worker,