/// <summary> /// Attempts to merge the database chunks. /// </summary> private void AttemptMerge() { BaseMessageData message = null; _checkForMerge = false; _lock.EnterUpgradeableReadLock(); for (int i = 0; i < _chunks.Count - 1; ++i) { if (_chunks[i].Count + _chunks[i + 1].Count < _maxChunkItemCount / 2 && Equals(_chunks[i].End, _chunks[i + 1].Start)) { _checkForMerge = true; if (_node.Primary == null) { break; } Message canMerge = new Message(_node.Primary, new ChunkManagementRequest(), true); _node.SendDatabaseMessage(canMerge); canMerge.BlockUntilDone(); if (canMerge.Success && ((ChunkManagementResponse)canMerge.Response.Data).Result) { _lock.EnterWriteLock(); // Merging two chunks, do the merge and then alert the primary controller before doing anything else. _chunks[i].Merge(_chunks[i + 1]); _chunks.RemoveAt(i + 1); message = new ChunkMerge(_chunks[i].Start, _chunks[i].End); _lock.ExitWriteLock(); } break; } } _lock.ExitUpgradeableReadLock(); if (message != null) { Message sentMessage = new Message(_node.Primary, message, true); _node.SendDatabaseMessage(sentMessage); sentMessage.BlockUntilDone(); } }
/// <inheritdoc /> protected override void MessageReceived(Message message) { if (Equals(message.Address, Primary)) { _lastPrimaryMessageId = Math.Max(_lastPrimaryMessageId, message.ID); } if (message.Data is JoinAttempt) { HandleJoinAttemptMessage(message, (JoinAttempt)message.Data); } else if (message.Data is VotingRequest) { if (Primary != null) { SendMessage(new Message(message, new VotingResponse(false), false)); } else { uint max = 0; List <Tuple <NodeDefinition, uint> > votingIds = new List <Tuple <NodeDefinition, uint> >(); foreach (var def in _controllerNodes) { if (Equals(def, Self)) { continue; } Message idRequest = new Message(def, new LastPrimaryMessageIdRequest(), true); SendMessage(idRequest); idRequest.BlockUntilDone(); if (idRequest.Success) { uint votingId = ((LastPrimaryMessageIdResponse)idRequest.Response.Data).LastMessageId; max = Math.Max(max, votingId); votingIds.Add(new Tuple <NodeDefinition, uint>(def, votingId)); } } bool votingResponse = false; if (votingIds.Count > 0) { var top = votingIds.Where(e => e.Item2 == max).OrderBy(e => e.Item1.ConnectionName); if (Equals(top.First().Item1, message.Address)) { votingResponse = true; } } SendMessage(new Message(message, new VotingResponse(votingResponse), false)); } } else if (message.Data is LastPrimaryMessageIdRequest) { SendMessage(new Message(message, new LastPrimaryMessageIdResponse(_lastPrimaryMessageId), false)); } else if (message.Data is PrimaryAnnouncement) { Logger.Log("Setting the primary controller to " + message.Address.ConnectionName, LogLevel.Info); Primary = message.Address; } else if (message.Data is ChunkListUpdate) { lock (_chunkList) { _chunkList.Clear(); _chunkList.AddRange(((ChunkListUpdate)message.Data).ChunkList); } SendMessage(new Message(message, new Acknowledgement(), false)); } else if (message.Data is ChunkSplit) { ChunkSplit splitData = (ChunkSplit)message.Data; lock (_chunkList) { _chunkList.Remove(_chunkList.Find(e => Equals(e.Start, splitData.Start1))); _chunkList.Add(new ChunkDefinition(splitData.Start1, splitData.End1, message.Address)); _chunkList.Add(new ChunkDefinition(splitData.Start2, splitData.End2, message.Address)); } SendMessage(new Message(message, new Acknowledgement(), false)); lock (_balancingLockObject) { _balancing = BalancingState.None; _nodeManagingChunk = null; Logger.Log("Committing chunk split.", LogLevel.Debug); } SendChunkList(); } else if (message.Data is ChunkMerge) { ChunkMerge mergeData = (ChunkMerge)message.Data; lock (_chunkList) { _chunkList.Remove(_chunkList.Find(e => Equals(e.Start, mergeData.Start))); _chunkList.Remove(_chunkList.Find(e => Equals(e.End, mergeData.End))); _chunkList.Add(new ChunkDefinition(mergeData.Start, mergeData.End, message.Address)); } SendMessage(new Message(message, new Acknowledgement(), false)); lock (_balancingLockObject) { _balancing = BalancingState.None; _nodeManagingChunk = null; Logger.Log("Committing chunk merge.", LogLevel.Debug); } SendChunkList(); } else if (message.Data is ChunkManagementRequest) { bool success = false; lock (_balancingLockObject) { if (_balancing == BalancingState.None) { _balancing = BalancingState.ChunkManagement; success = true; _nodeManagingChunk = message.Address; Logger.Log("Granting chunk management request.", LogLevel.Debug); } } SendMessage(new Message(message, new ChunkManagementResponse(success), false)); } else if (message.Data is ChunkTransferComplete) { if (((ChunkTransferComplete)message.Data).Success) { lock (_chunkList) { int index = _chunkList.IndexOf(_chunkBeingTransfered); _chunkList[index] = new ChunkDefinition(_chunkBeingTransfered.Start, _chunkBeingTransfered.End, message.Address); } Logger.Log("Chunk transfer completed successfully.", LogLevel.Info); SendChunkList(); BalanceChunks(); } else { Logger.Log("Chunk transfer failed.", LogLevel.Info); lock (_balancingLockObject) { _nodeTransferingChunk = null; _chunkBeingTransfered = null; _balancing = BalancingState.None; } } } }