public void HappyPath() { ISwimProtocolProvider protocolProvider = new SwimProtocolProvider(null, _output); var loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(new XunitLoggerProvider(_output)); var swimProtocol = new FailureDetection(protocolProvider, _configuration, loggerFactory.CreateLogger <FailureDetection>()); swimProtocol.OnTransitioned((action) => { _output.WriteLine($"{action.Source} => {action.Trigger} => {action.Destination}"); }); swimProtocol.Fire(SwimFailureDetectionTrigger.Ping); Assert.Equal(SwimFailureDetectionState.Pinged, swimProtocol.State); swimProtocol.Fire(SwimFailureDetectionTrigger.PingExpireLive); Assert.Equal(SwimFailureDetectionState.Alive, swimProtocol.State); swimProtocol.Fire(SwimFailureDetectionTrigger.ProtocolExpireLive); Assert.Equal(SwimFailureDetectionState.Expired, swimProtocol.State); swimProtocol.Fire(SwimFailureDetectionTrigger.Reset); Assert.Equal(SwimFailureDetectionState.Idle, swimProtocol.State); }
public async Task <Response> SendAsync(uint?nextHop, Request request, int timeout, int attempts) { comms.Node.SleepCount++; int retryDelay = 1000; TaskCompletionSource <Response> tcs = new TaskCompletionSource <Response>(); bool AckReceived = false; void GetResponse(object sender, ResponseEventArgs e) { if (e.Response.MessageIdentifer == request.MessageIdentifer) { if (request.SourceID != comms.Node.Id) { if (request.AckExpected && e.Response.ResponseCode == Response.ResponseCodes.ACK) { OnResponseReceived -= GetResponse; tcs.SetResult(e.Response); return; } } if (request.ResponseExpected) { if (e.Response.ResponseCode == Response.ResponseCodes.ACK) { AckReceived = true; } else { OnResponseReceived -= GetResponse; tcs.SetResult(e.Response); return; } } else if (request.AckExpected) { OnResponseReceived -= GetResponse; tcs.SetResult(e.Response); return; } } } new Thread(() => { OnResponseReceived += GetResponse; // Attempt to send multiple times, break loop if response for (int i = 0; i < attempts; i++) { if (AckReceived == false) { Send(nextHop, request); Thread.Sleep(timeout / Constants.TimeScale); // Delay and retry with increasing delay if (tcs.Task.IsCompleted == false) { Thread.Sleep(retryDelay / Constants.TimeScale); retryDelay *= 2; } else { break; } } } if (tcs.Task.IsCompleted == false) { Response response = new Response() { SourceID = request.DestinationID, DestinationID = request.SourceID, MessageIdentifer = request.MessageIdentifer, ResponseCode = Response.ResponseCodes.TIMEOUT }; tcs.SetResult(response); } }).Start(); await tcs.Task; // Trigger failure handling if no response after several attempts if (request.GetType() != typeof(DetectFailureRequest) && request.Command != Request.Commands.PING && tcs.Task.Result.ResponseCode == Response.ResponseCodes.TIMEOUT) { FailureDetection.FailureDetected(comms.Node, nextHop); } // Trigger recovery if no response after several attempts and already failure handling and attempted node is not the one to be checked via failure handling else if (request.GetType() == typeof(DetectFailureRequest) && tcs.Task.Result.ResponseCode == Response.ResponseCodes.TIMEOUT && (request as DetectFailureRequest).NodeToCheck != nextHop) { FailureDetection.Recovery(comms.Node, nextHop); } comms.Node.SleepCount--; return(tcs.Task.Result); }
private async void ExecuteRequest(Request request, bool isDependency) { switch (request.Command) { case Request.Commands.GENERATE: PlanGenerator.GeneratePlan(this, request as PlanRequest); break; case Request.Commands.EXECUTE: PlanExecuter.ExecutePlan(this, request as PlanRequest); break; case Request.Commands.HEARTBEAT: Heartbeat.RespondToHeartbeat(this, request); break; case Request.Commands.PING: Ping.RespondToPing(this, request); break; case Request.Commands.DETECTFAILURE: FailureDetection.DetectFailure(this, request as DetectFailureRequest); break; case Request.Commands.DISCOVER: await Discovery.DiscoverAsync(this, request as DiscoveryRequest); break; case Request.Commands.POSITION: PositionResponse response = new PositionResponse(Id, request.SourceID, Response.ResponseCodes.OK, request.MessageIdentifer, Position); CommsModule.Send(request.SourceID, response); break; case Request.Commands.ADDITION: List <uint?> neighbours = CommsModule.Discover(); AdditionRequest addition = (request as AdditionRequest).DeepCopy(); List <ConstellationPlanField> fields = new List <ConstellationPlanField> { new ConstellationPlanField("DeltaV", 0, (x, y) => x.CompareTo(y)) }; addition.plan.Entries.Add(new ConstellationPlanEntry(Id, Position, fields, (x, y) => 1)); ActivePlan = addition.plan; Router.UpdateNetworkMap(addition.plan); NodeAdditionResponse additionResponse = new NodeAdditionResponse(Id, request.SourceID, Response.ResponseCodes.OK, request.MessageIdentifer, Position, neighbours); CommsModule.Send(request.SourceID, additionResponse); break; case Request.Commands.UPDATENETWORKMAP: NetworkUpdateRequest updateRequest = request as NetworkUpdateRequest; //Remove any dead nodes from the networkmap Router.NetworkMap.Entries.RemoveAll(entry => updateRequest.DeadNodes.Contains(entry.ID)); //Remove any dead nodes from neighbour lists foreach (NetworkMapEntry entry in Router.NetworkMap.Entries) { foreach (uint?deadNode in updateRequest.DeadNodes) { entry.Neighbours.Remove(deadNode); } } break; default: throw new NotImplementedException(request.Command.ToString() + " was not implemented."); } }