Skip to content
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

fix(client): Adding graceful shutdown to event loop #2164

Closed
wants to merge 26 commits into from

Conversation

jamdavi
Copy link
Member

@jamdavi jamdavi commented Sep 7, 2021

When we create an Mqtt transport handler we set the EventLoopGroup but we do not gracefully shut it down. This leaves a number of TaskSchedulers open.

This is specific to DotNetty (well Netty really) and does not affect Amqp.

In most cases this will not cause a problem if the application closes after client shutdown, but in WPF applications it will cause the main UI thread to hang and never close.

By default this shutdown will take 15 seconds to complete.

This is documented in the Java Netty wiki.

This will address #2163 and #2194

@jamdavi
Copy link
Member Author

jamdavi commented Sep 8, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Sep 14, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@abhipsaMisra
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Sep 16, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Sep 21, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Oct 8, 2021

Also #2194

@jamdavi
Copy link
Member Author

jamdavi commented Oct 19, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Nov 2, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Nov 2, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

/// <remarks>
/// The DotNetty Mqtt transport has a graceful shutdown of the event loop that can block the <see cref="DeviceClient.CloseAsync(System.Threading.CancellationToken)"/> method. This timeout allows you to configure how long the shutdown event will wait for before forcefully shutting down the event loop. Change this if you need your application to respond to the CloseAsync command faster.
/// </remarks>
public TimeSpan GracefulEventLoopShutdownTimeout { get; set; } = TimeSpan.FromSeconds(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

This was 1 millisecond before. I asked why but the only response I see is changing it to 1 second. Either way, I don't understand how we've chosen a default value and why is 1 second a good default? What are the implications to the user?

Copy link
Member Author

Choose a reason for hiding this comment

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

So by default the timeout value is 14 seconds... I can make this a TimeSpan.Zero and then let the customer decide if they want to lower it.

Copy link
Member

Choose a reason for hiding this comment

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

Same questions about he timeout values selected. It would be helpful to add a reference to the netty specs which define these timeout values (default/recommended etc).

Copy link
Member Author

Choose a reason for hiding this comment

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

I can do that.


private void CreateWithoutLocking(Func<T> objectToCreate)
{
if (_obejctToCount == null && _referenceCount <= 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

under what circumstances does _referenceCount go below zero?

Copy link
Member Author

Choose a reason for hiding this comment

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

None, now that think of it.

@jamdavi
Copy link
Member Author

jamdavi commented Nov 23, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jamdavi
Copy link
Member Author

jamdavi commented Nov 23, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

/// Sets the MqttTransport to use a single Event Loop Group.
/// </summary>
/// <remarks>
/// This setting is to retain legacy behavior that will use a single Event Loop Group which is the thread pool model for DotNetty. If this is set to false a new group will be created per instance of the Device Client.
Copy link
Member

Choose a reason for hiding this comment

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

We have 1 instance of mqtt adapter per device client, and we can have 1 instance of device client for a particular device identity. Considering that, what does this mean:
"If this is set to false a new group will be created per instance of the Device Client.", i.e. does this refer to the scenario where we dispose a client instance and reinitialize?

/// <typeparam name="T">Any class based object type.</typeparam>
public class ReferenceCounter<T> where T : class
{
private T _obejctToCount;
Copy link
Member

Choose a reason for hiding this comment

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

nit: typo on object

///
///
///
/// This will return a mutable object. It is possible to alter the object state which could cause unexpected behavior.
Copy link
Member

Choose a reason for hiding this comment

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

q - What kind of unexpected behavior are we referencing here?

Copy link
Member Author

Choose a reason for hiding this comment

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

In general if you have an object that is reference counted there is no way to determine if the object has been changed. For example, an object could be disposed of while there is still a reference count of 1 but we'd have no way to tell and we'd still return the reference to the disposed object.

@jamdavi
Copy link
Member Author

jamdavi commented Dec 8, 2021

Closing this as the following fix will be released by DotNetty

Azure/DotNetty#570

@jamdavi jamdavi closed this Dec 8, 2021
@abhipsaMisra abhipsaMisra deleted the jamdavi/issue2163 branch February 7, 2022 20:43
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.

5 participants