diff --git a/.nuget/packages.config b/.nuget/packages.config index da7ace8042..a6e460ed07 100644 --- a/.nuget/packages.config +++ b/.nuget/packages.config @@ -1,6 +1,6 @@  - + - + \ No newline at end of file diff --git a/Hazelcast.Net/Hazelcast.Client.Proxy/ClientMapProxy.cs b/Hazelcast.Net/Hazelcast.Client.Proxy/ClientMapProxy.cs index ecb2dc4eaa..98efae7573 100644 --- a/Hazelcast.Net/Hazelcast.Client.Proxy/ClientMapProxy.cs +++ b/Hazelcast.Net/Hazelcast.Client.Proxy/ClientMapProxy.cs @@ -702,7 +702,7 @@ private ArrayList GetPartitionKeyData(ICollection keys) var partitionService = GetContext().GetPartitionService(); var partitionCount = partitionService.GetPartitionCount(); var initialCapacity = 2 * (keys.Count / partitionCount); - var partitionToKeyData = new ArrayList(partitionCount); + var partitionToKeyData = ArrayList.Synchronized(new ArrayList(partitionCount)); for (var i = 0; i < partitionCount; i++) { partitionToKeyData.Add(ArrayList.Synchronized(new ArrayList(initialCapacity))); @@ -765,9 +765,9 @@ public void PutAll(IDictionary m) var partitions = new ArrayList(partitionCount); for (var i = 0; i < partitionCount; i++) { - partitions.Add(ArrayList.Synchronized(new ArrayList())); + partitions.Add(new ArrayList()); } - Parallel.ForEach(m, kvp => + foreach (var kvp in m) { ValidationUtil.CheckNotNull(kvp.Key, ValidationUtil.NULL_KEY_IS_NOT_ALLOWED); ValidationUtil.CheckNotNull(kvp.Value, ValidationUtil.NULL_VALUE_IS_NOT_ALLOWED); @@ -777,7 +777,7 @@ public void PutAll(IDictionary m) var partition = (ArrayList) partitions[partitionId]; partition.Add(keyData); partition.Add(valueData); - }); + } PutAllInternal(m, partitions); } diff --git a/Hazelcast.Net/Hazelcast.Client.Spi/ClientClusterService.cs b/Hazelcast.Net/Hazelcast.Client.Spi/ClientClusterService.cs index 1e9cc39a33..63d2e6a27b 100644 --- a/Hazelcast.Net/Hazelcast.Client.Spi/ClientClusterService.cs +++ b/Hazelcast.Net/Hazelcast.Client.Spi/ClientClusterService.cs @@ -48,6 +48,8 @@ internal class ClientClusterService : IClientClusterService, IConnectionListener private readonly AtomicReference> _membersRef = new AtomicReference>(); + private readonly object _initialMembershipListenerMutex = new object(); + private ClientMembershipListener _clientMembershipListener; private ClientConnectionManager _connectionManager; private Address _ownerConnectionAddress; @@ -58,6 +60,7 @@ internal class ClientClusterService : IClientClusterService, IConnectionListener public ClientClusterService(HazelcastClient client) { + _membersRef.Set(new Dictionary()); _client = client; var networkConfig = GetClientConfig().GetNetworkConfig(); @@ -92,6 +95,7 @@ public ClientClusterService(HazelcastClient client) AddMembershipListenerWithoutInit(membershipListener); } } + } private Address OwnerConnectionAddress @@ -161,15 +165,13 @@ public string AddMembershipListener(IMembershipListener listener) { throw new ArgumentNullException("listener"); } - var id = Guid.NewGuid().ToString(); - _listeners[id] = listener; - if (listener is IInitialMembershipListener) + + lock (_initialMembershipListenerMutex) { - // TODO: needs sync with membership events... - var cluster = _client.GetCluster(); - ((IInitialMembershipListener) listener).Init(new InitialMembershipEvent(cluster, cluster.GetMembers())); + var id = AddMembershipListenerWithoutInit(listener); + InitMembershipListener(listener); + return id; } - return id; } public bool RemoveMembershipListener(string registrationId) @@ -215,7 +217,6 @@ public virtual void Start() { Init(); ConnectToCluster(); - InitMembershipListener(); } internal int ServerVersion @@ -232,55 +233,42 @@ internal int ServerVersion } } - internal virtual void FireMemberAttributeEvent(MemberAttributeEvent @event) + internal void FireMemberAttributeEvent(MemberAttributeEvent @event) { - _client.GetClientExecutionService().Submit(() => + foreach (var listener in _listeners.Values) { - foreach (var listener in _listeners.Values) - { - listener.MemberAttributeChanged(@event); - } - }); + listener.MemberAttributeChanged(@event); + } } - internal virtual void FireMembershipEvent(MembershipEvent @event) + private void FireMembershipEvent(MembershipEvent @event) { - _client.GetClientExecutionService().Submit((() => + foreach (var listener in _listeners.Values) { - foreach (var listener in _listeners.Values) + if (@event.GetEventType() == MembershipEvent.MemberAdded) { - if (@event.GetEventType() == MembershipEvent.MemberAdded) - { - listener.MemberAdded(@event); - } - else - { - listener.MemberRemoved(@event); - } + listener.MemberAdded(@event); } - })); - } - - internal virtual IDictionary GetMembersRef() - { - return _membersRef.Get(); + else + { + listener.MemberRemoved(@event); + } + } } - internal virtual string MembersString() - { - var sb = new StringBuilder("\n\nMembers ["); - var members = GetMemberList(); - sb.Append(members != null ? members.Count : 0); - sb.Append("] {"); - if (members != null) + private void FireInitialMembershipEvent(InitialMembershipEvent @event) { + foreach (var listener in _listeners.Values) { - foreach (var member in members) + if (listener is IInitialMembershipListener) { - sb.Append("\n\t").Append(member); + ((IInitialMembershipListener) listener).Init(@event); } } - sb.Append("\n}\n"); - return sb.ToString(); + } + + internal virtual IDictionary GetMembersRef() + { + return _membersRef.Get(); } internal virtual void SetMembersRef(IDictionary map) @@ -288,10 +276,11 @@ internal virtual void SetMembersRef(IDictionary map) _membersRef.Set(map); } - private void AddMembershipListenerWithoutInit(IMembershipListener listener) + private string AddMembershipListenerWithoutInit(IMembershipListener listener) { var id = Guid.NewGuid().ToString(); - _listeners[id] = listener; + _listeners.TryAdd(id, listener); + return id; } /// @@ -426,18 +415,53 @@ private void Init() _connectionManager.AddConnectionListener(this); } - private void InitMembershipListener() + private void InitMembershipListener(IMembershipListener listener) { + if (listener is IInitialMembershipListener) { + var cluster = _client.GetCluster(); + var memberCollection = _membersRef.Get().Values; + if (memberCollection.Count == 0) + { + //if members are empty,it means initial event did not arrive yet + //it will be redirected to listeners when it arrives see #handleInitialMembershipEvent + return; + } + var members = new HashSet(memberCollection); + var @event = new InitialMembershipEvent(cluster, members); + ((IInitialMembershipListener) listener).Init(@event); + } + } + + internal void HandleInitialMembershipEvent(InitialMembershipEvent @event) { - foreach (var membershipListener in _listeners.Values) + lock (_initialMembershipListenerMutex) { - if (membershipListener is IInitialMembershipListener) + var initialMembers = @event.GetMembers(); + var newMap = new Dictionary(); + foreach (var initialMember in initialMembers) { - // TODO: needs sync with membership events... - var cluster = _client.GetCluster(); - var @event = new InitialMembershipEvent(cluster, cluster.GetMembers()); - ((IInitialMembershipListener) membershipListener).Init(@event); + newMap.Add(initialMember.GetAddress(), initialMember); + } + _membersRef.Set(newMap); + FireInitialMembershipEvent(@event); + } + } + + internal void HandleMembershipEvent(MembershipEvent @event) + { + lock (_initialMembershipListenerMutex) + { + var member = @event.GetMember(); + var dictionary = _membersRef.Get(); + var newMap = new Dictionary(dictionary); + if (@event.GetEventType() == MembershipEvent.MemberAdded) { + newMap.Add(member.GetAddress(), member); + } else { + newMap.Remove(member.GetAddress()); } + _membersRef.Set(newMap); + FireMembershipEvent(@event); } } + } } \ No newline at end of file diff --git a/Hazelcast.Net/Hazelcast.Client.Spi/ClientListenerService.cs b/Hazelcast.Net/Hazelcast.Client.Spi/ClientListenerService.cs index 7850653e0c..68e62f2126 100644 --- a/Hazelcast.Net/Hazelcast.Client.Spi/ClientListenerService.cs +++ b/Hazelcast.Net/Hazelcast.Client.Spi/ClientListenerService.cs @@ -129,8 +129,8 @@ public bool DeregisterListener(string userRegistrationId) private bool DeregisterListenerInternal(string userRegistrationId) { - //This method should not be called from registrationExecutor - Debug.Assert(Thread.CurrentThread.Name == null || !Thread.CurrentThread.Name.Contains("eventRegistration")); + //This method should only be called from registrationExecutor + Debug.Assert(Thread.CurrentThread.Name != null && Thread.CurrentThread.Name.Contains("eventRegistration")); ListenerRegistration listenerRegistration; if (!_registrations.TryGetValue(userRegistrationId, out listenerRegistration)) { diff --git a/Hazelcast.Net/Hazelcast.Client.Spi/ClientMembershipListener.cs b/Hazelcast.Net/Hazelcast.Client.Spi/ClientMembershipListener.cs index 1efadeaae6..e54b1daa0e 100644 --- a/Hazelcast.Net/Hazelcast.Client.Spi/ClientMembershipListener.cs +++ b/Hazelcast.Net/Hazelcast.Client.Spi/ClientMembershipListener.cs @@ -13,8 +13,11 @@ // limitations under the License. using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; +using System.Text; using System.Threading; using Hazelcast.Client.Connection; using Hazelcast.Client.Protocol.Codec; @@ -28,12 +31,12 @@ namespace Hazelcast.Client.Spi { internal class ClientMembershipListener { - public const int InitialMembersTimeoutSeconds = 5; + private const int InitialMembersTimeoutSeconds = 5; private static readonly ILogger Logger = Logging.Logger.GetLogger(typeof (ClientMembershipListener)); private readonly HazelcastClient _client; private readonly ClientClusterService _clusterService; private readonly ClientConnectionManager _connectionManager; - private readonly IList _members = new List(); + private readonly ISet _members = new HashSet(); private readonly ClientPartitionService _partitionService; private ManualResetEventSlim _initialListFetched; @@ -49,7 +52,7 @@ public virtual void BeforeListenerRegister() { } - public void HandleMember(IMember member, int eventType) + private void HandleMember(IMember member, int eventType) { switch (eventType) { @@ -71,11 +74,10 @@ public void HandleMember(IMember member, int eventType) break; } } - _partitionService.RefreshPartitions(); } - public void HandleMemberAttributeChange(string uuid, string key, int operationType, string value) + private void HandleMemberAttributeChange(string uuid, string key, int operationType, string value) { var memberMap = _clusterService.GetMembersRef(); if (memberMap == null) @@ -96,39 +98,42 @@ public void HandleMemberAttributeChange(string uuid, string key, int operationTy } } - public void HandleMemberCollection(ICollection initialMembers) + private void HandleMemberCollection(ICollection initialMembers) { - var prevMembers = new Dictionary(); + var prevMembers = new HashSet(); if (_members.Any()) { - prevMembers = new Dictionary(_members.Count); - foreach (var member in _members) - { - prevMembers[member.GetUuid()] = member; - } + prevMembers = new HashSet(_members); _members.Clear(); } foreach (var initialMember in initialMembers) { _members.Add(initialMember); } - - var events = DetectMembershipEvents(prevMembers); - if (events.Count != 0) + + if (prevMembers.Count == 0) { - ApplyMemberListChanges(); + //this means this is the first time client connected to cluster + Logger.Info(MembersString()); + var immutableSetOfMembers = ImmutableSetOfMembers(); + var cluster = _client.GetCluster(); + var initialMembershipEvent = new InitialMembershipEvent(cluster, immutableSetOfMembers); + _clusterService.HandleInitialMembershipEvent(initialMembershipEvent); + _initialListFetched.Set(); + return; } + var events = DetectMembershipEvents(prevMembers); + Logger.Info(MembersString()); FireMembershipEvent(events); _initialListFetched.Set(); } - public virtual void OnListenerRegister() - { - } - - internal virtual void ListenMembershipEvents(Address ownerConnectionAddress) + internal void ListenMembershipEvents(Address ownerConnectionAddress) { - Logger.Finest("Starting to listen for membership events from " + ownerConnectionAddress); + if (Logger.IsFinestEnabled()) + { + Logger.Finest("Starting to listen for membership events from " + ownerConnectionAddress); + } _initialListFetched = new ManualResetEventSlim(); try { @@ -148,7 +153,7 @@ internal virtual void ListenMembershipEvents(Address ownerConnectionAddress) var invocationService = (ClientInvocationService) _client.GetInvocationService(); var future = invocationService.InvokeListenerOnConnection(clientMessage, handler, connection); var response = ThreadUtil.GetResult(future); - //registraiton id is ignored as this listener will never be removed + //registration id is ignored as this listener will never be removed var registirationId = ClientAddMembershipListenerCodec.DecodeResponse(response).response; WaitInitialMemberListFetched(); } @@ -174,94 +179,71 @@ internal virtual void ListenMembershipEvents(Address ownerConnectionAddress) } } - private void ApplyMemberListChanges() - { - UpdateMembersRef(); - Logger.Info(_clusterService.MembersString()); - } - - private IList DetectMembershipEvents(IDictionary prevMembers) - { - IList events = new List(); - var eventMembers = GetMembers(); + private IList DetectMembershipEvents(ISet prevMembers) { + var events = new List(); + + var eventMembers = ImmutableSetOfMembers(); + + var newMembers = new LinkedList(); foreach (var member in _members) { - IMember former; - prevMembers.TryGetValue(member.GetUuid(), out former); - if (former == null) - { - events.Add(new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberAdded, - eventMembers)); - } - else + if (!prevMembers.Remove(member)) { - prevMembers.Remove(member.GetUuid()); + newMembers.AddLast(member); } } - foreach (var member in prevMembers.Values) + // removal events should be added before added events + foreach (var member in prevMembers) { events.Add(new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberRemoved, eventMembers)); var address = member.GetAddress(); if (_clusterService.GetMember(address) == null) { var connection = _connectionManager.GetConnection(address); - if (connection != null) - { + if (connection != null) { _connectionManager.DestroyConnection(connection, new TargetDisconnectedException(address, "member left the cluster.")); } } } + foreach (var member in newMembers) + { + events.Add(new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberAdded, eventMembers)); + } return events; } - private void FireMembershipEvent(IList events) { foreach (var @event in events) { - _clusterService.FireMembershipEvent(@event); + _clusterService.HandleMembershipEvent(@event); } } - private ICollection GetMembers() + private ICollection ImmutableSetOfMembers() { - var set = new HashSet(); - foreach (var member in _members) - { - set.Add(member); - } - return set; + return new ReadOnlyCollection(_members.ToList()); } private void MemberAdded(IMember member) { _members.Add(member); - ApplyMemberListChanges(); - var @event = new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberAdded, GetMembers()); - _clusterService.FireMembershipEvent(@event); + Logger.Info(MembersString()); + var @event = new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberAdded, ImmutableSetOfMembers()); + _clusterService.HandleMembershipEvent(@event); } private void MemberRemoved(IMember member) { _members.Remove(member); - ApplyMemberListChanges(); + Logger.Info(MembersString()); var connection = _connectionManager.GetConnection(member.GetAddress()); if (connection != null) { _connectionManager.DestroyConnection(connection, new TargetDisconnectedException(member.GetAddress(), "member left the cluster.")); } - var @event = new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberRemoved, GetMembers()); - _clusterService.FireMembershipEvent(@event); - } - - private void UpdateMembersRef() - { - IDictionary map = new Dictionary(_members.Count); - foreach (var member in _members) - { - map[member.GetAddress()] = member; - } - _clusterService.SetMembersRef(map); + var @event = new MembershipEvent(_client.GetCluster(), member, MembershipEvent.MemberRemoved, ImmutableSetOfMembers()); + _clusterService.HandleMembershipEvent(@event); } /// @@ -274,5 +256,18 @@ private void WaitInitialMemberListFetched() Logger.Warning("Error while getting initial member list from cluster!"); } } + + private string MembersString() { + var sb = new StringBuilder("\n\nMembers ["); + sb.Append(_members.Count); + sb.Append("] {"); + foreach (var member in _members) + { + sb.Append("\n\t").Append(member); + } + sb.Append("\n}\n"); + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/Hazelcast.Net/Hazelcast.Core/Member.cs b/Hazelcast.Net/Hazelcast.Core/Member.cs index cb8db38938..91b0f38a7d 100644 --- a/Hazelcast.Net/Hazelcast.Core/Member.cs +++ b/Hazelcast.Net/Hazelcast.Core/Member.cs @@ -100,44 +100,24 @@ public string GetAttribute(string key) return _out; } + private bool Equals(Member other) + { + return Equals(_address, other._address) && string.Equals(_uuid, other._uuid); + } + public override bool Equals(object obj) { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (GetType() != obj.GetType()) - { - return false; - } - var other = (Member) obj; - if (_address == null) - { - if (other._address != null) - { - return false; - } - } - else - { - if (!_address.Equals(other._address)) - { - return false; - } - } - return true; + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj is Member && Equals((Member) obj); } public override int GetHashCode() { - var Prime = 31; - var result = 1; - result = Prime*result + ((_address == null) ? 0 : _address.GetHashCode()); - return result; + unchecked + { + return ((_address != null ? _address.GetHashCode() : 0) * 397) ^ (_uuid != null ? _uuid.GetHashCode() : 0); + } } public override string ToString() diff --git a/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterRestartEventTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterRestartEventTest.cs new file mode 100644 index 0000000000..6f83a09c88 --- /dev/null +++ b/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterRestartEventTest.cs @@ -0,0 +1,128 @@ +// Copyright (c) 2008-2019, Hazelcast, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using Hazelcast.Core; +using NUnit.Framework; + +namespace Hazelcast.Client.Test +{ + [TestFixture] + public class ClientClusterRestartEventTest : MultiMemberBaseNoSetupTest + { + [TearDown] + public void Teardown() + { + ShutdownRemoteController(); + } + + [Test] + public void TestSingleMember() + { + string oldMemberUUID = null; + SetupCluster(() => + { + oldMemberUUID = StartNewMember(); + }); + + var memberAdded = new CountdownEvent(1); + var memberRemoved = new CountdownEvent(1); + + string addedMemberReferenceUUID = null; + string removedMemberReferenceUUID = null; + + Client.GetCluster().AddMembershipListener(new MembershipListener + { + OnMemberAdded = membershipEvent => + { + Interlocked.Exchange(ref addedMemberReferenceUUID, membershipEvent.GetMember().GetUuid()); + memberAdded.Signal(); + }, + OnMemberRemoved = membershipEvent => + { + Interlocked.Exchange(ref removedMemberReferenceUUID, membershipEvent.GetMember().GetUuid()); + memberRemoved.Signal(); + } + }); + + ShutdownMember(oldMemberUUID); + var newMemberUUID = StartNewMember(); + + TestSupport.AssertOpenEventually(memberRemoved); + Assert.AreEqual(oldMemberUUID, removedMemberReferenceUUID); + + TestSupport.AssertOpenEventually(memberAdded); + Assert.AreEqual(newMemberUUID, addedMemberReferenceUUID); + + var members = Client.GetCluster().GetMembers(); + Assert.True(members.Any(member => member.GetUuid() == newMemberUUID)); + Assert.AreEqual(1, members.Count); + } + + [Test] + public void TestMultiMember() + { + string oldMemberUUID0 = null; + string oldMemberUUID1 = null; + SetupCluster(() => + { + oldMemberUUID0 = StartNewMember(); + oldMemberUUID1 = StartNewMember(); + }); + + var memberAdded = new CountdownEvent(2); + var memberRemoved = new CountdownEvent(2); + + var addedMemberUUIDs = new ConcurrentBag(); + var removedMemberUUIDs = new ConcurrentBag(); + + Client.GetCluster().AddMembershipListener(new MembershipListener + { + OnMemberAdded = membershipEvent => + { + addedMemberUUIDs.Add(membershipEvent.GetMember().GetUuid()); + memberAdded.Signal(); + }, + OnMemberRemoved = membershipEvent => + { + removedMemberUUIDs.Add(membershipEvent.GetMember().GetUuid()); + memberRemoved.Signal(); + } + }); + while (!ShutdownCluster()) + { + Thread.Sleep(1000); + } + var newMemberUUID0 = StartNewMember(); + var newMemberUUID1 = StartNewMember(); + + TestSupport.AssertOpenEventually(memberRemoved); + Assert.AreEqual(2, removedMemberUUIDs.Count); + Assert.Contains(oldMemberUUID0, removedMemberUUIDs); + Assert.Contains(oldMemberUUID1, removedMemberUUIDs); + + TestSupport.AssertOpenEventually(memberAdded); + Assert.AreEqual(2, addedMemberUUIDs.Count); + Assert.Contains(newMemberUUID0, addedMemberUUIDs); + Assert.Contains(newMemberUUID1, addedMemberUUIDs); + + var members = Client.GetCluster().GetMembers(); + Assert.AreEqual(2, members.Count); + Assert.True(members.Any(member => member.GetUuid() == newMemberUUID0)); + Assert.True(members.Any(member => member.GetUuid() == newMemberUUID1)); + } + } +} \ No newline at end of file diff --git a/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterServiceTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterServiceTest.cs index a7cd809b69..7b6b03a74a 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterServiceTest.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/ClientClusterServiceTest.cs @@ -27,9 +27,12 @@ internal class ClientClusterServiceTest : HazelcastTestSupport private Cluster _cluster; private RemoteController.Client _remoteController; + private InitialMembershipListener _initialMembershipListener; + [SetUp] public void Setup() { + _initialMembershipListener = new InitialMembershipListener(); _remoteController = CreateRemoteController(); _cluster = CreateCluster(_remoteController); StartMember(_remoteController, _cluster); @@ -43,7 +46,13 @@ public void TearDown() _remoteController.shutdownCluster(_cluster.Id); StopRemoteController(_remoteController); } - + + protected override void ConfigureClient(ClientConfig config) + { + base.ConfigureClient(config); + config.AddListenerConfig(new ListenerConfig(_initialMembershipListener)); + } + protected override void ConfigureGroup(ClientConfig config) { config.GetGroupConfig().SetName(_cluster.Id).SetPassword(_cluster.Id); @@ -116,11 +125,26 @@ public void TestInitialMembershipService() Assert.IsNotNull(member); Assert.IsNotNull(listener._membershipEvent.GetCluster()); Assert.IsNotNull(listener._membershipEvent.ToString()); + Assert.AreEqual(1, _initialMembershipListener.counter); + } + + [Test] + public void TestInitialMembershipWithConfig() + { + var members = _initialMembershipListener._membershipEvent.GetMembers(); + Assert.AreEqual(1, members.Count); + + var member = members.FirstOrDefault(); + Assert.IsNotNull(member); + Assert.IsNotNull(_initialMembershipListener._membershipEvent.GetCluster()); + Assert.IsNotNull(_initialMembershipListener._membershipEvent.ToString()); + Assert.AreEqual(1, _initialMembershipListener.counter); } private class InitialMembershipListener : IInitialMembershipListener { public InitialMembershipEvent _membershipEvent; + public long counter = 0; public void MemberAdded(MembershipEvent membershipEvent) { @@ -137,6 +161,7 @@ public void MemberAttributeChanged(MemberAttributeEvent memberAttributeEvent) public void Init(InitialMembershipEvent membershipEvent) { _membershipEvent = membershipEvent; + Interlocked.Increment(ref counter); } } } diff --git a/Hazelcast.Test/Hazelcast.Client.Test/ClientMapTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/ClientMapTest.cs index a634cd94c0..fe54f5709a 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/ClientMapTest.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/ClientMapTest.cs @@ -469,7 +469,7 @@ public virtual void TestGet() } } - [Test] + [Test, Repeat(100)] public virtual void TestGetAllExtreme() { IDictionary mm = new Dictionary(); @@ -486,6 +486,10 @@ public virtual void TestGetAllExtreme() var dictionary = map.GetAll(mm.Keys); Assert.AreEqual(keycount, dictionary.Count); + foreach (var pair in dictionary) + { + Assert.AreEqual(mm[pair.Key] , pair.Value); + } } [Test] diff --git a/Hazelcast.Test/Hazelcast.Client.Test/ClientPNCounterConsistencyLossTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/ClientPNCounterConsistencyLossTest.cs index aaadbfb23a..9fc1f8d318 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/ClientPNCounterConsistencyLossTest.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/ClientPNCounterConsistencyLossTest.cs @@ -57,8 +57,8 @@ public void TearDown() protected override void InitMembers() { //Init 2 members - MemberList.Add(RemoteController.startMember(HzCluster.Id)); - MemberList.Add(RemoteController.startMember(HzCluster.Id)); + StartNewMember(); + StartNewMember(); } protected override void ConfigureClient(ClientConfig config) diff --git a/Hazelcast.Test/Hazelcast.Client.Test/HazelcastTestSupport.cs b/Hazelcast.Test/Hazelcast.Client.Test/HazelcastTestSupport.cs index ae97466068..a953a86c25 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/HazelcastTestSupport.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/HazelcastTestSupport.cs @@ -33,7 +33,7 @@ namespace Hazelcast.Client.Test { public class HazelcastTestSupport { - private readonly ILogger _logger; + protected readonly ILogger _logger; private readonly ConcurrentQueue _unobservedExceptions = new ConcurrentQueue(); @@ -183,9 +183,17 @@ protected Member StartMemberAndWait(IHazelcastInstance client, RemoteController. return member; } - protected virtual void StopCluster(RemoteController.Client remoteController, Cluster cluster) + protected virtual bool StopCluster(RemoteController.Client remoteController, Cluster cluster) { - remoteController.shutdownCluster(cluster.Id); + return remoteController.shutdownCluster(cluster.Id); + } + + protected void ShutdownCluster(RemoteController.Client remoteController, Cluster cluster) + { + while (!StopCluster(remoteController, cluster)) + { + Thread.Sleep(1000); + } } protected virtual void StopMember(RemoteController.Client remoteController, Cluster cluster, Member member) diff --git a/Hazelcast.Test/Hazelcast.Client.Test/MultiMemberBaseNoSetupTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/MultiMemberBaseNoSetupTest.cs index 4062900884..c0939f8433 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/MultiMemberBaseNoSetupTest.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/MultiMemberBaseNoSetupTest.cs @@ -12,10 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.Collections.Concurrent; using Hazelcast.Core; using Hazelcast.Remote; using Hazelcast.Config; using System.Collections.Generic; +using System.Linq; +using Hazelcast.Logging; +using Member = Hazelcast.Remote.Member; namespace Hazelcast.Client.Test { @@ -25,26 +30,31 @@ public class MultiMemberBaseNoSetupTest : HazelcastTestSupport protected HazelcastClient ClientInternal { get; private set; } protected ThreadSafeRemoteController RemoteController { get; private set; } protected Cluster HzCluster { get; private set; } - protected readonly List MemberList = new List(); + private readonly ConcurrentDictionary MemberList = new ConcurrentDictionary(); - public virtual void SetupCluster() + public virtual void SetupCluster(Action initMembers) { RemoteController = (ThreadSafeRemoteController)CreateRemoteController(); HzCluster = CreateCluster(RemoteController, GetServerConfig()); - InitMembers(); + initMembers(); Client = CreateClient(); ClientInternal = ((HazelcastClientProxy)Client).GetClient(); } + public virtual void SetupCluster() + { + SetupCluster(InitMembers); + } public virtual void ShutdownRemoteController() { HazelcastClient.ShutdownAll(); StopRemoteController(RemoteController); + MemberList.Clear(); } protected virtual void InitMembers() { - MemberList.Add(RemoteController.startMember(HzCluster.Id)); + StartNewMember(); } protected virtual string GetServerConfig() @@ -56,5 +66,31 @@ protected override void ConfigureGroup(ClientConfig config) { config.GetGroupConfig().SetName(HzCluster.Id).SetPassword(HzCluster.Id); } + + protected string StartNewMember() + { + var newMember = RemoteController.startMember(HzCluster.Id); + MemberList.TryAdd(newMember.Uuid,newMember); + return newMember.Uuid; + } + + protected void ShutdownMember(string memberUuid) + { + Member member; + if(MemberList.TryRemove(memberUuid, out member)) + { + StopMember(RemoteController, HzCluster, member); + } + } + protected bool ShutdownCluster() + { + _logger.Info(string.Format("Shutting cluster {0}", HzCluster.Id)); + var result = StopCluster(RemoteController, HzCluster); + if (result) + { + MemberList.Clear(); + } + return result; + } } } diff --git a/Hazelcast.Test/Hazelcast.Client.Test/NonSmartRoutingTest.cs b/Hazelcast.Test/Hazelcast.Client.Test/NonSmartRoutingTest.cs index a24d4db0ef..9b457de869 100644 --- a/Hazelcast.Test/Hazelcast.Client.Test/NonSmartRoutingTest.cs +++ b/Hazelcast.Test/Hazelcast.Client.Test/NonSmartRoutingTest.cs @@ -18,7 +18,6 @@ using Hazelcast.Core; using Hazelcast.Remote; using NUnit.Framework; -using Member = Hazelcast.Remote.Member; namespace Hazelcast.Client.Test { @@ -45,7 +44,7 @@ public void Setup() public void TearDown() { HazelcastClient.ShutdownAll(); - StopCluster(_remoteController, _cluster); + ShutdownCluster(_remoteController, _cluster); StopRemoteController(_remoteController); } diff --git a/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheInvalidationTest.cs b/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheInvalidationTest.cs index d1d31728e0..4635b3a166 100644 --- a/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheInvalidationTest.cs +++ b/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheInvalidationTest.cs @@ -28,8 +28,8 @@ public class NearCacheInvalidationTest : NearcacheTestSupport protected override void InitMembers() { //Init 2 members - MemberList.Add(RemoteController.startMember(HzCluster.Id)); - MemberList.Add(RemoteController.startMember(HzCluster.Id)); + StartNewMember(); + StartNewMember(); } protected override string GetServerConfig() diff --git a/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheMetaDataDistortionTest.cs b/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheMetaDataDistortionTest.cs index a65086e159..da39006c93 100644 --- a/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheMetaDataDistortionTest.cs +++ b/Hazelcast.Test/Hazelcast.NearCache.Test/NearCacheMetaDataDistortionTest.cs @@ -35,8 +35,8 @@ public class NearCacheMetaDataDistortionTest : NearcacheTestSupport protected override void InitMembers() { //Init 2 members - MemberList.Add(RemoteController.startMember(HzCluster.Id)); - MemberList.Add(RemoteController.startMember(HzCluster.Id)); + StartNewMember(); + StartNewMember(); } protected override string GetServerConfig() diff --git a/Hazelcast.Test/Hazelcast.Test.csproj b/Hazelcast.Test/Hazelcast.Test.csproj index 37afe2657d..081d20a74f 100644 --- a/Hazelcast.Test/Hazelcast.Test.csproj +++ b/Hazelcast.Test/Hazelcast.Test.csproj @@ -16,14 +16,14 @@ ..\public.snk - - + + ..\packages\ApacheThrift.0.9.3\lib\Thrift.dll - + diff --git a/build.ps1 b/build.ps1 index 841b4d0391..bf44f520d4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -89,7 +89,7 @@ if($enterprise){ $remoteControllerApp = Start-Process -FilePath java -ArgumentList ( "-Dhazelcast.enterprise.license.key=$env:HAZELCAST_ENTERPRISE_KEY","-cp", "$classpath", "com.hazelcast.remotecontroller.Main" ) -RedirectStandardOutput "rc_stdout.log" -RedirectStandardError "rc_stderr.log" -PassThru $testDLL=".\Hazelcast.Test\bin\Release\${targetFramework}\Hazelcast.Test.dll" -$nunitConsolePath=".\packages\NUnit.ConsoleRunner.3.9.0\tools\nunit3-console.exe" +$nunitConsolePath=".\packages\NUnit.ConsoleRunner.3.10.0\tools\nunit3-console.exe" $nunitArgs=@("`"${testDLL}`"", "--labels=All", "--result=console-text.xml;format=nunit2") if($testCategory.Length -gt 0) { @@ -99,7 +99,7 @@ if($testCategory.Length -gt 0) { if (!$netcore) { $nunitArgs += "--framework=v4.0" if($coverage) { - $dotCoverCmd=".\packages\JetBrains.dotCover.CommandLineTools.2018.2.3\tools\dotCover.exe" + $dotCoverCmd=".\packages\JetBrains.dotCover.CommandLineTools.2019.1.2\tools\dotCover.exe" $dotCoverArgs=@("cover", "/Filters=-:Hazelcast.Test", "/TargetWorkingDir=.", "/Output=Coverage.html", "/ReportType=HTML", "/TargetExecutable=${nunitConsolePath}", "/TargetArguments=${nunitArgs}") Write-Host "$dotCoverCmd" $dotCoverArgs & "$dotCoverCmd" $dotCoverArgs diff --git a/start-rc.sh b/start-rc.sh index 9a96ec47e6..c7e7074c19 100644 --- a/start-rc.sh +++ b/start-rc.sh @@ -1,6 +1,6 @@ #!/bin/sh -HZ_VERSION="3.12.1-SNAPSHOT" +HZ_VERSION="3.12.1" HAZELCAST_TEST_VERSION=${HZ_VERSION} HAZELCAST_VERSION=${HZ_VERSION}