Skip to content

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:

  1. Sends initial global state to client;
  2. Checks if state changed; if true it tries to send the new state for all connected clients;
  3. Waits for 200ms or until client sends something;
  4. 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:

  1. Sends initial global state to client;
  2. 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 meantime Awaits for a state change or client disconnection, what happens first;
  3. If there's a state change, sends the received state change; if there's a disconnection, it proceeds to connection cleanup;
  4. 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) and slide_controller_changed (slidecontroller.py) signals;
  • websocketspoll.py's WebSocketPoller now listens to signals coming from ServiceManager and LiveController;
  • websockets.py's WebSocket connection handling functions are inside of WebSocketWorker;
  • WebSocketWorker uses per-client asyncio's Queue to queue messages for each connected client;
  • WebSocketServer initialization is moved to a start() function, to make unit test easier;
  • WebSocketServer now has a close() 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

Merge request reports