Esempio n. 1
0
		public void Stream(NodeConnectionInfo dest, InstallSnapshotRequest req, Action<Stream> streamWriter)
		{
			HttpClient client;
			using (GetConnection(dest, out client))
			{
				LogStatus("install snapshot to " + dest, async () =>
				{
					var requestUri =
						string.Format("raft/installSnapshot?term={0}&=lastIncludedIndex={1}&lastIncludedTerm={2}&from={3}&topology={4}&clusterTopologyId={5}",
							req.Term, req.LastIncludedIndex, req.LastIncludedTerm, req.From, Uri.EscapeDataString(JsonConvert.SerializeObject(req.Topology)), req.ClusterTopologyId);
					var httpResponseMessage = await client.PostAsync(requestUri, new SnapshotContent(streamWriter));
					var reply = await httpResponseMessage.Content.ReadAsStringAsync();
					if (httpResponseMessage.IsSuccessStatusCode == false && httpResponseMessage.StatusCode != HttpStatusCode.NotAcceptable)
					{
						_log.Warn("Error installing snapshot to {0}. Status: {1}\r\n{2}", dest.Name, httpResponseMessage.StatusCode, reply);
						return;
					}
					var installSnapshotResponse = JsonConvert.DeserializeObject<InstallSnapshotResponse>(reply);
					SendToSelf(installSnapshotResponse);
				});
			}
		}
Esempio n. 2
0
		public void Stream(NodeConnectionInfo dest, InstallSnapshotRequest req, Action<Stream> streamWriter)
		{
			_sender.Stream(dest, req, streamWriter);
		}
		public virtual InstallSnapshotResponse Handle(MessageContext context, InstallSnapshotRequest req, Stream stream)
		{
            if (FromOurTopology(req) == false)
            {
                _log.Info("Got an install snapshot message outside my cluster topology (id: {0}), ignoring", req.ClusterTopologyId);
                return new InstallSnapshotResponse
                {
                    Success = false,
                    Message = "Cannot accept message from outside my topology. My cluster topology id is: " + Engine.CurrentTopology.TopologyId,
                    CurrentTerm = Engine.PersistentState.CurrentTerm,
                    From = Engine.Name,
                    ClusterTopologyId = Engine.CurrentTopology.TopologyId,
                    LastLogIndex = Engine.PersistentState.LastLogEntry().Index
                };
            }
			
			stream.Dispose();
			return new InstallSnapshotResponse
			{
				Success = false,
				Message = "Cannot install snapshot from state " + State,
				CurrentTerm = Engine.PersistentState.CurrentTerm,
				From = Engine.Name,
				ClusterTopologyId = Engine.CurrentTopology.TopologyId,
				LastLogIndex = Engine.PersistentState.LastLogEntry().Index
			};
		}
		public override InstallSnapshotResponse Handle(MessageContext context, InstallSnapshotRequest req, Stream stream)
		{
			if (_installingSnapshot != null)
			{
                return new InstallSnapshotResponse
                {
                    Success = false,
                    Message = "Cannot install snapshot because we are already installing a snapshot",
                    CurrentTerm = Engine.PersistentState.CurrentTerm,
                    From = Engine.Name,
                    ClusterTopologyId = Engine.CurrentTopology.TopologyId,
                    LastLogIndex = Engine.PersistentState.LastLogEntry().Index
                };
			}


            if (FromOurTopology(req) == false)
            {
                _log.Info("Got an install snapshot message outside my cluster topology (id: {0}), ignoring", req.ClusterTopologyId);

                return new InstallSnapshotResponse
                {
                    Success = false,
                    Message = "Cannot install snapshot because the cluster topology id doesn't match, mine is: " + Engine.CurrentTopology.TopologyId,
                    CurrentTerm = Engine.PersistentState.CurrentTerm,
                    From = Engine.Name,
                    ClusterTopologyId = Engine.CurrentTopology.TopologyId,
                    LastLogIndex = Engine.PersistentState.LastLogEntry().Index
                };
            }

            var lastLogEntry = Engine.PersistentState.LastLogEntry();
            if (req.Term < lastLogEntry.Term || req.LastIncludedIndex < lastLogEntry.Index)
			{
				stream.Dispose();

				return new InstallSnapshotResponse
				{
					From = Engine.Name,
					ClusterTopologyId = Engine.CurrentTopology.TopologyId,
					CurrentTerm = lastLogEntry.Term,
					LastLogIndex = lastLogEntry.Index,
					Message = string.Format("Snapshot is too old (term {0} index {1}) while we have (term {2} index {3})",
						req.Term, req.LastIncludedIndex, lastLogEntry.Term, lastLogEntry.Index),
					Success = false
				};
			}

			_log.Info("Received InstallSnapshotRequest from {0} until term {1} / {2}", req.From, req.LastIncludedTerm, req.LastIncludedIndex);

			Engine.OnSnapshotInstallationStarted();
			
			// this can be a long running task
			_installingSnapshot = Task.Run(() =>
			{
				try
				{
					Engine.StateMachine.ApplySnapshot(req.LastIncludedTerm, req.LastIncludedIndex, stream);
					Engine.PersistentState.MarkSnapshotFor(req.LastIncludedIndex, req.LastIncludedTerm, int.MaxValue);
					Engine.PersistentState.SetCurrentTopology(req.Topology, req.LastIncludedIndex);
					var tcc = new TopologyChangeCommand { Requested = req.Topology };
					Engine.StartTopologyChange(tcc);
					Engine.CommitTopologyChange(tcc);
				}
				catch (Exception e)
				{
					_log.Warn(string.Format("Failed to install snapshot term {0} index {1}", req.LastIncludedIndex, req.LastIncludedIndex), e);
					context.ExecuteInEventLoop(() =>
					{
						_installingSnapshot = null;
					});
				}

				// we are doing it this way to ensure that we are single threaded
				context.ExecuteInEventLoop(() =>
				{
					Engine.UpdateCurrentTerm(req.Term, req.From); // implicitly put us in follower state
					_log.Info("Updating the commit index to the snapshot last included index of {0}", req.LastIncludedIndex);
					Engine.OnSnapshotInstallationEnded(req.Term);

					context.Reply(new InstallSnapshotResponse
					{
						From = Engine.Name,
						ClusterTopologyId = Engine.CurrentTopology.TopologyId,
						CurrentTerm = req.Term,
						LastLogIndex = req.LastIncludedIndex,
						Success = true
					});
				});
			});

			return null;
		}