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

Cluster LeaveAsync() with CancellationToken support #2501

Merged
merged 11 commits into from
Mar 25, 2017
Merged

Cluster LeaveAsync() with CancellationToken support #2501

merged 11 commits into from
Mar 25, 2017

Conversation

kostrse
Copy link
Contributor

@kostrse kostrse commented Feb 4, 2017

I added overload of the LeaveAsync() method with support of CancellationToken.

Cancellation token support should be useful assurance to avoid unexpected hang of application if member removal confirmation didn't come for some reason.

I also made the leave logic by LeaveAsync() to be triggered only once (via Interlocked). This is probably not critical for the cluster daemon, but just in case 😺 (related to #2498)

Usage example:

    var cts = new CancellationTokenSource();
    cts.CancelAfter(TimeSpan.FromMinutes(5));

    await Cluster.Get(_actorSystem).LeaveAsync(cancellationToken);
    await _actorSystem.Terminate();

Related issues: #2280, #2498

Also:

Leave() and LeaveAsync() do not cooperate now, but it's possible to make Leave() call LeaveSelf() to share state between them:

public void Leave(Address address)
{
    if (address == SelfAddress)
    {
        LeaveSelf();
    }
    else
        ClusterCore.Tell(new ClusterUserAction.Leave(FillLocal(address)));
    }
}

Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

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

Left some questions, but overall I think these changes are good.

@@ -192,6 +194,76 @@ public void A_cluster_must_complete_LeaveAsync_task_upon_being_removed()
_cluster.LeaveAsync().IsCompleted.Should().BeTrue();
}

[Fact(Skip = "This behavior not yet supported.")]
Copy link
Member

Choose a reason for hiding this comment

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

Why isn't this behavior supported yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because wanted to discuss before 😄

Right now Leave() and LeaveAsync() do not share state with each other.

LeaveAsync() memoizes the _leaveTask, therefore two subsequent calls of LeaveAsync() won't result in double sending of the leave request.

But Leave() doesn't take the _leaveTask into account. So, if cluster have been left by the Leave() method (or is in state of leaving) , LeaveAsync() doesn't know about it and will be waiting for the MemberRemoved event.

Here is how we can change the Leave() method to support that:

public void Leave(Address address)
{
    if (address == SelfAddress)
    {
        LeaveSelf();
    }
    else
        ClusterCore.Tell(new ClusterUserAction.Leave(FillLocal(address)));
}

I just wanted to check before, to be sure that it won't make any negative effect on anything.
Plus the self address comparison looks questionable.

Copy link
Member

Choose a reason for hiding this comment

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

Might need to compare the output of FillLocal(address) to SelfAddress, although I'd need to look at the FillLocal function again to say for that sure :p

if (tcs.Task.IsCompleted)
return tcs.Task;
// It's assumed here that once the member left the cluster, it won't get back again.
// So, the member removal event being memoized in TaskCompletionSource and never reset.
Copy link
Member

Choose a reason for hiding this comment

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

Makes sense

/// <param name="task">The original task.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The task which completes with result of original task or with cancelled state.</returns>
public static Task WithCancellation(this Task task, CancellationToken cancellationToken)
Copy link
Member

Choose a reason for hiding this comment

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

Nice, I like it, although maybe we should mark this as internal? cc @alexvaluyskiy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The TaskExtensions and Cluster are in different assemblies.
I found this extensions class and decided to add the WithCancellation() method here, but I can move it into another place if you know a better location.

Is the name WithCancellation sounds good, BTW?

Copy link
Member

Choose a reason for hiding this comment

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

@kostrse actually it's probably fine to leave this public. WithCancellation sounds great 👍

Copy link
Contributor

@alexvaluyskiy alexvaluyskiy Feb 16, 2017

Choose a reason for hiding this comment

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

The TaskExtensions and Cluster are in different assemblies.

We are using [InternalVisibleToAttribute]. So, It does not matter that these classes are situated in different assemblies

private Task LeaveSelf()
{
var tcs = new TaskCompletionSource<object>();
var leaveTask = Interlocked.CompareExchange(ref _leaveTask, tcs.Task, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice solution

@Danthar
Copy link
Member

Danthar commented Feb 7, 2017

Note: windows-unit-tests are failing due to Api Approval.

@Aaronontheweb
Copy link
Member

@kostrse for fixing the public API issue: http://getakka.net/docs/akka-developers/public-api-changes

@kostrse
Copy link
Contributor Author

kostrse commented Feb 9, 2017 via email

@Aaronontheweb
Copy link
Member

@kostrse have you had a chance to update this yet?

@kostrse
Copy link
Contributor Author

kostrse commented Feb 14, 2017

Sorry for delay, working on it now

@kostrse
Copy link
Contributor Author

kostrse commented Feb 14, 2017

The test ClusterSpec.A_cluster_must_complete_LeaveAsync_task_upon_being_removed is still racy:

Timeout 00:00:10 expired while waiting for condition.Failed to observe node as Exiting.
Expected: True
Actual:   False
   at Akka.TestKit.Xunit2.XunitAssertions.Fail(String format, Object[] args) in D:\work\d26c9d7f36545acd\src\contrib\testkits\Akka.TestKit.Xunit2\XunitAssertions.cs:line 27
   at Akka.TestKit.TestKitBase.<>c__DisplayClass135_0.<AwaitCondition>b__0(String format, Object[] args) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_AwaitConditions.cs:line 91
   at Akka.TestKit.TestKitBase.InternalAwaitCondition(Func`1 conditionIsFulfilled, TimeSpan max, Nullable`1 interval, Action`2 fail, ILoggingAdapter logger) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_AwaitConditions.cs:line 229
   at Akka.TestKit.TestKitBase.AwaitCondition(Func`1 conditionIsFulfilled, Nullable`1 max, String message) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_AwaitConditions.cs:line 92
   at Akka.Cluster.Tests.ClusterSpec.<>c__DisplayClass16_0.<A_cluster_must_complete_LeaveAsync_task_upon_being_removed>b__2() in D:\work\d26c9d7f36545acd\src\core\Akka.Cluster.Tests\ClusterSpec.cs:line 187
   at Akka.TestKit.TestKitBase.<>c__DisplayClass116_0.<Within>b__0() in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_Within.cs:line 48
   at Akka.TestKit.TestKitBase.Within[T](TimeSpan min, TimeSpan max, Func`1 function, String hint, Nullable`1 epsilonValue) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_Within.cs:line 101
   at Akka.TestKit.TestKitBase.Within(TimeSpan min, TimeSpan max, Action action, String hint, Nullable`1 epsilonValue) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_Within.cs:line 49
   at Akka.TestKit.TestKitBase.Within(TimeSpan max, Action action, Nullable`1 epsilonValue) in D:\work\d26c9d7f36545acd\src\core\Akka.TestKit\TestKitBase_Within.cs:line 32
   at Akka.Cluster.Tests.ClusterSpec.A_cluster_must_complete_LeaveAsync_task_upon_being_removed() in D:\work\d26c9d7f36545acd\src\core\Akka.Cluster.Tests\ClusterSpec.cs:line 193

I don't know yet the TestKit very well to track down the problem :(

@Aaronontheweb Aaronontheweb added this to the 1.2.0 milestone Feb 20, 2017
@Aaronontheweb
Copy link
Member

@kostrse I think #2516 needs to be added in order to formally solve that problem.

@Aaronontheweb
Copy link
Member

@kostrse would you mind handling the merge conflict here?

@kostrse
Copy link
Contributor Author

kostrse commented Mar 23, 2017

Yes, sorry, will take a look now.

@kostrse
Copy link
Contributor Author

kostrse commented Mar 23, 2017

the continuous-integration/nuget-pack build failure probably not related to the change itself.

@Aaronontheweb Aaronontheweb merged commit 69baba6 into akkadotnet:dev Mar 25, 2017
@kostrse kostrse deleted the dev-leaveasync-cancellation branch March 25, 2017 10:32
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