fix(relay): hold a still-measured incumbent through an incomplete catalog

An expiry that lands during the other region's roll wave saw a one-region catalog and
withheld the hint, sending a correctly homed desktop to the director default for an hour.
The incumbent was earned against a full catalog, so it is kept; a lone survivor that is
not the incumbent is still never promoted.
This commit is contained in:
Jinwoo-H
2026-09-07 15:45:14 -04:00
parent 2c2fa3d25c
commit 43d31d4b25
2 changed files with 51 additions and 4 deletions
@@ -384,6 +384,47 @@ describe('Relay region preference', () => {
])
})
it('holds a still-measured incumbent through an incomplete catalog', async () => {
// Why: an Asia desktop whose 24 h hint expires during a US roll wave sees a
// one-region catalog. Withholding the hint would place it at the director
// default for an hour; the incumbent was earned against a full catalog.
const path = userDataPath()
writeCache(path, 'asia-east2')
const events: unknown[] = []
const healthy = sampledProbe({ [ASIA]: [90, 30, 32, 34] })
await expect(
new RelayRegionPreferenceResolver({
directorUrl: DIRECTOR,
userDataPath: path,
fetch: catalogFetch([{ region: 'asia-east2', probeOrigins: [ASIA] }]),
probe: healthy.probe,
now: () => 1_000,
logEvent: (event) => events.push(event)
}).resolve()
).resolves.toBe('asia-east2')
expect(JSON.parse(readFileSync(cachePath(path), 'utf8'))).toMatchObject({
region: 'asia-east2',
expiresAt: 1_000 + 24 * 60 * 60_000
})
expect(events).toEqual([expect.objectContaining({ chosenRegion: 'asia-east2' })])
})
it('does not promote a lone survivor that is not the incumbent', async () => {
const path = userDataPath()
writeCache(path, 'us-central1')
const healthy = sampledProbe({ [ASIA]: [90, 30, 32, 34] })
await expect(
new RelayRegionPreferenceResolver({
directorUrl: DIRECTOR,
userDataPath: path,
fetch: catalogFetch([{ region: 'asia-east2', probeOrigins: [ASIA] }]),
probe: healthy.probe,
now: () => 1_000
}).resolve()
).resolves.toBeUndefined()
expect(JSON.parse(readFileSync(cachePath(path), 'utf8'))).toMatchObject({ region: null })
})
it('uses a valid diagnostic override without network or cache mutation', async () => {
const path = userDataPath()
const fetch = vi.fn<typeof globalThis.fetch>()
@@ -183,13 +183,19 @@ export class RelayRegionPreferenceResolver {
this.log(relayRegionCatalogFailureEvent(this.options.directorUrl))
)
const measurements = measuredRegions(reports)
const previousRegion = previous?.region ?? null
// Why: a region may only win against a measured competitor. An unmeasured
// peer, or one the catalog dropped for having no general cell (a roll wave
// or heartbeat stall), means director default placement beats a lone survivor.
const selected =
measurements.length < reports.length || reports.length < RELAY_REGIONS.length
? null
: selectRegionMeasurement(measurements, previous?.region ?? null)
// The exception is the incumbent: a hint that was earned against a full
// catalog and still measures is held, not discarded, or an expiry that lands
// during the other region's roll wave would send the desktop to the default.
const complete =
measurements.length === reports.length && reports.length === RELAY_REGIONS.length
const incumbent = measurements.find((measurement) => measurement.region === previousRegion)
const selected = complete
? selectRegionMeasurement(measurements, previousRegion)
: (incumbent ?? null)
const ttlMs = selected ? CACHE_TTL_MS : NO_HINT_TTL_MS
this.log(
relayRegionRefreshEvent({