moozzyk / SignalR-Client-Swift

Swift SignalR Client for Asp.Net Core SignalR server
MIT License
351 stars 132 forks source link

Signal R Seems to be throttling invocation sent to client. #298

Closed JCWA2023 closed 5 months ago

JCWA2023 commented 5 months ago

We are implementing a feature where we receive both captions and audio over two different web socket callbacks both registered using the on(method: callback) function. Thee callbacks are both fired multiple times a second when we are actively receiving audio. When I add additional log statements into the signal R code I can see that we are landing in connectionDidReceiveData for each of the messages, each one is successfully finding the registered callback - however we are only ever executing our callback code for one of the events (the more frequent one is being preferred).

Is there some sort of throttling that is happening in the swift signal R client that could cause these events to not actually be invoked?

Consumed via cocoa pods: SwiftSignalRClient -> 0.8.0

Registration (our code) this is done after the socket is connected

hubConnection.on(method: "broadcast", callback: { [weak self] in guard let self else { return } handleBroadcast() }

hubConnection.on(method: "broadcastCaptions", callback: { [weak self] in guard let self else { return } handleCaptions() }

We are not ever executing the "handleCaptions" function. However I can see that the handleCaptions message being received by the signalRClient

So these printStatements I added in these functions:

        logger.log(logLevel: .debug, message: "Data received")
        var data = data
        if !handshakeStatus.isHandled {
            logger.log(logLevel: .debug, message: "Processing handshake response: \(String(data: data, encoding: .utf8) ?? "(invalid)")")
            let (error, remainingData) = HandshakeProtocol.parseHandshakeResponse(data: data)
            data = remainingData
            let originalHandshakeStatus = handshakeStatus
            handshakeStatus = .handled
            if let e = error {
                // TODO: (BUG) if this fails when reconnecting the callback should not be called and there
                // will be no further reconnect attempts
                logger.log(logLevel: .error, message: "Parsing handshake response failed: \(e)")
                delegate?.connectionDidFailToOpen(error: e)
                return
            }
            if originalHandshakeStatus.isReconnect {
                delegate?.connectionDidReconnect()
            } else {
                delegate?.connectionDidOpen(hubConnection: self)
            }
        }
        do {
            let messages = try hubProtocol.parseMessages(input: data)
            print("&&& connection received data with: \(messages.count) messages: \(messages)")
            for incomingMessage in messages {
                print("&&& connection received data: \(incomingMessage.type)")
                switch(incomingMessage.type) {
                case MessageType.Completion:
                    try handleCompletion(message: incomingMessage as! CompletionMessage)
                case MessageType.StreamItem:
                    try handleStreamItem(message: incomingMessage as! StreamItemMessage)
                case MessageType.Invocation:
                    handleInvocation(message: incomingMessage as! ClientInvocationMessage)
                case MessageType.Close:
                    connection.stop(stopError: SignalRError.serverClose(message: (incomingMessage as! CloseMessage).error))
                case MessageType.Ping:
                    // no action required for ping messages
                    break
                default:
                    logger.log(logLevel: .error, message: "Usupported message type: \(incomingMessage.type.rawValue)")
                }
            }
        } catch {
            logger.log(logLevel: .debug, message: "Parsing message failed: \(error)")
        }
    }

    private func handleInvocation(message: ClientInvocationMessage) {
        var callback: ((ArgumentExtractor) throws -> Void)?
        print("&&&& \(message.target)")
        self.hubConnectionQueue.sync {
            print("&&&& in sync to get callback for method: \(message.target)")
            callback = self.callbacks[message.target]
        }

        if callback != nil {
            print("&&& found callback for method: \(message.target)")
            Util.dispatchToMainThread {
                do {
                    print("&&& in do try block for for method: \(message.target)")
                    try callback!(ArgumentExtractor(clientInvocationMessage: message))
                } catch {
                    self.logger.log(logLevel: .error, message: "Invoking client hub method \(message.target) failed due to: \(error)")
                }
            }
        } else {
            logger.log(logLevel: .error, message: "No handler registered for method \'\(message.target)\'")
        }
    }

Are printing out the following:

&&& connection received data with: 1 messages: [SwiftSignalRClient.ClientInvocationMessage] &&& connection received data: Invocation &&&& broadcast &&&& in sync to get callback for method: broadcast &&& found callback for method: broadcast &&& in do try block for for method: broadcast &&& connection received data with: 1 messages: [SwiftSignalRClient.ClientInvocationMessage] &&& connection received data: Invocation &&&& broadcastCaptions &&&& in sync to get callback for method: broadcastCaptions &&& found callback for method: broadcastCaptions &&& in do try block for for method: broadcastCaptions

However like stated above I only ever end up executing our callback code for handleBroadcast().

We are getting about 4 broadcast events for every one broadcastCaptions event

moozzyk commented 5 months ago

https://github.com/moozzyk/SignalR-Client-Swift/blob/a028e01acc5ef32c0f4933c4f56d645d64b36500/Sources/SignalRClient/HubConnection.swift#L451

JCWA2023 commented 5 months ago

We weren't seeing that logged at all - so it seems like it was potentially invoking it correctly but we were somehow just not getting it. I don't know if there could have been some throttling under the hood in swift?

moozzyk commented 5 months ago

There is no throttling I am aware of. If the invocation is not working it usually means there is some mismatch between how your callback is registered and the invocation message. Without seeing logs I don't have any way to tell where the problem is.