Skip to content

Conversation

@zackliu
Copy link
Member

@zackliu zackliu commented Oct 19, 2018

  • As customer can't get any error messages when service terminate the server connections now, it will be very confusing when service reach message limit. Under that circumstance, server will keep reconnecting without any useful reasons.
  • ServerCloseMessage is used to send back error messages from service before service abort the connection. There are some changes in service to enable the workflow and in SDK, we just log these error messages.
  • This is not a breaking change, old sdk can work with new service and vice versa.

return new MultiGroupBroadcastDataMessage(groupList, payloads);
}

private static ServerCloseMessage createServerCloseMessage(byte[] input, ref int offset)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the casing of this method, it should pascal cased.

binary: "kw6Spmdyb3VwNKZncm91cDWCpGpzb27ECAECAwQFBgcIq21lc3NhZ2VwYWNrxAgHCAECAwQFBg=="),
new ProtocolTestData(
name: "ServerClose",
message: new ServerCloseMessage("Message count reach limit: 100000"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message should be: "Maximum message count limit reached: 100000"

@vwxyzh vwxyzh mentioned this pull request Oct 19, 2018
2 tasks
@davidfowl
Copy link
Member

Where's the unit test? Does it stop reconnecting as a result?

@zackliu
Copy link
Member Author

zackliu commented Oct 19, 2018

No it won't, just log an error message.

@davidfowl
Copy link
Member

Where does the message show up? In the logs? Can you ever recover from this error or will it keep getting sent until a day has passed?

@zackliu
Copy link
Member Author

zackliu commented Oct 19, 2018

It will show in the log. We can't recover it in the SDK side, we just log it and reconnect. The reasons of aborting connection are various, some of them can be recovered after reconnecting. For those can't be recovered through reconnecting e.g. message count limit reached, we will handle it in service side and turn to a slow reconnecting path.(#233)

@davidfowl
Copy link
Member

I'm thinking that it would be good to capture this exception and re-throw it from the calls to

public async virtual Task WriteAsync(ServiceMessage serviceMessage)
. The user would get the message from the service side of a generic message.

@zackliu
Copy link
Member Author

zackliu commented Oct 19, 2018

public async virtual Task WriteAsync(ServiceMessage serviceMessage)

This function is to write message to service side. But now we get error message from service and soon the transport connection and application connection will be terminated. We have no way to throw it to user.

@davidfowl
Copy link
Member

This function is to write message to service side. But now we get error message from service and soon the transport connection and application connection will be terminated. We have no way to throw it to user.

That's not true. This is directly called by user code here https://github.com/Azure/azure-signalr/blob/c823e993aba01774e6f19faf78f216e8b71f8e46/src/Microsoft.Azure.SignalR/HubHost/ServiceLifetimeManager.cs. If we capture the exception and rethrow it, the user will be able to observe it in their code.

@zackliu
Copy link
Member Author

zackliu commented Oct 19, 2018

I see, but we receive error message in ProcessIncomingAsync, how can I throw exceptions in WriteAsync. When error happens, user may not call any methods. He just find the service connection abort without calling anything, now we've already handle the reconnecting and then just need to tell him why in logs,

@davidfowl
Copy link
Member

I see, but we receive error message in ProcessIncomingAsync, how can I throw exceptions in WriteAsync

By storing the error and throwing it when WriteAsync is called. When the connection recovers the error should be cleared.

@davidfowl
Copy link
Member

He just find the service connection abort without calling anything, now we've already handle the reconnecting and then just need to tell him why in logs,

It dosn't need to be one or the other, I don't see why you wouldn't do both. I'd also probably make this a specific exception type.

@zackliu
Copy link
Member Author

zackliu commented Oct 19, 2018

I think throwing exception when calling WriteAsync has been handled by


After we receives ServerCloseMessage, the connection will be terminated soon. And this error should be only the connection scoped error. So when a new connection is created, we can't treat it as a bad connection.

@davidfowl
Copy link
Member

After we receives ServerCloseMessage, the connection will be terminated soon. And this error should be only the connection scoped error. So when a new connection is created, we can't treat it as a bad connection.

That's my point though. You can give the user the right error message instead of the generic one "The connection is not active, data cannot be sent to the service." becomes the actual error that came from the service.

@vicancy
Copy link
Member

vicancy commented Oct 19, 2018

I love not throwing generic one "The connection is not active, data cannot be sent to the service."!

case ServiceProtocolConstants.MultiGroupBroadcastDataMessageType:
return CreateMultiGroupBroadcastDataMessage(input, ref startOffset);
case ServiceProtocolConstants.ServerCloseMessageType:
return createServerCloseMessage(input, ref startOffset);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the function name consistent with others style: "CreateServerCloseMessage"

return MultiGroupBroadcastDataMessagesEqual(multiGroupBroadcastDataMessage,
(MultiGroupBroadcastDataMessage)y);
case ServerCloseMessage serverCloseMessage:
return ServerCloseMessageEqual(serverCloseMessage, (ServerCloseMessage) y);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove the white space in type casting: "(ServerCloseMessage) y);"

private bool _isStopped;
private long _lastReceiveTimestamp;
protected ConnectionContext _connection;
protected string _errorMessage;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this isn't private?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inheritance class which override WriteAsync can use _errorMessage

throw new Exception(serverCloseMessage.ErrorMessage);
}

return Task.CompletedTask;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CompletedTask [](start = 24, length = 13)

shall we close the server connection with this message?

if (!string.IsNullOrEmpty(serverCloseMessage.ErrorMessage))
{
_errorMessage = serverCloseMessage.ErrorMessage;
throw new Exception(serverCloseMessage.ErrorMessage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exception [](start = 26, length = 9)

throw a more detailed exception?

{
if (!string.IsNullOrEmpty(serviceErrorMessage.ErrorMessage))
{
// When receives service error message, we suppose server -> service connection doesn't work,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidfowl After discussed offline, we renamed the message name and changed to log rather than throw exceptions in ProcessIncomingAsync

LoggerMessage.Define(LogLevel.Warning, new EventId(26, "FailedSendingPing"), "Failed sending a ping message to service.");

private static readonly Action<ILogger, string, Exception> _receivedServiceErrorMessage =
LoggerMessage.Define<string>(LogLevel.Warning, new EventId(27, "ReceivedServiceErrorMessage"), "Received error message from service: {Error}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also log connectionId?


if (!string.IsNullOrEmpty(ErrorMessage))
{
_serviceConnectionLock.Release();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a local errorMessage=ErrorMessage for throw, ErrorMessage can be reset in between if and throw

@zackliu zackliu merged commit 5032c59 into Azure:dev Oct 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants