/// <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 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); }
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); } } }
/// <summary> /// To be called when a new leader event is delivered. This method needs to be called when using the /// <seealso cref="EgressAdapter"/> or <seealso cref="EgressPoller"/> rather than <seealso cref="#pollEgress()"/> method. /// </summary> /// <param name="clusterSessionId"> which must match <seealso cref="#clusterSessionId()"/>. </param> /// <param name="leaderMemberId"> which has become the new leader. </param> /// <param name="memberEndpoints"> comma separated list of cluster members endpoints to connect to with the leader first. </param> public void OnNewLeader(long clusterSessionId, int leaderMemberId, string memberEndpoints) { if (clusterSessionId != _clusterSessionId) { throw new ClusterException("invalid clusterSessionId=" + clusterSessionId + " expected " + _clusterSessionId); } _leaderMemberId = leaderMemberId; if (_isUnicast) { _publication?.Dispose(); _fragmentAssembler.Clear(); UpdateMemberEndpoints(memberEndpoints); ChannelUri channelUri = ChannelUri.Parse(_ctx.IngressChannel()); channelUri.Put(Aeron.Aeron.Context.ENDPOINT_PARAM_NAME, _endpointByMemberIdMap[leaderMemberId]); _publication = AddIngressPublication(channelUri.ToString(), _ctx.IngressStreamId()); } }
/// <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 Publication ConnectToCluster() { Publication publication = null; string ingressChannel = _ctx.IngressChannel(); int ingressStreamId = _ctx.IngressStreamId(); long deadlineNs = _nanoClock.NanoTime() + _ctx.MessageTimeoutNs(); if (_isUnicast) { ChannelUri channelUri = ChannelUri.Parse(ingressChannel); int memberCount = _endpointByMemberIdMap.Count; Publication[] publications = new Publication[memberCount]; foreach (var entry in _endpointByMemberIdMap) { channelUri.Put(Aeron.Aeron.Context.ENDPOINT_PARAM_NAME, entry.Value); string channel = channelUri.ToString(); publications[entry.Key] = AddIngressPublication(channel, ingressStreamId); } int connectedIndex = -1; while (true) { for (int i = 0; i < memberCount; i++) { if (publications[i].IsConnected) { connectedIndex = i; break; } } if (-1 != connectedIndex) { for (int i = 0; i < memberCount; i++) { if (i == connectedIndex) { publication = publications[i]; } else { publications[i]?.Dispose(); } } break; } if (_nanoClock.NanoTime() > deadlineNs) { for (int i = 0; i < memberCount; i++) { CloseHelper.QuietDispose(publications[i]); } throw new TimeoutException("awaiting connection to cluster"); } _idleStrategy.Idle(); } } else { publication = AddIngressPublication(ingressChannel, ingressStreamId); _idleStrategy.Reset(); while (!publication.IsConnected) { if (_nanoClock.NanoTime() > deadlineNs) { CloseHelper.QuietDispose(publication); throw new TimeoutException("awaiting connection to cluster"); } _idleStrategy.Idle(); } } return(publication); }