private void OnTakeSnapshot(long termPosition) { long recordingId; string channel = ctx.SnapshotChannel(); int streamId = ctx.SnapshotStreamId(); using (AeronArchive archive = AeronArchive.Connect(archiveCtx)) using (Publication publication = archive.AddRecordedExclusivePublication(channel, streamId)) { try { CountersReader counters = aeron.CountersReader(); int counterId = AwaitRecordingCounter(publication, counters); recordingId = RecordingPos.GetRecordingId(counters, counterId); SnapshotState(publication, termBaseLogPosition + termPosition); service.OnTakeSnapshot(publication); AwaitRecordingComplete(recordingId, publication.Position, counters, counterId, archive); } finally { archive.StopRecording(publication); } } recordingLog.AppendSnapshot(recordingId, leadershipTermId, termBaseLogPosition, termPosition, timestampMs); }
/// <summary> /// Create a <seealso cref="ReplayMerge"/> to manage the merging of a replayed stream and switching to live stream as /// appropriate. /// </summary> /// <param name="subscription"> to use for the replay and live stream. Must be a multi-destination subscription. </param> /// <param name="archive"> to use for the replay. </param> /// <param name="replayChannel"> to use for the replay. </param> /// <param name="replayDestination"> to send the replay to and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="liveDestination"> for the live stream and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="recordingId"> for the replay. </param> /// <param name="startPosition"> for the replay. </param> public ReplayMerge(Subscription subscription, AeronArchive archive, string replayChannel, string replayDestination, string liveDestination, long recordingId, long startPosition) { var subscriptionChannelUri = ChannelUri.Parse(subscription.Channel); if (!Context.MDC_CONTROL_MODE_MANUAL.Equals(subscriptionChannelUri.Get(Context.MDC_CONTROL_MODE_PARAM_NAME)) ) { throw new ArgumentException("Subscription channel must be manual control mode: mode=" + subscriptionChannelUri.Get(Context.MDC_CONTROL_MODE_PARAM_NAME) ); } this.archive = archive; this.subscription = subscription; this.replayDestination = replayDestination; this.replayChannel = replayChannel; this.liveDestination = liveDestination; this.recordingId = recordingId; this.startPosition = startPosition; this.liveAddThreshold = LIVE_ADD_THRESHOLD; this.replayRemoveThreshold = REPLAY_REMOVE_THRESHOLD; subscription.AddDestination(replayDestination); }
private static void GetRecordingExtent(AeronArchive archive, RecordingExtent recordingExtent, Entry entry) { if (archive.ListRecording(entry.recordingId, recordingExtent) == 0) { throw new InvalidOperationException("Unknown recording id: " + entry.recordingId); } }
private long OnTakeSnapshot(long logPosition, long leadershipTermId) { long recordingId; using (AeronArchive archive = AeronArchive.Connect(archiveCtx)) using (Publication publication = aeron.AddExclusivePublication(ctx.SnapshotChannel(), ctx.SnapshotStreamId())) { var channel = ChannelUri.AddSessionId(ctx.SnapshotChannel(), publication.SessionId); long subscriptionId = archive.StartRecording(channel, ctx.SnapshotStreamId(), SourceLocation.LOCAL); try { CountersReader counters = aeron.CountersReader(); int counterId = AwaitRecordingCounter(publication.SessionId, counters); recordingId = RecordingPos.GetRecordingId(counters, counterId); SnapshotState(publication, logPosition, leadershipTermId); service.OnTakeSnapshot(publication); AwaitRecordingComplete(recordingId, publication.Position, counters, counterId, archive); } finally { archive.StopRecording(subscriptionId); } } return(recordingId); }
/// <summary> /// Create a recovery plan for the cluster that when the steps are replayed will bring the cluster back to the /// latest stable state. /// </summary> /// <param name="archive"> to lookup recording descriptors. </param> /// <returns> a new <seealso cref="RecoveryPlan"/> for the cluster. </returns> public virtual RecoveryPlan CreateRecoveryPlan(AeronArchive archive) { var steps = new List <ReplayStep>(); var snapshotStep = PlanRecovery(steps, entries, archive); long lastLeadershipTermId = -1; long lastLogPosition = 0; long lastTermPositionCommitted = -1; long lastTermPositionAppended = 0; if (null != snapshotStep) { lastLeadershipTermId = snapshotStep.entry.leadershipTermId; lastLogPosition = snapshotStep.entry.logPosition; lastTermPositionCommitted = snapshotStep.entry.termPosition; lastTermPositionAppended = lastTermPositionCommitted; } int size = steps.Count; if (size > 0) { ReplayStep replayStep = steps[size - 1]; Entry entry = replayStep.entry; lastLeadershipTermId = entry.leadershipTermId; lastLogPosition = entry.logPosition; lastTermPositionCommitted = entry.termPosition; lastTermPositionAppended = replayStep.recordingStopPosition; } return(new RecoveryPlan(lastLeadershipTermId, lastLogPosition, lastTermPositionCommitted, lastTermPositionAppended, snapshotStep, steps)); }
private void LoadSnapshot(long recordingId) { using (AeronArchive archive = AeronArchive.Connect(archiveCtx)) { RecordingExtent recordingExtent = new RecordingExtent(); if (0 == archive.ListRecording(recordingId, recordingExtent)) { throw new System.InvalidOperationException("Could not find recordingId: " + recordingId); } string channel = ctx.ReplayChannel(); int streamId = ctx.ReplayStreamId(); long length = recordingExtent.stopPosition - recordingExtent.startPosition; int sessionId = (int)archive.StartReplay(recordingId, 0, length, channel, streamId); string replaySessionChannel = ChannelUri.AddSessionId(channel, sessionId); using (Subscription subscription = aeron.AddSubscription(replaySessionChannel, streamId)) { Image image = AwaitImage(sessionId, subscription); LoadState(image); service.OnLoadSnapshot(image); } } }
private void LoadSnapshot(long recordingId) { using (AeronArchive archive = AeronArchive.Connect(archiveCtx)) { string channel = ctx.ReplayChannel(); int streamId = ctx.ReplayStreamId(); int sessionId = (int)archive.StartReplay(recordingId, 0, AeronArchive.NULL_LENGTH, channel, streamId); string replaySessionChannel = ChannelUri.AddSessionId(channel, sessionId); using (Subscription subscription = aeron.AddSubscription(replaySessionChannel, streamId)) { Image image = AwaitImage(sessionId, subscription); LoadState(image); service.OnLoadSnapshot(image); } } }
private static bool PollForResponse(AeronArchive archive, long correlationId) { ControlResponsePoller poller = archive.ControlResponsePoller(); if (poller.Poll() > 0 && poller.IsPollComplete()) { if (poller.ControlSessionId() == archive.ControlSessionId() && poller.CorrelationId() == correlationId) { if (poller.Code() == ControlResponseCode.ERROR) { throw new ArchiveException("archive response for correlationId=" + correlationId + ", error: " + poller.ErrorMessage(), (int)poller.RelevantId()); } return(true); } } return(false); }
private void AwaitRecordingComplete( long recordingId, long completePosition, CountersReader counters, int counterId, AeronArchive archive) { idleStrategy.Reset(); do { idleStrategy.Idle(); CheckInterruptedStatus(); if (!RecordingPos.IsActive(counters, counterId, recordingId)) { throw new InvalidOperationException("recording has stopped unexpectedly: " + recordingId); } archive.CheckForErrorResponse(); } while (counters.GetCounterValue(counterId) < completePosition); }
/// <summary> /// Create a <seealso cref="ReplayMerge"/> to manage the merging of a replayed stream and switching over to live stream as /// appropriate. /// </summary> /// <param name="subscription"> to use for the replay and live stream. Must be a multi-destination subscription. </param> /// <param name="archive"> to use for the replay. </param> /// <param name="replayChannel"> to use for the replay. </param> /// <param name="replayDestination"> to send the replay to and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="liveDestination"> for the live stream and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="recordingId"> for the replay. </param> /// <param name="startPosition"> for the replay. </param> /// <param name="epochClock"> to use for progress checks. </param> /// <param name="mergeProgressTimeoutMs"> to use for progress checks. </param> public ReplayMerge(Subscription subscription, AeronArchive archive, string replayChannel, string replayDestination, string liveDestination, long recordingId, long startPosition, IEpochClock epochClock, long mergeProgressTimeoutMs) { if (subscription.Channel.StartsWith(IPC_CHANNEL) || replayChannel.StartsWith(IPC_CHANNEL) || replayDestination.StartsWith(IPC_CHANNEL) || liveDestination.StartsWith(IPC_CHANNEL)) { throw new System.ArgumentException("IPC merging is not supported"); } ChannelUri subscriptionChannelUri = ChannelUri.Parse(subscription.Channel); if (!MDC_CONTROL_MODE_MANUAL.Equals(subscriptionChannelUri.Get(MDC_CONTROL_MODE_PARAM_NAME))) { throw new ArgumentException("Subscription must have manual control-mode: control-mode=" + subscriptionChannelUri.Get(MDC_CONTROL_MODE_PARAM_NAME)); } ChannelUri replayChannelUri = ChannelUri.Parse(replayChannel); replayChannelUri.Put(LINGER_PARAM_NAME, "0"); replayChannelUri.Put(EOS_PARAM_NAME, "false"); this.archive = archive; this.subscription = subscription; this.epochClock = epochClock; this.replayDestination = replayDestination; this.replayChannel = replayChannelUri.ToString(); this.liveDestination = liveDestination; this.recordingId = recordingId; this.startPosition = startPosition; this.timeOfLastProgressMs = epochClock.Time(); this.mergeProgressTimeoutMs = mergeProgressTimeoutMs; subscription.AsyncAddDestination(replayDestination); }
private static long PolledRelevantId(AeronArchive archive) { return(archive.ControlResponsePoller().RelevantId()); }
internal static ReplayStep PlanRecovery(List <ReplayStep> steps, List <Entry> entries, AeronArchive archive) { if (entries.Count == 0) { return(null); } int snapshotIndex = -1; for (int i = entries.Count - 1; i >= 0; i--) { Entry entry = entries[i]; if (ENTRY_TYPE_SNAPSHOT == entry.type) { snapshotIndex = i; } } ReplayStep snapshotStep; RecordingExtent recordingExtent = new RecordingExtent(); if (-1 != snapshotIndex) { Entry snapshot = entries[snapshotIndex]; GetRecordingExtent(archive, recordingExtent, snapshot); snapshotStep = new ReplayStep(recordingExtent.startPosition, recordingExtent.stopPosition, snapshot); if (snapshotIndex - 1 >= 0) { for (int i = snapshotIndex - 1; i >= 0; i--) { Entry entry = entries[i]; if (ENTRY_TYPE_TERM == entry.type) { GetRecordingExtent(archive, recordingExtent, snapshot); long snapshotPosition = snapshot.logPosition + snapshot.termPosition; if (recordingExtent.stopPosition == AeronArchive.NULL_POSITION || (entry.logPosition + recordingExtent.stopPosition) > snapshotPosition) { steps.Add(new ReplayStep(snapshot.termPosition, recordingExtent.stopPosition, entry)); } break; } } } } else { snapshotStep = null; } for (int i = snapshotIndex + 1, length = entries.Count; i < length; i++) { Entry entry = entries[i]; GetRecordingExtent(archive, recordingExtent, entry); steps.Add(new ReplayStep(recordingExtent.startPosition, recordingExtent.stopPosition, entry)); } return(snapshotStep); }
/// <summary> /// Create a <seealso cref="ReplayMerge"/> to manage the merging of a replayed stream and switching over to live stream as /// appropriate. /// </summary> /// <param name="subscription"> to use for the replay and live stream. Must be a multi-destination subscription. </param> /// <param name="archive"> to use for the replay. </param> /// <param name="replayChannel"> to use for the replay. </param> /// <param name="replayDestination"> to send the replay to and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="liveDestination"> for the live stream and the destination added by the <seealso cref="Subscription"/>. </param> /// <param name="recordingId"> for the replay. </param> /// <param name="startPosition"> for the replay. </param> public ReplayMerge(Subscription subscription, AeronArchive archive, string replayChannel, string replayDestination, string liveDestination, long recordingId, long startPosition) : this(subscription, archive, replayChannel, replayDestination, liveDestination, recordingId, startPosition, archive.Ctx().AeronClient().Ctx.EpochClock(), MERGE_PROGRESS_TIMEOUT_DEFAULT_MS) { }