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

Non Promotable Clone #1751

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/EventStore.ClusterNode/ClusterNodeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@ public class ClusterNodeOptions : IOptions {
[ArgDescription(Opts.MaxAutoMergeIndexLevelDescr, Opts.DbGroup)]
public int MaxAutoMergeIndexLevel { get; set; }

[ArgDescription(Opts.IsPromotableDescr, Opts.ClusterGroup)]
public bool IsPromotable { get; set; }

public ClusterNodeOptions() {
Config = "";
Help = Opts.ShowHelpDefault;
Expand Down Expand Up @@ -426,6 +429,8 @@ public class ClusterNodeOptions : IOptions {
ChunkInitialReaderCount = Opts.ChunkInitialReaderCountDefault;

MaxAutoMergeIndexLevel = Opts.MaxAutoMergeIndexLevelDefault;

IsPromotable = Opts.IsPromotableDefault;
}
}
}
2 changes: 1 addition & 1 deletion src/EventStore.ClusterNode/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public class Program : ProgramBase<ClusterNodeOptions> {

VNodeBuilder builder;
if (options.ClusterSize > 1) {
builder = ClusterVNodeBuilder.AsClusterMember(options.ClusterSize);
builder = ClusterVNodeBuilder.AsClusterMember(options.ClusterSize).AsPromotable(options.IsPromotable);
} else {
builder = ClusterVNodeBuilder.AsSingleNode();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using EventStore.Common.Utils;
Expand All @@ -14,6 +15,10 @@ public class ClusterSettingsFactory {
private const int StartingPort = 1002;

private static ClusterVNodeSettings CreateVNode(int nodeNumber) {
return CreateVNode(nodeNumber, Opts.IsPromotableDefault);
}

private static ClusterVNodeSettings CreateVNode(int nodeNumber, bool isPromotable) {
int tcpIntPort = StartingPort + nodeNumber * 2,
tcpExtPort = tcpIntPort + 1,
httpIntPort = tcpIntPort + 10,
Expand All @@ -40,7 +45,13 @@ public class ClusterSettingsFactory {
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(10), true, Opts.MaxMemtableSizeDefault, Opts.HashCollisionReadLimitDefault, false,
false, false,
Opts.ConnectionPendingSendBytesThresholdDefault, Opts.ChunkInitialReaderCountDefault);
Opts.ConnectionPendingSendBytesThresholdDefault, Opts.ChunkInitialReaderCountDefault, null,
Opts.HistogramEnabledDefault, Opts.SkipDbVerifyDefault, Opts.IndexCacheDepthDefault,
Opts.IndexBitnessVersionDefault, Opts.OptimizeIndexMergeDefault, null,
Opts.UnsafeIgnoreHardDeleteDefault, Opts.BetterOrderingDefault, Opts.ReaderThreadsCountDefault,
Opts.AlwaysKeepScavengedDefault, Opts.GossipOnSingleNodeDefault, Opts.SkipIndexScanOnReadsDefault,
Opts.ReduceFileCachePressureDefault, Opts.InitializationThreadsDefault, Opts.FaultOutOfOrderProjectionsDefault,
Opts.StructuredLogDefault, Opts.MaxAutoMergeIndexLevelDefault, isPromotable);

return vnode;
}
Expand All @@ -63,5 +74,24 @@ public class ClusterSettingsFactory {
var settings = new ClusterSettings("test-dns", clusterManager, self, others, nodes.Length);
return settings;
}

public ClusterSettings GetClusterSettingsWithSelfAsNonPromotableClone(int selfIndex, int nodesCount) {
if (selfIndex < 0 || selfIndex >= nodesCount)
throw new ArgumentOutOfRangeException("selfIndex", "Index of self should be in range of created nodes");


var clusterManager = GetLoopbackForPort(ManagerPort);

var nodes = new List<ClusterVNodeSettings>();
for (var i = 0; i < nodesCount; i++) {
nodes.Add(i == selfIndex ? CreateVNode(i, false) : CreateVNode(i));
}

var self = nodes[selfIndex];
var others = nodes.Where((x, i) => i != selfIndex).ToArray();

var settings = new ClusterSettings("test-dns", clusterManager, self, others, nodes.Count);
return settings;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ internal class RandomizedElectionsAndGossipTestCase : RandomizedElectionsTestCas
var members = _createInitialGossip(instance, allInstances.ToArray());
_updateGossipProcessor.SetInitialData(allInstances, members);

return new GossipMessage.GossipUpdated(new ClusterInfo(members));
return new GossipMessage.GossipUpdated(new ClusterInfo(members),
new ClusterInfo(_createInitialGossip(instance, allInstances.ToArray())));
}

protected override SendOverHttpProcessor GetSendOverHttpProcessor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@ internal class RandomizedElectionsTestCase {

private readonly List<ElectionsInstance> _instances = new List<ElectionsInstance>();

private readonly bool _isPromotable;

public RandomizedElectionsTestCase(int maxIterCnt,
int instancesCnt,
double httpLossProbability,
double httpDupProbability,
int httpMaxDelay,
int timerMinDelay,
int timerMaxDelay,
int? rndSeed = null) {
int? rndSeed = null,
bool isPromotable = true) {
RndSeed = rndSeed ?? Math.Abs(Environment.TickCount);
Rnd = new Random(RndSeed);

Expand All @@ -51,6 +54,7 @@ internal class RandomizedElectionsTestCase {
HttpMaxDelay = httpMaxDelay;
_timerMinDelay = timerMinDelay;
_timerMaxDelay = timerMaxDelay;
_isPromotable = isPromotable;

Runner = new RandomTestRunner(_maxIterCnt);
Logger = new ElectionsLogger();
Expand All @@ -64,7 +68,7 @@ internal class RandomizedElectionsTestCase {
var outputBus = new InMemoryBus(string.Format("ELECTIONS-OUTPUT-BUS-{0}", i));
var endPoint = new IPEndPoint(BaseEndPoint.Address, BaseEndPoint.Port + i);
var nodeInfo = new VNodeInfo(Guid.NewGuid(), 0, endPoint, endPoint, endPoint, endPoint, endPoint,
endPoint);
endPoint, _isPromotable);
_instances.Add(new ElectionsInstance(nodeInfo.InstanceId, endPoint, inputBus, outputBus));

sendOverHttpHandler.RegisterEndPoint(endPoint, inputBus);
Expand Down Expand Up @@ -110,7 +114,7 @@ internal class RandomizedElectionsTestCase {
var members = allInstances.Select(
x => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true,
x.EndPoint, null, x.EndPoint, null, x.EndPoint, x.EndPoint, -1, 0, 0, -1, -1, Guid.Empty, 0));
var gossip = new GossipMessage.GossipUpdated(new ClusterInfo(members.ToArray()));
var gossip = new GossipMessage.GossipUpdated(new ClusterInfo(members.ToArray()), new ClusterInfo());
return gossip;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ internal class UpdateGossipProcessor : IRandTestItemProcessor {
_sendOverHttpProcessor.RegisterEndpointToSkip(memberInfo.ExternalTcpEndPoint, !memberInfo.IsAlive);
}

var updateGossipMessage = new GossipMessage.GossipUpdated(new ClusterInfo(updatedGossip));
var updateGossipMessage = new GossipMessage.GossipUpdated(new ClusterInfo(updatedGossip),
new ClusterInfo(_initialGossip));

_enqueue(item, updateGossipMessage);
_previousGossip[item.EndPoint] = updatedGossip;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using EventStore.Core.Cluster;
using EventStore.Core.Messages;
using EventStore.Core.Services.TimerService;
using EventStore.Core.Tests.Helpers;
Expand All @@ -20,7 +21,7 @@ public sealed class elections_service_should_stuck_with_single_node_response {
}

private void ProcessElections() {
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo);
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo, new ClusterInfo());
_electionsUnit.Publish(gossipUpdate);

_electionsUnit.Publish(new ElectionMessage.StartElections());
Expand Down Expand Up @@ -50,7 +51,7 @@ public sealed class elections_service_should_stuck_with_single_node_response_2_i
}

private void ProcessElections() {
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo);
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo, new ClusterInfo());
_electionsUnit.Publish(gossipUpdate);

_electionsUnit.Publish(new ElectionMessage.StartElections());
Expand Down Expand Up @@ -91,7 +92,7 @@ public sealed class elections_service_should_stuck_with_single_alive_node {
}

private void ProcessElections() {
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo);
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo, new ClusterInfo());
_electionsUnit.Publish(gossipUpdate);

_electionsUnit.Publish(new ElectionMessage.StartElections());
Expand Down Expand Up @@ -121,4 +122,58 @@ public sealed class elections_service_should_stuck_with_single_alive_node {
Assert.That(_electionsUnit.Publisher.Messages.ContainsSingle<ElectionMessage.SendViewChangeProof>());
}
}

[TestFixture]
public sealed class elections_service_should_not_elect_nonpromotableclone {
private ElectionsServiceUnit _electionsUnit;
private const int SelfIndex = 0;
private const int View = 2;

[SetUp]
public void SetUp() {
var clusterSettingsFactory = new ClusterSettingsFactory();
var clusterSettings = clusterSettingsFactory.GetClusterSettingsWithSelfAsNonPromotableClone(SelfIndex, 3);

_electionsUnit = new ElectionsServiceUnit(clusterSettings);
_electionsUnit.UpdateClusterMemberInfo(0, isAlive: true);
_electionsUnit.UpdateClusterMemberInfo(1, isAlive: true);
_electionsUnit.UpdateClusterMemberInfo(2, isAlive: false);

StartElections();
Prepare(SelfIndex, View);
}

private void StartElections() {
var gossipUpdate = new GossipMessage.GossipUpdated(_electionsUnit.ClusterInfo, new ClusterInfo());
_electionsUnit.Publish(gossipUpdate);

_electionsUnit.Publish(new ElectionMessage.StartElections());

_electionsUnit.RepublishFromPublisher();

_electionsUnit.RepublishFromPublisher();
Assert.That(_electionsUnit.Publisher.Messages.All(x => x is HttpMessage.SendOverHttp || x is TimerMessage.Schedule),
Is.True,
"Only OverHttp or Schedule messages are expected.");

_electionsUnit.RepublishFromPublisher();

_electionsUnit.RepublishFromPublisher();
Assert.That(_electionsUnit.Publisher.Messages.All(x => x is HttpMessage.SendOverHttp || x is TimerMessage.Schedule),
Is.True,
"Only OverHttp or Schedule messages are expected.");

_electionsUnit.RepublishFromPublisher();
}

private void Prepare(int selfIndex, int view) {
_electionsUnit.Publish(new ElectionMessage.Prepare(_electionsUnit.ClusterInfo.Members[selfIndex].InstanceId,
_electionsUnit.ClusterInfo.Members[selfIndex].ExternalTcpEndPoint, view));
}

[Test]
public void prepare_node_for_election_fail_if_nonpromotableclone() {
Assert.That(_electionsUnit.Publisher.Messages.ContainsSingle<ElectionMessage.PrepareKo>());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public abstract class with_index_committer_service {
var masterIPEndPoint = new IPEndPoint(IPAddress.Loopback, 2113);
_service.Handle(new SystemMessage.BecomeSlave(Guid.NewGuid(), new VNodeInfo(Guid.NewGuid(), 1,
masterIPEndPoint, masterIPEndPoint, masterIPEndPoint,
masterIPEndPoint, masterIPEndPoint, masterIPEndPoint)));
masterIPEndPoint, masterIPEndPoint, masterIPEndPoint, true)));
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/EventStore.Core/Cluster/Settings/ClusterVNodeSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public class ClusterVNodeSettings {
public readonly bool FaultOutOfOrderProjections;
public readonly bool StructuredLog;

public readonly bool IsPromotable;

public ClusterVNodeSettings(Guid instanceId, int debugIndex,
IPEndPoint internalTcpEndPoint,
IPEndPoint internalSecureTcpEndPoint,
Expand Down Expand Up @@ -149,7 +151,8 @@ public class ClusterVNodeSettings {
int initializationThreads = 1,
bool faultOutOfOrderProjections = false,
bool structuredLog = false,
int maxAutoMergeIndexLevel = 1000) {
int maxAutoMergeIndexLevel = 1000,
bool isPromotable = true) {
Ensure.NotEmptyGuid(instanceId, "instanceId");
Ensure.NotNull(internalTcpEndPoint, "internalTcpEndPoint");
Ensure.NotNull(externalTcpEndPoint, "externalTcpEndPoint");
Expand Down Expand Up @@ -178,7 +181,8 @@ public class ClusterVNodeSettings {
NodeInfo = new VNodeInfo(instanceId, debugIndex,
internalTcpEndPoint, internalSecureTcpEndPoint,
externalTcpEndPoint, externalSecureTcpEndPoint,
internalHttpEndPoint, externalHttpEndPoint);
internalHttpEndPoint, externalHttpEndPoint,
isPromotable);
GossipAdvertiseInfo = gossipAdvertiseInfo;
IntHttpPrefixes = intHttpPrefixes;
ExtHttpPrefixes = extHttpPrefixes;
Expand Down Expand Up @@ -249,6 +253,7 @@ public class ClusterVNodeSettings {
MaxAutoMergeIndexLevel = maxAutoMergeIndexLevel;
FaultOutOfOrderProjections = faultOutOfOrderProjections;
StructuredLog = structuredLog;
IsPromotable = isPromotable;
}


Expand Down
4 changes: 2 additions & 2 deletions src/EventStore.Core/Cluster/VNodeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace EventStore.Core.Cluster {
public static class VNodeInfoHelper {
public static VNodeInfo FromMemberInfo(MemberInfo member) {
public static VNodeInfo FromMemberInfo(MemberInfo member, bool isPromotable) {
return new VNodeInfo(member.InstanceId, 0,
member.InternalTcpEndPoint, member.InternalSecureTcpEndPoint,
member.ExternalTcpEndPoint, member.ExternalSecureTcpEndPoint,
member.InternalHttpEndPoint, member.ExternalHttpEndPoint);
member.InternalHttpEndPoint, member.ExternalHttpEndPoint, isPromotable);
}
}
}
3 changes: 2 additions & 1 deletion src/EventStore.Core/ClusterVNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,8 @@ public class ClusterVNode :
vNodeSettings.GossipAdvertiseInfo.ExternalTcp,
vNodeSettings.GossipAdvertiseInfo.ExternalSecureTcp,
vNodeSettings.GossipAdvertiseInfo.InternalHttp,
vNodeSettings.GossipAdvertiseInfo.ExternalHttp);
vNodeSettings.GossipAdvertiseInfo.ExternalHttp,
_nodeInfo.IsPromotable);
if (!isSingleNode) {
// MASTER REPLICATION
var masterReplicationService = new MasterReplicationService(_mainQueue, gossipInfo.InstanceId, db,
Expand Down
4 changes: 3 additions & 1 deletion src/EventStore.Core/Data/VNodeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ public class VNodeInfo {
public readonly IPEndPoint ExternalSecureTcp;
public readonly IPEndPoint InternalHttp;
public readonly IPEndPoint ExternalHttp;
public readonly bool IsPromotable;

public VNodeInfo(Guid instanceId, int debugIndex,
IPEndPoint internalTcp, IPEndPoint internalSecureTcp,
IPEndPoint externalTcp, IPEndPoint externalSecureTcp,
IPEndPoint internalHttp, IPEndPoint externalHttp) {
IPEndPoint internalHttp, IPEndPoint externalHttp, bool isPromotable) {
Ensure.NotEmptyGuid(instanceId, "instanceId");
Ensure.NotNull(internalTcp, "internalTcp");
Ensure.NotNull(externalTcp, "externalTcp");
Expand All @@ -31,6 +32,7 @@ public class VNodeInfo {
ExternalSecureTcp = externalSecureTcp;
InternalHttp = internalHttp;
ExternalHttp = externalHttp;
IsPromotable = isPromotable;
}

public bool Is(IPEndPoint endPoint) {
Expand Down
6 changes: 4 additions & 2 deletions src/EventStore.Core/Data/VNodeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ public enum VNodeState {
Master,
Manager,
ShuttingDown,
Shutdown
Shutdown,
NonPromotableClone
}

public static class VNodeStateExtensions {
public static bool IsReplica(this VNodeState state) {
return state == VNodeState.CatchingUp
|| state == VNodeState.Clone
|| state == VNodeState.Slave;
|| state == VNodeState.Slave
|| state == VNodeState.NonPromotableClone;
}
}
}