-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Access] Draft design of new WebSockets #6508
Comments
Looks great @Guitarheroua. A few comments:
|
@peterargue What is described here is the Web Application Messaging Protocol, or simply WAMP Protocol, which has a few implementations in Go. The most popular one is NEXUS, which implements the WAMP protocol and includes the features we need. It's also actively maintained, with a new version released this year. While it seems like a good fit for our requirements, we should first discuss the pros and cons of using it. My main concern is the subscription model on the client side. To me, it adds an extra layer of complexity, and clients might not be happy with that. |
We agreed that the Nexus library offers many useful features, but it also includes a lot of unnecessary functionality that we won't use. Additionally, the WAMP protocol implemented by this library adds an extra layer of complexity, particularly on the client side, making it more challenging to handle. |
Thanks for the updates. I think we can consolidate the endpoints a bit. With grpc, we get some input validation for free. Since we don't get that with websockets, I think it makes sense to group the endpoints and add explicit argument checks, similar to the rest endpoints. e.g.
each have optional args For What does the Endpoint to What will the endpoint logic look like? Will it be similar to the existing events endpoint where it simply passes the backend subscription object to the |
I don't think we'll want this on all topics, so I don't think it's worth the complexity of adding it for only a subset. It's not harmful to have duplicate streams, and may limit some usecases. Best to use a combination of max subscriptions per connection, max responses per second, and send timeouts.
I think a client should only need the subscription ID to unsubscribe |
Looks good @Guitarheroua. |
User Story: WebSocket Subscription Management
Connection Establishment:
The client establishes a single WebSocket connection with the Access Node (AN), e.g., via
ws://localhost:8080/ws
. This connection is maintained until closed by either the AN or the client.Subscription Mechanism:
The client sends a subscription request through the WebSocket to subscribe to topics. The AN responds with either success or failure. Upon success, a unique subscription ID is generated:
The client can subscribe to multiple topics through the same connection, managing the messages received accordingly.
If needed, the client can pass initial parameters via the
arguments
field:Updates from the AN are received as follows:
Unsubscription:
To unsubscribe the client sends the following message:
List Active Subscriptions:
The client can request the list of active subscriptions:
Closing the Connection:
The client can close the connection manually, or the AN may do so. When the connection closes, all subscriptions are lost.
Access Node Implementation Requirements
WebSocketController Requirements
The new
WebSocketController
is similar to the existingWebSocketController
but includes several key improvements:Connection Management:
The new controller should establish the WebSocket connection during construction but avoid subscribing to topics immediately. It will handle ping/pong messages for connection tracking and error management.
Messages Responce
The response messages for the client should have the following format:
The
list_subscriptions
action`s response will be different and should have the following format:Message Handling: The new controller listens for incoming client messages and processes actions like
subscribe
,unsubscribe
, andlist_subscriptions
. Supported topics include:Handler Creation:
For each new subscription, the new controller creates a
DataProvider
specific to the topic. The handler formats and sends the appropriate response to the client, using the correct topic and data.Unsubscription:
The new controller should allow unsubscribing by subscription ID.
List Subscriptions:
The new controller should return a list of all active subscriptions for the client upon request.
Limitations
The new controller should implement limits on the maximum number of subscriptions per connection, the maximum number of responses per second, and the send timeout.
Connection Handling:
The new controller should manage connectivity by handling ping/pong messages and unsubscriptions. If the client fails to respond to ping/pong messages, the new controller should gracefully close the connection and clean up all associated subscriptions.
A visual representation of the new REST subscription process:
New Pub/Sub API Description
1. The
router.go
A new
AddWsPubSubRoute
function will configure the route for the new subscription mechanism, using a distinct address such asv1/ws
, separate from the currentv1/subscribe_events
route. There will be one main route and one handler for the pub/sub mechanism. Different topics (akin to REST routes) will be handled by the newWebSocketController
, which reacts to messages from the client.2. The
WebSocketController
The
WebSocketController
manages subscriptions within a single connection between the client and the node.The
conn
field represents the WebSocket connection for bidirectional communication with the client. It handles incoming messages from the client and broadcasts messages back to the client based on subscribed topics. Additionally, it manages ping/pong messages, error handling, and connectivity issues.The methods associated with the
conn
field include:readMessages
:This method runs while the connection is active. It retrieves, validates, and processes client messages. Actions handled include
subscribe
,unsubscribe
, andlist_subscriptions
. Additional actions can be added as needed.writeMessages
:This method runs while the connection is active, listening on the
broadcast
channel. It retrieves responses and sends them to the client.broadcastMessage
:This method will be called by each
DataProvider
, who will receive formatted subscription messages and write them to thebroadcast
channel.pingPongHandler
:This method periodically checks connection availability using ping/pong messages and will terminate the connection if the client becomes unresponsive.
The methods associated with the
subs
field include:subscribe
:Triggered by the
readMessages
method when theaction
issubscribe
. It takes the topic from the message’stopic
field, creates the appropriateDataProvider
for the topic using a factory functionCreateSubscription
, and adds an instance of the new handler to thesubs
map. The client receives a notification confirming the successful subscription along with the specific ID.unsubscribe
:It is triggered by the
readMessages
method when theaction
isunsubscribe
. It removes the relevant handler from thesubs
map by callingDataProvider::Close
and notifying the client of successful unsubscription.listSubscriptions
:It is triggered by the
readMessages
method when theaction
islist_subscriptions
. It gathers all active subscriptions for the current connection, formats the response, and sends it back to the client.3. The
DataProvider
The
DataProvider
interface abstracts the actual subscriptions used by theWebSocketController
. ConcreteDataProvider
implementations will be created during theWebSocketController::subscribe
call, depending on thetopic
provided by the client. For example, the topicevents
will have anEventsDataProvider
implementation managing event subscriptions.New[Concrete]DataProvider
:Each constructor function takes a
topic
, thearguments
from the client’s message, and thebroadcastMessage
callback function. It stores these values and creates the correspondingsubscription.Subscription
on the backend. Each subscription is unique, identified by an ID fromsubscription.Subscription
and linked to an individual instance ofDataProvider
. This ensures the client can subscribe to the same topic multiple times with different parameters.messagesHandler
:Each handler includes a method that processes messages received from the backend and formats them for the client. This formatted message is passed to the new web socket for further processing by calling the
broadcastMessage
callback.Close
:The method gracefully shuts down the subscription when called.
ID
:The method should return the
subscription.Subscription
ID.Topic
:The method should return the subscription topic.
NewDataProvider
:A factory function, part of the
DataProvider
module that creates concreteDataProvider
based on thetopic
from theWebSocketController
and returns a new instance ofDataProvider
.WebSocketController and DataProvider Relationship
Receiving a Subscription Request:
subscribe
action and the topic to subscribe to.WebSocketController
processes this message inside thereadMessages()
method. It parses the message, and extracts thetopic
andarguments
. Then thesubscribe()
method is called.subscribe()
method, based on the topic from the subscription request, theWebSocketController
callsCreateSubscription
to instantiate the appropriateDataProvider
for the topic.NewDataProvider
function is a factory method responsible for returning the correctDataProvider
(e.g.,EventsDataProvider
,BlocksSubscriptionHandler
, etc.).DataProvider
is stored in thesubs
map of theWebSocketController
using the topic and a generated subscription ID.broadcastMessage
.Handling Subscription Data:
DataProvider
listens for updates from the backend relevant to its topic.DataProvider
calls thebroadcastMessage(data)
callback function provided by theWebSocketController
.broadcastMessage
method then sends this data to thebroadcast
channel, which the new controller monitors.Broadcasting Data to the Client:
WebSocketController
listens on thebroadcast
channel using thewriteMessages()
method.writeMessages()
retrieves it from the channel and sends it to the client over the WebSocket connection.The text was updated successfully, but these errors were encountered: