Make Websocket Server and Poller to notify state change based on events
Currently the WebSocket message sending process works like this (for every connection):
WebSocket:
- Sends initial global state to client;
- Checks if state changed; if true it tries to send the new state for all connected clients;
- Waits for 200ms or until client sends something;
- Repeats into step 2, until client disconnects or an error is registered.
State Generation:
- On first client message (Requested from WebSocket thread): Always generate state;
- On subsequent messages (Requested from WebSocket thread): Generates state and check whether state matches or not; if not, a None is returned;
Using this strategy has the following drawbacks:
- Makes the state detection sensible, as WebSocketPoller.get_state_if_changed documentation states;
- Has a potential to increase CPU usage as the number of connected clients increase, as the state is queried every 200ms;
- Generates the state every 200ms;
- Makes hard to implement new messages to WebSocket.
With this merge request, the process happens as following:
WebSocket:
- Sends initial global state to client;
-
Awaits up to 1 minute for a state change using a Queue (per-client queue), it runs the cleanup process if the client disconnected in meantimeAwaits for a state change or client disconnection, what happens first; - If there's a state change, sends the received state change; if there's a disconnection, it proceeds to connection cleanup;
- Repeats into step 2, until client disconnects or an error is registered.
State Generation:
- On first client message (Requested from WebSocket thread): Use stored last state;
- On subsequent messages (Requested from WebSocket thread): Use stored last state;
- On every relevant change in LiveController and ServiceManager, a message to regenerate state is sent to WebSocketPoller;
- A message is sent to WebSocketServer after the state is regenerated. WebSocketServer calls a function inside WebSocketWorker to push message to each client's Message Queue
- The message is received on client loop (in WebSocket thread) and sent to client, after that it waits for the next message.
This new strategy has the following drawback:
- There is an extra event listener inside LiveController and ServiceManager for every mutatble operation of them;
But has the following benefits:
- Makes the state change notification faster (as messages are sent as soon as possible, instead of waiting 200ms for next check);
- Allows future insertion of WebSocket messages from plugins (it would be dangerous to allow custom messages on WebSocket when using the polling strategy, due to the 200ms deadline);
These are notable changes/additions inside this merge request:
-
servicemanager_changed
(servicemanager.py
) andslide_controller_changed
(slidecontroller.py
) signals; -
websocketspoll.py
'sWebSocketPoller
now listens to signals coming from ServiceManager and LiveController; -
websockets.py
's WebSocket connection handling functions are inside ofWebSocketWorker
; -
WebSocketWorker
uses per-clientasyncio
's Queue to queue messages for each connected client; -
WebSocketServer
initialization is moved to astart()
function, to make unit test easier; -
WebSocketServer
now has aclose()
method that is called on application exit. Used to unhook signals
Open questions:
-
For each client, it waits up to 1 minute for changes on client's queue; that allows the allocated resources for this client (for now the queue and the USERS' entry) to be released from memory. Is 1 minute a good default or it should be changed?Now it cleans instantly on client disconnect.
Edited by Mateus Meyer Jiacomelli