1
Fork 0
mirror of https://github.com/Steffo99/todocolors.git synced 2024-11-22 08:14:18 +00:00

Fix websocket backoff mechanisms

This commit is contained in:
Steffo 2023-08-08 17:35:12 +02:00
parent 550b2e6c51
commit 5982352792
Signed by: steffo
GPG key ID: 2A24051445686895
10 changed files with 64 additions and 43 deletions

View file

@ -0,0 +1,26 @@
.rootMain {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.rootMain h2 {
margin-top: 0;
}
.rootMain > :global(.chapter-2) {
max-width: 976px;
}
.rootMain small {
font-style: italic;
}
.rootMain :global(.panel) {
max-width: 484px;
padding-left: 12px;
padding-right: 12px;
}

View file

@ -1,12 +1,12 @@
import {CreateBoardChapter} from "@/app/[lang]/(page)/CreateBoardChapter"
import {ExistingBoardChapter} from "@/app/[lang]/(page)/ExistingBoardChapter"
import style from "@/app/[lang]/page.module.css"
import style from "./RootMain.module.css"
import {default as React} from "react"
export async function RootMain({lng}: {lng: string}) {
return (
<main className={style.pageMain}>
<main className={style.rootMain}>
<CreateBoardChapter lng={lng}/>
<ExistingBoardChapter lng={lng}/>
</main>

View file

@ -6,7 +6,7 @@ import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
export function BoardMain({className}: {className?: string}) {
const {webSocketState} = useManagedBoard()
const {webSocketState, webSocketBackoffMs} = useManagedBoard()
switch(webSocketState) {
case undefined:
@ -18,6 +18,6 @@ export function BoardMain({className}: {className?: string}) {
case WebSocket.CLOSING:
return <BoardMainIcon icon={<FontAwesomeIcon size={"4x"} icon={faLinkSlash} beatFade/>} text={"Disconnessione..."} className={className}/>
case WebSocket.CLOSED:
return <BoardMainIcon icon={<FontAwesomeIcon size={"4x"} icon={faLinkSlash}/>} text={"Disconnesso"} className={className}/>
return <BoardMainIcon icon={<FontAwesomeIcon size={"4x"} icon={faLinkSlash}/>} text={`Disconnesso, riconnessione tra ${webSocketBackoffMs}ms`} className={className}/>
}
}

View file

@ -16,6 +16,7 @@ export interface UseBoardReturns {
tasksById: {[id: string]: Task},
taskGroups: TaskGroup[],
webSocketState: number | undefined,
webSocketBackoffMs: number,
isEditingTitle: boolean,
stopEditingTitle: () => void,
startEditingTitle: () => void,
@ -38,7 +39,7 @@ export interface UseBoardReturns {
}
export function useBoard(name: string): UseBoardReturns {
const {state: {title, tasksById}, sendAction, webSocketState} = useBoardWs(name);
const {state: {title, tasksById}, sendAction, webSocketState, webSocketBackoffMs} = useBoardWs(name);
const {value: [taskGrouper, groupSorter, groupNamer], move: moveGrouper, next: nextGrouper, previous: previousGrouper} = useCycleState(TASK_GROUPERS);
const {value: taskSorter, move: moveSorter, next: nextSorter, previous: previousSorter} = useCycleState(TASK_SORTERS);
@ -56,6 +57,7 @@ export function useBoard(name: string): UseBoardReturns {
tasksById,
taskGroups,
webSocketState,
webSocketBackoffMs,
isEditingTitle,
stopEditingTitle,
startEditingTitle,

View file

@ -13,7 +13,7 @@ export function useBoardWs(name: string) {
const {state, act} = useBoardState();
const {webSocket, webSocketState} = useWs(wsFullURL, {
const {webSocket, webSocketState, webSocketBackoffMs} = useWs(wsFullURL, {
onopen: useCallback(({}) => {
console.debug("[useBoardWs] Connected to board:", name);
act(null);
@ -27,9 +27,8 @@ export function useBoardWs(name: string) {
console.error("[useBoardWs] Encountered a WebSocket error, closing current connection:", event);
closeWebSocket()
}, []),
onclose: useCallback(({event, openWebSocket}: WebSocketHandlerParams<Event>) => {
console.debug("[useBoardWs] WebSocket was closed, trying to reconnect:", event);
openWebSocket()
onclose: useCallback(({event}: WebSocketHandlerParams<Event>) => {
console.debug("[useBoardWs] WebSocket was closed:", event);
}, [])
});
@ -42,5 +41,5 @@ export function useBoardWs(name: string) {
webSocket.send(JSON.stringify(data));
}, [webSocket, webSocketState])
return {state, sendAction, webSocketState}
return {state, sendAction, webSocketState, webSocketBackoffMs}
}

View file

@ -1,7 +1,7 @@
// noinspection JSUnusedGlobalSymbols
import "./layout.css";
import {Body} from "@/app/[lang]/Body"
import {Body} from "@/app/[lang]/(layout)/Body"
import {StarredManager} from "@/app/[lang]/StarContext"
import type {Metadata as NextMetadata} from "next"
import {default as React, ReactNode} from "react"

View file

@ -9,11 +9,3 @@
padding: 8px;
}
.pageMain h2 {
margin-top: 0;
}
.pageMain div {
max-width: 960px;
}

View file

@ -4,7 +4,7 @@ import {useState, useCallback, useEffect} from "react"
export interface WebSocketHandlerParams<E extends Event> {
event: E,
openWebSocket: () => void,
openWebSocketAfterBackoff: () => void,
closeWebSocket: () => void,
}
@ -18,6 +18,7 @@ export interface WebSocketHandlers {
export function useWs(url: string | undefined, {onclose, onerror, onmessage, onopen}: WebSocketHandlers) {
const [webSocket, setWebSocket] = useState<WebSocket | undefined>(undefined)
const [webSocketState, setWebSocketState] = useState<number | undefined>(undefined);
const [isBackingOff, setBackingOff] = useState<boolean>(false);
const [webSocketBackoffMs, setWebSocketBackoffMs] = useState<number>(1);
const closeWebSocket = useCallback(() => {
@ -36,56 +37,57 @@ export function useWs(url: string | undefined, {onclose, onerror, onmessage, ono
setWebSocketState(WebSocket.CLOSED);
}, [webSocket])
const backOff = useCallback((func: () => void) => async () => {
// This WILL cause no-ops, but they're going to be pretty infrequent, so idc
console.debug("[useWebSocket] Backing off for:", webSocketBackoffMs, "ms")
setBackingOff(true)
await new Promise(resolve => setTimeout(resolve, webSocketBackoffMs))
setBackingOff(false)
func()
}, [webSocketBackoffMs])
const openWebSocket = useCallback(() => {
console.debug("[useWebSocket] Opening WebSocket:", url);
if(url === undefined) {
console.warn("[useWebSocket] Trying to open WebSocket, but no URL has been given; ignoring request...")
return;
}
setWebSocketBackoffMs(prev => prev * 2); // Workaround for connections that get closed immediately by the server
setWebSocketState(WebSocket.CONNECTING); // Workaround for constructor blocking and giving no feedback to the user
const sock = new WebSocket(url);
const openWebSocketAfterBackoff = backOff(openWebSocket);
sock.onopen = (event) => {
console.debug("[useWebSocket] Opened connection:", event)
setWebSocket(sock)
setWebSocketState(sock.readyState)
setWebSocketBackoffMs(1)
onopen?.({event, openWebSocket, closeWebSocket});
onopen?.({event, openWebSocketAfterBackoff, closeWebSocket});
}
sock.onclose = (event) => {
console.debug("[useWebSocket] Closed connection:", event)
setWebSocket(undefined)
setWebSocketState(sock.readyState);
onclose?.({event, openWebSocket, closeWebSocket});
onclose?.({event, openWebSocketAfterBackoff, closeWebSocket});
}
sock.onerror = (event) => {
console.error("[useWebSocket] Error in connection:", event)
setWebSocketState(sock.readyState)
setWebSocketBackoffMs(prev => prev * 2)
onerror?.({event, openWebSocket, closeWebSocket});
onerror?.({event, openWebSocketAfterBackoff, closeWebSocket});
}
sock.onmessage = (event) => {
console.debug("[useWebSocket] Received message:", event)
onmessage?.({event, openWebSocket, closeWebSocket});
onmessage?.({event, openWebSocketAfterBackoff, closeWebSocket});
}
}, [url, onopen, onclose, onerror, onmessage])
// @ts-ignore
const openWebSocketAfterBackoff = useCallback(() => {
console.debug("[useWebSocket] Backing off for:", webSocketBackoffMs, "ms")
return setTimeout(openWebSocket, webSocketBackoffMs)
}, [openWebSocket, webSocketBackoffMs])
}, [url, onopen, onclose, onerror, onmessage, backOff, closeWebSocket])
useEffect(() => {
if(!url) {
return;
}
if(!url) return;
if(webSocket !== undefined) return;
if(isBackingOff) return;
console.debug("[useWebSocket] Hook mounted, opening connection as soon as possible...")
// @ts-ignore
const handle = openWebSocketAfterBackoff()
return () => {
clearTimeout(handle)
}
}, [url, openWebSocketAfterBackoff])
const openWebSocketAfterBackoff = backOff(openWebSocket);
// noinspection JSIgnoredPromiseFromCall
openWebSocketAfterBackoff()
}, [url, isBackingOff, webSocketState])
return {webSocket, webSocketState, openWebSocket, closeWebSocket}
return {webSocket, webSocketState, webSocketBackoffMs}
}