log message size in worker analytics (#3274)

Adds logging of message size in worker analytics.

This also adds the environment to worker analytics as `blob2`. We need
this because previously, all the analytics from all environments were
going to the same place with no ability to tell them apart, which means
we can't easily compare analytics on e.g. a particular PR.

This means that all the other blobs get shifted along one, so we won't
be able to query across the boundary of when this gets released for
those properties. I think this is fine though - it's things like
`roomId` that I don't think we were querying on anyway.

You can query the analytics through grafana - [docs
here](https://www.notion.so/tldraw/How-to-11fce2ed0be5480bb8e711c7ff1a0488?pvs=4#a66fae7bfcfe4ffe9d5348504598c6a0)

### Change Type
- [x] `internal` — Does not affect user-facing stuff
- [x] `chore` — Updating dependencies, other boring stuff
pull/3281/head
alex 2024-03-27 11:33:47 +00:00 zatwierdzone przez GitHub
rodzic d45d77bedf
commit 408a269114
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
6 zmienionych plików z 84 dodań i 45 usunięć

Wyświetl plik

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

Wyświetl plik

@ -26,4 +26,5 @@ export interface Environment {
TLDRAW_ENV: string | undefined
SENTRY_DSN: string | undefined
IS_LOCAL: string | undefined
WORKER_NAME: string | undefined
}

Wyświetl plik

@ -1,4 +1,4 @@
export { TLServer, type DBLoadResult } from './lib/TLServer'
export { TLServer, type DBLoadResult, type TLServerEvent } from './lib/TLServer'
export {
TLSyncClient,
type TLPersistentClientSocket,

Wyświetl plik

@ -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<R extends UnknownRecord> implements TLRoomSocket<R> {
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<R>) {
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()
}
}

Wyświetl plik

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

Wyświetl plik

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