mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
Use header-only usage server timing
This commit is contained in:
@@ -1,15 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { calculateServerClockOffsetMs, useServerClock } from '../useServerClock'
|
||||
import {
|
||||
calculateServerClockOffsetMs,
|
||||
shouldUseServerClockSample,
|
||||
useServerClock
|
||||
} from '../useServerClock'
|
||||
|
||||
describe('useServerClock', () => {
|
||||
it('calculates offset from the request midpoint', () => {
|
||||
it('calculates offset from the response receive time', () => {
|
||||
const offset = calculateServerClockOffsetMs({
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_200,
|
||||
round_trip_ms: 200,
|
||||
})
|
||||
|
||||
expect(offset).toBe(-9_600)
|
||||
expect(offset).toBe(-9_700)
|
||||
})
|
||||
|
||||
it('ignores missing or invalid timing samples', () => {
|
||||
@@ -18,11 +23,19 @@ describe('useServerClock', () => {
|
||||
server_now_unix_ms: Number.NaN,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_200,
|
||||
round_trip_ms: 200,
|
||||
})).toBeNull()
|
||||
expect(calculateServerClockOffsetMs({
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_200,
|
||||
client_receive_unix_ms: 20_000,
|
||||
round_trip_ms: 200,
|
||||
})).toBeNull()
|
||||
expect(calculateServerClockOffsetMs({
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_200,
|
||||
round_trip_ms: Number.NaN,
|
||||
})).toBeNull()
|
||||
})
|
||||
|
||||
@@ -33,10 +46,57 @@ describe('useServerClock', () => {
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_200,
|
||||
round_trip_ms: 200,
|
||||
})
|
||||
clock.updateServerClockOffset(undefined)
|
||||
|
||||
expect(clock.hasServerClockOffset.value).toBe(true)
|
||||
expect(clock.serverClockOffsetMs.value).toBe(-9_600)
|
||||
expect(clock.serverClockOffsetMs.value).toBe(-9_700)
|
||||
expect(clock.serverClockSampleRoundTripMs.value).toBe(200)
|
||||
})
|
||||
|
||||
it('does not let a much slower sample overwrite a better clock offset', () => {
|
||||
const clock = useServerClock()
|
||||
|
||||
clock.updateServerClockOffset({
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_050,
|
||||
round_trip_ms: 50,
|
||||
})
|
||||
clock.updateServerClockOffset({
|
||||
server_now_unix_ms: 20_500,
|
||||
client_send_unix_ms: 30_000,
|
||||
client_receive_unix_ms: 30_500,
|
||||
round_trip_ms: 500,
|
||||
})
|
||||
|
||||
expect(clock.serverClockOffsetMs.value).toBe(-9_550)
|
||||
expect(clock.serverClockSampleRoundTripMs.value).toBe(50)
|
||||
})
|
||||
|
||||
it('accepts a faster sample after an initial slow sample', () => {
|
||||
const clock = useServerClock()
|
||||
|
||||
clock.updateServerClockOffset({
|
||||
server_now_unix_ms: 10_500,
|
||||
client_send_unix_ms: 20_000,
|
||||
client_receive_unix_ms: 20_500,
|
||||
round_trip_ms: 500,
|
||||
})
|
||||
clock.updateServerClockOffset({
|
||||
server_now_unix_ms: 20_500,
|
||||
client_send_unix_ms: 30_000,
|
||||
client_receive_unix_ms: 30_050,
|
||||
round_trip_ms: 50,
|
||||
})
|
||||
|
||||
expect(clock.serverClockOffsetMs.value).toBe(-9_550)
|
||||
expect(clock.serverClockSampleRoundTripMs.value).toBe(50)
|
||||
})
|
||||
|
||||
it('allows small RTT regressions so the offset can stay fresh', () => {
|
||||
expect(shouldUseServerClockSample(140, 50)).toBe(true)
|
||||
expect(shouldUseServerClockSample(151, 50)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,36 +1,62 @@
|
||||
import { ref } from 'vue'
|
||||
import type { ServerTimingMetadata } from '@/api/serverTiming'
|
||||
|
||||
const SERVER_CLOCK_RTT_REGRESSION_TOLERANCE_MS = 100
|
||||
|
||||
export function calculateServerClockOffsetMs(timing: ServerTimingMetadata | null | undefined): number | null {
|
||||
if (!timing) return null
|
||||
const { server_now_unix_ms: serverNowUnixMs, client_send_unix_ms: clientSendUnixMs, client_receive_unix_ms: clientReceiveUnixMs } = timing
|
||||
const {
|
||||
server_now_unix_ms: serverNowUnixMs,
|
||||
client_send_unix_ms: clientSendUnixMs,
|
||||
client_receive_unix_ms: clientReceiveUnixMs,
|
||||
round_trip_ms: roundTripMs,
|
||||
} = timing
|
||||
|
||||
if (!Number.isFinite(serverNowUnixMs) || !Number.isFinite(clientSendUnixMs) || !Number.isFinite(clientReceiveUnixMs)) {
|
||||
if (
|
||||
!Number.isFinite(serverNowUnixMs) ||
|
||||
!Number.isFinite(clientSendUnixMs) ||
|
||||
!Number.isFinite(clientReceiveUnixMs) ||
|
||||
!Number.isFinite(roundTripMs)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
if (clientReceiveUnixMs < clientSendUnixMs) {
|
||||
if (clientReceiveUnixMs < clientSendUnixMs || roundTripMs < 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const clientMidpointUnixMs = clientSendUnixMs + ((clientReceiveUnixMs - clientSendUnixMs) / 2)
|
||||
return serverNowUnixMs - clientMidpointUnixMs
|
||||
return serverNowUnixMs - clientReceiveUnixMs
|
||||
}
|
||||
|
||||
export function shouldUseServerClockSample(
|
||||
nextRoundTripMs: number,
|
||||
currentRoundTripMs: number | null | undefined
|
||||
): boolean {
|
||||
if (!Number.isFinite(nextRoundTripMs) || nextRoundTripMs < 0) return false
|
||||
if (currentRoundTripMs == null || !Number.isFinite(currentRoundTripMs)) return true
|
||||
return nextRoundTripMs <= currentRoundTripMs + SERVER_CLOCK_RTT_REGRESSION_TOLERANCE_MS
|
||||
}
|
||||
|
||||
export function useServerClock() {
|
||||
const serverClockOffsetMs = ref(0)
|
||||
const hasServerClockOffset = ref(false)
|
||||
const serverClockSampleRoundTripMs = ref<number | null>(null)
|
||||
|
||||
function updateServerClockOffset(timing: ServerTimingMetadata | null | undefined): void {
|
||||
const offsetMs = calculateServerClockOffsetMs(timing)
|
||||
if (offsetMs == null) return
|
||||
if (!shouldUseServerClockSample(timing?.round_trip_ms ?? Number.NaN, serverClockSampleRoundTripMs.value)) {
|
||||
return
|
||||
}
|
||||
|
||||
serverClockOffsetMs.value = offsetMs
|
||||
serverClockSampleRoundTripMs.value = timing?.round_trip_ms ?? null
|
||||
hasServerClockOffset.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
serverClockOffsetMs,
|
||||
hasServerClockOffset,
|
||||
serverClockSampleRoundTripMs,
|
||||
updateServerClockOffset,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user