mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 01:32:40 +00:00
fix(app): fall back to relay when the direct connection drops (#7616)
The probe cycle reused the selected client's last heartbeat RTT even after that client disconnected. A dead direct connection kept reporting as available, so the app never switched to a reachable relay until it was restarted. Mark the selected connection unavailable while its client is not connected and let the existing failover pick another connection. Co-authored-by: 3ae3ae <ogyshjsm@naver.com>
This commit is contained in:
@@ -1011,6 +1011,50 @@ describe("HostRuntimeController", () => {
|
||||
expect(activeClient.latencyMeasurements()).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([12, null])(
|
||||
"falls back to relay when the direct client disconnects with heartbeat RTT %s",
|
||||
async (heartbeatRtt) => {
|
||||
useHostRuntimeClock();
|
||||
const clients: FakeDaemonClient[] = [];
|
||||
const latencies: Record<string, number | Error> = {
|
||||
"direct:lan:6767": 12,
|
||||
"relay:relay.paseo.sh:443": 65,
|
||||
};
|
||||
const controller = new HostRuntimeController({
|
||||
host: makeHost(),
|
||||
deps: makeDeps(latencies, clients),
|
||||
});
|
||||
|
||||
await controller.start({ autoProbe: false });
|
||||
expect(controller.getSnapshot().activeConnectionId).toBe("direct:lan:6767");
|
||||
expect(controller.getSnapshot().connectionStatus).toBe("online");
|
||||
|
||||
const directClient = clients[0];
|
||||
directClient.heartbeatReportsRtt(heartbeatRtt);
|
||||
directClient.clearLatencyMeasurements();
|
||||
latencies["direct:lan:6767"] = new Error("LAN unreachable");
|
||||
directClient.setConnectionState({ status: "disconnected", reason: "network changed" });
|
||||
expect(controller.getSnapshot().connectionStatus).toBe("error");
|
||||
expect(directClient.getLastLivenessRttMs()).toBe(heartbeatRtt);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(2_000);
|
||||
await controller.runProbeCycleNow();
|
||||
|
||||
const snapshot = controller.getSnapshot();
|
||||
expect(snapshot.activeConnectionId).toBe("relay:relay.paseo.sh:443");
|
||||
expect(snapshot.connectionStatus).toBe("online");
|
||||
expect(snapshot.probeByConnectionId).toEqual(
|
||||
new Map([
|
||||
["direct:lan:6767", { status: "unavailable", latencyMs: null }],
|
||||
["relay:relay.paseo.sh:443", { status: "available", latencyMs: 65 }],
|
||||
]),
|
||||
);
|
||||
expect(directClient.isDisposed()).toBe(true);
|
||||
expect(directClient.latencyMeasurements()).toEqual([]);
|
||||
await controller.stop();
|
||||
},
|
||||
);
|
||||
|
||||
it("does not create a probe client for the selected connection while it reconnects", async () => {
|
||||
useHostRuntimeClock();
|
||||
const relay: HostConnection = {
|
||||
|
||||
@@ -1110,6 +1110,14 @@ export class HostRuntimeController {
|
||||
shouldCloseClient = shouldCloseClient && !activated;
|
||||
|
||||
if (activeClient) {
|
||||
if (activeClient.getConnectionState().status !== "connected") {
|
||||
probeByConnectionId.set(connection.id, {
|
||||
status: "unavailable",
|
||||
latencyMs: null,
|
||||
});
|
||||
publishProbeState();
|
||||
return;
|
||||
}
|
||||
const rttMs = activeClient.getLastLivenessRttMs();
|
||||
if (!this.isCurrentProbeRequest(requestVersion)) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user