Make Plex polling faster and its errors specific

Servers advertise many addresses (public, relay, LAN, Docker bridges).
Use the last one that worked and race the rest only if it fails. Friends'
servers that keep failing back off exponentially instead of slowing
every poll. Errors now name each server and the underlying cause, e.g.
"blackcoffee: ECONNREFUSED", instead of undici's generic "fetch failed".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
2026-10-03 16:02:38 -04:00
co-authored by Claude Opus 5.5
parent 60e6a89297
commit cb3e3d11a9
2 changed files with 112 additions and 35 deletions
+35 -2
View File
@@ -160,7 +160,7 @@ describe('OAuth return URLs', () => {
});
describe('Plex polling', () => {
async function plexConnection(uris: string[]) {
async function plexConnection(uris: string[], extraServers: object[] = []) {
const { user } = await createUser(app(), 'alice');
return upsertConnection({
userId: user.id,
@@ -168,9 +168,14 @@ describe('Plex polling', () => {
externalUserId: '12345',
displayName: null,
accessTokenEnc: encrypt('account-token'),
meta: { discoveredAt: Date.now(), servers: [{ name: 'Home', owned: true, tokenEnc: encrypt('server-token'), uris }] },
meta: {
discoveredAt: Date.now(),
servers: [{ name: 'Home', owned: true, tokenEnc: encrypt('server-token'), uris }, ...extraServers],
},
});
}
const friendServer = (name: string, uri: string) => ({ name, owned: false, tokenEnc: encrypt('t'), uris: [uri] });
const refused = () => Promise.reject(Object.assign(new TypeError('fetch failed'), { cause: { code: 'ECONNREFUSED' } }));
const sessions = {
MediaContainer: {
@@ -208,4 +213,32 @@ describe('Plex polling', () => {
await expect(plexProvider.poll(conn)).rejects.toThrow(/private network/);
expect(fetchMock).not.toHaveBeenCalled();
});
it("backs off a friend's server that keeps failing, so it stops slowing every poll", async () => {
let friendCalls = 0;
vi.stubGlobal(
'fetch',
routeFetch([
[/^https:\/\/8\.8\.8\.8/, () => json(sessions)],
[/^https:\/\/9\.9\.9\.9/, () => (friendCalls++, refused())],
]),
);
const conn = await plexConnection(['https://8.8.8.8:32400'], [friendServer('Friend', 'https://9.9.9.9:32400')]);
expect((await plexProvider.poll(conn))?.track.title).toBe('Digital Love');
const [row] = await db.select().from(schema.connections).where(eq(schema.connections.id, conn.id));
await plexProvider.poll(row!);
expect(friendCalls).toBe(1); // skipped on the second poll
});
it("reports the owner's server being down even when friends' servers answer", async () => {
vi.stubGlobal(
'fetch',
routeFetch([
[/^https:\/\/8\.8\.8\.8/, () => refused()],
[/^https:\/\/9\.9\.9\.9/, () => json({ MediaContainer: {} })],
]),
);
const conn = await plexConnection(['https://8.8.8.8:32400'], [friendServer('Friend', 'https://9.9.9.9:32400')]);
await expect(plexProvider.poll(conn)).rejects.toThrow("Couldn't reach your Plex server (Home: ECONNREFUSED)");
});
});