-
Notifications
You must be signed in to change notification settings - Fork 1k
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 singleton should consider Member AppVersion during hand over. #6065
Merged
Aaronontheweb
merged 7 commits into
akkadotnet:dev
from
Arkatufus:SINGLLETON_consider_AppVersion_on_restart
Aug 18, 2022
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
5e0c762
Cluster singleton should consider Member AppVersion during hand over.
Arkatufus b02e9e1
Add `akka.cluster.singleton.consider-app-version` HOCON setting as op…
Arkatufus f31b4c0
Update API Verify list
Arkatufus 71b2845
Merge branch 'dev' into SINGLLETON_consider_AppVersion_on_restart
Aaronontheweb 288e961
Add MemberAgeOrdering spec
Arkatufus 23589f0
Change insertion order
Arkatufus 229b37d
Update API Verify list
Arkatufus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
135 changes: 135 additions & 0 deletions
135
src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/ClusterSingletonRestart3Spec.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="ClusterSingletonRestart2Spec.cs" company="Akka.NET Project"> | ||
// Copyright (C) 2009-2021 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2021 .NET Foundation <https://github.com/akkadotnet/akka.net> | ||
// </copyright> | ||
//----------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Akka.Actor; | ||
using Akka.Cluster.Tools.Singleton; | ||
using Akka.Configuration; | ||
using Akka.TestKit; | ||
using FluentAssertions; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
|
||
namespace Akka.Cluster.Tools.Tests.Singleton | ||
{ | ||
public class ClusterSingletonRestart3Spec : AkkaSpec | ||
{ | ||
private readonly ActorSystem _sys1; | ||
private readonly ActorSystem _sys2; | ||
private readonly ActorSystem _sys3; | ||
|
||
public ClusterSingletonRestart3Spec(ITestOutputHelper output) : base(@" | ||
akka.loglevel = DEBUG | ||
akka.actor.provider = ""cluster"" | ||
akka.cluster.app-version = ""1.0.0"" | ||
akka.cluster.auto-down-unreachable-after = 2s | ||
akka.cluster.singleton.min-number-of-hand-over-retries = 5 | ||
akka.cluster.singleton.consider-app-version = true | ||
akka.remote { | ||
dot-netty.tcp { | ||
hostname = ""127.0.0.1"" | ||
port = 0 | ||
} | ||
}", output) | ||
{ | ||
_sys1 = Sys; | ||
_sys2 = ActorSystem.Create(Sys.Name, Sys.Settings.Config); | ||
InitializeLogger(_sys2); | ||
_sys3 = ActorSystem.Create(Sys.Name, ConfigurationFactory.ParseString("akka.cluster.app-version = \"1.0.2\"") | ||
.WithFallback(Sys.Settings.Config)); | ||
InitializeLogger(_sys3); | ||
} | ||
|
||
public void Join(ActorSystem from, ActorSystem to) | ||
{ | ||
from.ActorOf(ClusterSingletonManager.Props(Props.Create(() => new Singleton()), | ||
PoisonPill.Instance, | ||
ClusterSingletonManagerSettings.Create(from)), "echo"); | ||
|
||
|
||
Within(TimeSpan.FromSeconds(45), () => | ||
{ | ||
AwaitAssert(() => | ||
{ | ||
Cluster.Get(from).Join(Cluster.Get(to).SelfAddress); | ||
Cluster.Get(from).State.Members.Select(x => x.UniqueAddress).Should().Contain(Cluster.Get(from).SelfUniqueAddress); | ||
Cluster.Get(from) | ||
.State.Members.Select(x => x.Status) | ||
.ToImmutableHashSet() | ||
.Should() | ||
.Equal(ImmutableHashSet<MemberStatus>.Empty.Add(MemberStatus.Up)); | ||
}); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public void Singleton_should_consider_AppVersion_when_handing_over() | ||
{ | ||
Join(_sys1, _sys1); | ||
Join(_sys2, _sys1); | ||
|
||
var proxy2 = _sys2.ActorOf( | ||
ClusterSingletonProxy.Props("user/echo", ClusterSingletonProxySettings.Create(_sys2)), "proxy2"); | ||
|
||
Within(TimeSpan.FromSeconds(5), () => | ||
{ | ||
AwaitAssert(() => | ||
{ | ||
var probe = CreateTestProbe(_sys2); | ||
proxy2.Tell("poke", probe.Ref); | ||
var singleton = probe.ExpectMsg<Member>(TimeSpan.FromSeconds(1)); | ||
singleton.Should().Be(Cluster.Get(_sys1).SelfMember); | ||
singleton.AppVersion.Version.Should().Be("1.0.0"); | ||
}); | ||
}); | ||
|
||
// A new node with higher AppVersion joins the cluster | ||
Join(_sys3, _sys1); | ||
|
||
// Old node with the singleton instance left the cluster | ||
Cluster.Get(_sys1).Leave(Cluster.Get(_sys1).SelfAddress); | ||
|
||
// let it stabilize | ||
Task.Delay(TimeSpan.FromSeconds(5)).Wait(); | ||
|
||
Within(TimeSpan.FromSeconds(10), () => | ||
{ | ||
AwaitAssert(() => | ||
{ | ||
var probe = CreateTestProbe(_sys2); | ||
proxy2.Tell("poke", probe.Ref); | ||
|
||
// note that _sys3 has a higher app-version, so the singleton should start there | ||
var singleton = probe.ExpectMsg<Member>(TimeSpan.FromSeconds(1)); | ||
singleton.Should().Be(Cluster.Get(_sys3).SelfMember); | ||
singleton.AppVersion.Version.Should().Be("1.0.2"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
}); | ||
}); | ||
} | ||
|
||
protected override async Task AfterAllAsync() | ||
{ | ||
await base.AfterAllAsync(); | ||
await ShutdownAsync(_sys2); | ||
await ShutdownAsync(_sys3); | ||
} | ||
|
||
public class Singleton : ReceiveActor | ||
{ | ||
public Singleton() | ||
{ | ||
ReceiveAny(o => | ||
{ | ||
Sender.Tell(Cluster.Get(Context.System).SelfMember); | ||
}); | ||
} | ||
} | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/MemberAgeOrderingSpec.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// ----------------------------------------------------------------------- | ||
// <copyright file="MemberAgeOrderingSpec.cs" company="Akka.NET Project"> | ||
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net> | ||
// </copyright> | ||
// ----------------------------------------------------------------------- | ||
|
||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Akka.Actor; | ||
using Akka.Cluster.Tools.Singleton; | ||
using Akka.Util; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace Akka.Cluster.Tools.Tests.Singleton | ||
{ | ||
public class MemberAgeOrderingSpec | ||
{ | ||
[Fact(DisplayName = "MemberAgeOrdering should sort based on UpNumber")] | ||
public void SortByUpNumberTest() | ||
{ | ||
var members = new SortedSet<Member>(MemberAgeOrdering.DescendingWithAppVersion) | ||
{ | ||
Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 3), | ||
Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1), | ||
Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 9), | ||
}; | ||
|
||
var seq = members.ToList(); | ||
seq.Count.Should().Be(3); | ||
seq[0].Should().Be(Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1)); | ||
seq[1].Should().Be(Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 3)); | ||
seq[2].Should().Be(Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 9)); | ||
} | ||
|
||
[Fact(DisplayName = "MemberAgeOrdering should sort based on Address if UpNumber is the same")] | ||
Aaronontheweb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public void SortByAddressTest() | ||
{ | ||
var members = new SortedSet<Member>(MemberAgeOrdering.DescendingWithAppVersion) | ||
{ | ||
Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 1), | ||
Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1), | ||
Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 1), | ||
}; | ||
|
||
var seq = members.ToList(); | ||
seq.Count.Should().Be(3); | ||
seq[0].Should().Be(Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 1)); | ||
seq[1].Should().Be(Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 1)); | ||
seq[2].Should().Be(Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1)); | ||
} | ||
|
||
[Fact(DisplayName = "MemberAgeOrdering should prefer AppVersion over UpNumber")] | ||
public void SortByAppVersionTest() | ||
{ | ||
var members = new SortedSet<Member>(MemberAgeOrdering.DescendingWithAppVersion) | ||
{ | ||
Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 3, appVersion: AppVersion.Create("1.0.0")), | ||
Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1, appVersion: AppVersion.Create("1.0.0")), | ||
Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 2, appVersion: AppVersion.Create("1.0.0")), | ||
Create(Address.Parse("akka://sys@darkstar:1114"), upNumber: 7, appVersion: AppVersion.Create("1.0.2")), | ||
Create(Address.Parse("akka://sys@darkstar:1115"), upNumber: 8, appVersion: AppVersion.Create("1.0.2")), | ||
Create(Address.Parse("akka://sys@darkstar:1116"), upNumber: 6, appVersion: AppVersion.Create("1.0.2")), | ||
}; | ||
|
||
var seq = members.ToList(); | ||
seq.Count.Should().Be(6); | ||
seq[0].Should().Be(Create(Address.Parse("akka://sys@darkstar:1116"), upNumber: 6, appVersion: AppVersion.Create("1.0.2"))); | ||
seq[1].Should().Be(Create(Address.Parse("akka://sys@darkstar:1114"), upNumber: 7, appVersion: AppVersion.Create("1.0.2"))); | ||
seq[2].Should().Be(Create(Address.Parse("akka://sys@darkstar:1115"), upNumber: 8, appVersion: AppVersion.Create("1.0.2"))); | ||
seq[3].Should().Be(Create(Address.Parse("akka://sys@darkstar:1113"), upNumber: 1, appVersion: AppVersion.Create("1.0.0"))); | ||
seq[4].Should().Be(Create(Address.Parse("akka://sys@darkstar:1111"), upNumber: 2, appVersion: AppVersion.Create("1.0.0"))); | ||
seq[5].Should().Be(Create(Address.Parse("akka://sys@darkstar:1112"), upNumber: 3, appVersion: AppVersion.Create("1.0.0"))); | ||
} | ||
|
||
public static Member Create( | ||
Address address, | ||
MemberStatus status = MemberStatus.Up, | ||
ImmutableHashSet<string> roles = null, | ||
int uid = 0, | ||
int upNumber = 0, | ||
AppVersion appVersion = null) | ||
{ | ||
return Member.Create(new UniqueAddress(address, uid), upNumber, status, roles ?? ImmutableHashSet<string>.Empty, appVersion ?? AppVersion.Zero); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -880,7 +880,9 @@ private void InitializeFSM() | |
{ | ||
case StartOldestChangedBuffer _: | ||
{ | ||
_oldestChangedBuffer = Context.ActorOf(Actor.Props.Create<OldestChangedBuffer>(_settings.Role).WithDispatcher(Context.Props.Dispatcher)); | ||
_oldestChangedBuffer = Context.ActorOf( | ||
Actor.Props.Create(() => new OldestChangedBuffer(_settings.Role, _settings.ConsiderAppVersion)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
.WithDispatcher(Context.Props.Dispatcher)); | ||
GetNextOldestChanged(); | ||
return Stay(); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM