Browse Source

add relay debug loggin

imwald
Silberengel 1 month ago
parent
commit
9872145f55
  1. 135
      src/services/client.service.ts

135
src/services/client.service.ts

@ -53,6 +53,21 @@ import { QueryService } from './client-query.service'
/** Live timeline REQ: dead relays fail fast; EOSE caps “connected but silent” relays. */ /** Live timeline REQ: dead relays fail fast; EOSE caps “connected but silent” relays. */
const SUBSCRIBE_RELAY_CONNECTION_TIMEOUT_MS = 2800 const SUBSCRIBE_RELAY_CONNECTION_TIMEOUT_MS = 2800
const SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS = 4800 const SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS = 4800
function summarizeFiltersForRelayLog(filters: Filter[]): Record<string, unknown> {
const f = filters[0]
if (!f) return {}
const out: Record<string, unknown> = {}
if (f.kinds != null) out.kinds = f.kinds
if (f.limit != null) out.limit = f.limit
if (f.authors?.length) out.authorCount = f.authors.length
if (f.ids?.length) out.idCount = f.ids.length
if (f.search) out.search = true
if (f['#p']?.length) out.pTagCount = f['#p'].length
if (f['#e']?.length) out.eTagCount = f['#e'].length
if (f['#t']?.length) out.tTagCount = f['#t'].length
return out
}
import { EventService } from './client-events.service' import { EventService } from './client-events.service'
import { ReplaceableEventService } from './client-replaceable-events.service' import { ReplaceableEventService } from './client-replaceable-events.service'
import { MacroService, createBookstrService } from './client-macro.service' import { MacroService, createBookstrService } from './client-macro.service'
@ -869,6 +884,14 @@ class ClientService extends EventTarget {
firstRelayResultGraceMs?: number firstRelayResultGraceMs?: number
} = {} } = {}
) { ) {
const timelineBatchId = `tl-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`
const timelineT0 = performance.now()
logger.info('[relay-req] timeline_batch_start', {
timelineBatchId,
subRequestCount: subRequests.length,
relayCounts: subRequests.map((r) => r.urls.length)
})
const newEventIdSet = new Set<string>() const newEventIdSet = new Set<string>()
const requestCount = subRequests.length const requestCount = subRequests.length
let eventIdSet = new Set<string>() let eventIdSet = new Set<string>()
@ -882,6 +905,30 @@ class ClientService extends EventTarget {
) )
) )
let firstPaintLogged = false
const deliverTimelineToConsumer = (snapshot: NEvent[], allEosed: boolean) => {
if (!firstPaintLogged && snapshot.length > 0) {
firstPaintLogged = true
logger.info('[relay-req] first_paint', {
timelineBatchId,
phase: 'data_to_feed',
rowCount: snapshot.length,
allEosed,
ms: Math.round(performance.now() - timelineT0)
})
if (typeof requestAnimationFrame === 'function') {
requestAnimationFrame(() => {
logger.info('[relay-req] first_paint', {
timelineBatchId,
phase: 'raf',
ms: Math.round(performance.now() - timelineT0)
})
})
}
}
onEvents(snapshot, allEosed)
}
let outerFlushQueued = false let outerFlushQueued = false
let outerFlushBump = 0 let outerFlushBump = 0
const scheduleOuterFlush = (immediate = false) => { const scheduleOuterFlush = (immediate = false) => {
@ -889,7 +936,7 @@ class ClientService extends EventTarget {
outerFlushQueued = false outerFlushQueued = false
const snapshot = events.length ? [...events] : [] const snapshot = events.length ? [...events] : []
const allEosed = eosedCount >= requestCount const allEosed = eosedCount >= requestCount
onEvents(snapshot, allEosed) deliverTimelineToConsumer(snapshot, allEosed)
} }
if (immediate || eosedCount >= requestCount || events.length <= 1) { if (immediate || eosedCount >= requestCount || events.length <= 1) {
outerFlushBump++ outerFlushBump++
@ -908,7 +955,7 @@ class ClientService extends EventTarget {
} }
const subs = await Promise.all( const subs = await Promise.all(
subRequests.map(({ urls, filter }) => { subRequests.map(({ urls, filter }, shardIndex) => {
return this._subscribeTimeline( return this._subscribeTimeline(
urls, urls,
filter, filter,
@ -937,7 +984,12 @@ class ClientService extends EventTarget {
}, },
onClose onClose
}, },
{ startLogin, needSort, firstRelayResultGraceMs } {
startLogin,
needSort,
firstRelayResultGraceMs,
relayReqLog: { groupId: `${timelineBatchId}:shard${shardIndex}` }
}
) )
}) })
) )
@ -1018,7 +1070,8 @@ class ClientService extends EventTarget {
onclose?: (url: string, reason: string) => void onclose?: (url: string, reason: string) => void
startLogin?: () => void startLogin?: () => void
onAllClose?: (reasons: string[]) => void onAllClose?: (reasons: string[]) => void
} },
relayReqLog?: { groupId?: string }
) { ) {
let relays = Array.from(new Set(urls)) let relays = Array.from(new Set(urls))
const filters = Array.isArray(filter) ? filter : [filter] const filters = Array.isArray(filter) ? filter : [filter]
@ -1053,17 +1106,60 @@ class ClientService extends EventTarget {
// Kind-1 queries drop KIND_1_BLOCKED_RELAY_URLS; if every URL was removed, no subs run and // Kind-1 queries drop KIND_1_BLOCKED_RELAY_URLS; if every URL was removed, no subs run and
// oneose would never fire — timelines stay loading forever (e.g. favorites feed). // oneose would never fire — timelines stay loading forever (e.g. favorites feed).
if (groupedRequests.length === 0) { if (groupedRequests.length === 0) {
logger.info('[relay-req] batch_skip', {
reason: 'no_relays_after_filters',
filterSummary: summarizeFiltersForRelayLog(filters)
})
queueMicrotask(() => oneose?.(true)) queueMicrotask(() => oneose?.(true))
return { return {
close: () => {} close: () => {}
} }
} }
const reqGroupId =
relayReqLog?.groupId ??
`sub-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`
const reqT0 = performance.now()
let firstRelayResponseLogged = false
/** When the first `first_response` was `eose` (no row yet), log once when the first EVENT arrives. */
let awaitingFirstEventAfterEoseFirstResponse = false
const logFirstRelayResponse = (kind: 'event' | 'eose', relayUrl: string) => {
if (firstRelayResponseLogged) return
firstRelayResponseLogged = true
if (kind === 'eose') awaitingFirstEventAfterEoseFirstResponse = true
logger.info('[relay-req] first_response', {
reqGroupId,
kind,
relayUrl,
ms: Math.round(performance.now() - reqT0)
})
}
const logFirstEventIfFirstResponseWasEmpty = (evt: NEvent, relayKey: string) => {
if (!awaitingFirstEventAfterEoseFirstResponse) return
awaitingFirstEventAfterEoseFirstResponse = false
logger.info('[relay-req] first_event', {
reqGroupId,
relayUrl: relayKey,
eventId: evt.id,
eventKind: evt.kind,
ms: Math.round(performance.now() - reqT0),
note: 'first_relay_response_was_eose_not_event'
})
}
logger.info('[relay-req] batch_start', {
reqGroupId,
relayCount: groupedRequests.length,
relays: groupedRequests.map((r) => r.url),
filterSummary: summarizeFiltersForRelayLog(filters)
})
const eosesReceived: boolean[] = [] const eosesReceived: boolean[] = []
const closesReceived: (string | undefined)[] = [] const closesReceived: (string | undefined)[] = []
const handleEose = (i: number) => { const handleEose = (i: number) => {
if (eosesReceived[i]) return if (eosesReceived[i]) return
eosesReceived[i] = true eosesReceived[i] = true
logFirstRelayResponse('eose', groupedRequests[i]!.url)
if (eosesReceived.filter(Boolean).length === groupedRequests.length) { if (eosesReceived.filter(Boolean).length === groupedRequests.length) {
oneose?.(true) oneose?.(true)
} }
@ -1110,7 +1206,11 @@ class ClientService extends EventTarget {
const sub = relay.subscribe(relayFilters, { const sub = relay.subscribe(relayFilters, {
receivedEvent: (_relay, id) => that.trackEventSeenOn(id, _relay), receivedEvent: (_relay, id) => that.trackEventSeenOn(id, _relay),
onevent: (evt: NEvent) => onevent?.(evt), onevent: (evt: NEvent) => {
logFirstEventIfFirstResponseWasEmpty(evt, relayKey)
logFirstRelayResponse('event', relayKey)
onevent?.(evt)
},
oneose: () => handleEose(i), oneose: () => handleEose(i),
onclose: (reason: string) => { onclose: (reason: string) => {
releaseOnce() releaseOnce()
@ -1145,7 +1245,11 @@ class ClientService extends EventTarget {
try { try {
const sub2 = liveRelay.subscribe(relayFilters, { const sub2 = liveRelay.subscribe(relayFilters, {
receivedEvent: (_relay, id) => that.trackEventSeenOn(id, _relay), receivedEvent: (_relay, id) => that.trackEventSeenOn(id, _relay),
onevent: (evt: NEvent) => onevent?.(evt), onevent: (evt: NEvent) => {
logFirstEventIfFirstResponseWasEmpty(evt, relayKey)
logFirstRelayResponse('event', relayKey)
onevent?.(evt)
},
oneose: () => handleEose(i), oneose: () => handleEose(i),
onclose: (reason2: string) => { onclose: (reason2: string) => {
releaseSlot2() releaseSlot2()
@ -1154,6 +1258,12 @@ class ClientService extends EventTarget {
alreadyHaveEvent: localAlreadyHaveEvent, alreadyHaveEvent: localAlreadyHaveEvent,
eoseTimeout: SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS eoseTimeout: SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS
}) })
logger.info('[relay-req] req_sent', {
reqGroupId,
url: relayKey,
ms: Math.round(performance.now() - reqT0),
note: 'after_auth'
})
subs.push({ subs.push({
relayKey, relayKey,
close: () => { close: () => {
@ -1179,6 +1289,11 @@ class ClientService extends EventTarget {
alreadyHaveEvent: localAlreadyHaveEvent, alreadyHaveEvent: localAlreadyHaveEvent,
eoseTimeout: SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS eoseTimeout: SUBSCRIBE_RELAY_EOSE_TIMEOUT_MS
}) })
logger.info('[relay-req] req_sent', {
reqGroupId,
url: relayKey,
ms: Math.round(performance.now() - reqT0)
})
subs.push({ subs.push({
relayKey, relayKey,
close: () => { close: () => {
@ -1230,11 +1345,14 @@ class ClientService extends EventTarget {
startLogin, startLogin,
needSort = true, needSort = true,
/** @deprecated No longer used; streaming does not fake EOSE (see flushStreamingSnapshot). Kept for call-site compatibility. */ /** @deprecated No longer used; streaming does not fake EOSE (see flushStreamingSnapshot). Kept for call-site compatibility. */
firstRelayResultGraceMs: _unusedFirstRelayGraceMs = FIRST_RELAY_RESULT_GRACE_MS firstRelayResultGraceMs: _unusedFirstRelayGraceMs = FIRST_RELAY_RESULT_GRACE_MS,
relayReqLog
}: { }: {
startLogin?: () => void startLogin?: () => void
needSort?: boolean needSort?: boolean
firstRelayResultGraceMs?: number firstRelayResultGraceMs?: number
/** Correlate {@link ClientService.subscribe} logs with a timeline shard */
relayReqLog?: { groupId: string }
} = {} } = {}
) { ) {
void _unusedFirstRelayGraceMs void _unusedFirstRelayGraceMs
@ -1366,7 +1484,8 @@ class ClientService extends EventTarget {
}, },
oneose: handleTimelineEose, oneose: handleTimelineEose,
onclose: onClose onclose: onClose
}) },
relayReqLog)
return { return {
timelineKey: key, timelineKey: key,

Loading…
Cancel
Save