public void Send(JsonOperationContext context, InstallSnapshot installSnapshot) { Send(context, new DynamicJsonValue { ["Type"] = nameof(InstallSnapshot), [nameof(InstallSnapshot.LastIncludedIndex)] = installSnapshot.LastIncludedIndex, [nameof(InstallSnapshot.LastIncludedTerm)] = installSnapshot.LastIncludedTerm, [nameof(InstallSnapshot.Topology)] = installSnapshot.Topology }); }
public void Send(JsonOperationContext context, InstallSnapshot installSnapshot) { if (_log.IsInfoEnabled) { _log.Info($"Install snapshot on: ({installSnapshot.LastIncludedIndex} / {installSnapshot.LastIncludedTerm})"); } Send(context, new DynamicJsonValue { ["Type"] = nameof(InstallSnapshot), [nameof(InstallSnapshot.LastIncludedIndex)] = installSnapshot.LastIncludedIndex, [nameof(InstallSnapshot.LastIncludedTerm)] = installSnapshot.LastIncludedTerm, [nameof(InstallSnapshot.Topology)] = installSnapshot.Topology }); }
private void ReadAndCommitSnapshot(ClusterOperationContext context, InstallSnapshot snapshot, CancellationToken token) { using (context.OpenWriteTransaction()) { var lastTerm = _engine.GetTermFor(context, snapshot.LastIncludedIndex); var lastCommitIndex = _engine.GetLastEntryIndex(context); if (_engine.GetSnapshotRequest(context) == false && snapshot.LastIncludedTerm == lastTerm && snapshot.LastIncludedIndex < lastCommitIndex) { if (_engine.Log.IsInfoEnabled) { _engine.Log.Info( $"{ToString()}: Got installed snapshot with last index={snapshot.LastIncludedIndex} while our lastCommitIndex={lastCommitIndex}, will just ignore it"); } //This is okay to ignore because we will just get the committed entries again and skip them ReadInstallSnapshotAndIgnoreContent(token); } else if (InstallSnapshot(context, token)) { if (_engine.Log.IsInfoEnabled) { _engine.Log.Info( $"{ToString()}: Installed snapshot with last index={snapshot.LastIncludedIndex} with LastIncludedTerm={snapshot.LastIncludedTerm} "); } _engine.SetLastCommitIndex(context, snapshot.LastIncludedIndex, snapshot.LastIncludedTerm); _engine.ClearLogEntriesAndSetLastTruncate(context, snapshot.LastIncludedIndex, snapshot.LastIncludedTerm); } else { var lastEntryIndex = _engine.GetLastEntryIndex(context); if (lastEntryIndex < snapshot.LastIncludedIndex) { var message = $"The snapshot installation had failed because the last included index {snapshot.LastIncludedIndex} in term {snapshot.LastIncludedTerm} doesn't match the last entry {lastEntryIndex}"; if (_engine.Log.IsInfoEnabled) { _engine.Log.Info($"{ToString()}: {message}"); } throw new InvalidOperationException(message); } } // snapshot always has the latest topology if (snapshot.Topology == null) { const string message = "Expected to get topology on snapshot"; if (_engine.Log.IsInfoEnabled) { _engine.Log.Info($"{ToString()}: {message}"); } throw new InvalidOperationException(message); } using (var topologyJson = context.ReadObject(snapshot.Topology, "topology")) { if (_engine.Log.IsInfoEnabled) { _engine.Log.Info($"{ToString()}: topology on install snapshot: {topologyJson}"); } var topology = JsonDeserializationRachis <ClusterTopology> .Deserialize(topologyJson); RachisConsensus.SetTopology(_engine, context, topology); } _engine.SetSnapshotRequest(context, false); context.Transaction.InnerTransaction.LowLevelTransaction.OnDispose += t => { if (t is LowLevelTransaction llt && llt.Committed) { // we might have moved from passive node, so we need to start the timeout clock _engine.Timeout.Start(_engine.SwitchToCandidateStateOnTimeout); } }; context.Transaction.Commit(); } }