/// <summary> /// Cstor for InboundProtocolResponseValue /// Validate the payload for valid protocol response and store. /// </summary> /// <param name="inboundStreamMessage"></param> /// <param name="expectedType"></param> internal InboundProtocolResponseValue(InboundBaseStreamWireMessage inboundStreamMessage, StreamWireProtocolMessageKind expectedType) { Diagnostics.Assert( inboundStreamMessage.Kind == expectedType, "Unexpected StreamWireProtocolMessageKind in InboundBaseStreamWireMessage used to construct InboundProtocolResponseValue"); Diagnostics.Assert( inboundStreamMessage.Payload != null, "null ProtocolResponseValue payload in InboundBaseStreamWireMessage used to construct InboundProtocolResponseValue"); Diagnostics.Assert( inboundStreamMessage.Payload.Length == sizeof(int), "payload in InboundBaseStreamWireMessage used to construct InboundProtocolResponseValue contains more than ProtocolResponseValue"); // Store appropriate decoded Protocol Response this.Response = (StreamWireProtocolResponse)BitConverter.ToInt32(inboundStreamMessage.Payload, 0); }
internal InboundOpenStreamName(InboundBaseStreamWireMessage inboundBaseMessage) { Diagnostics.Assert( inboundBaseMessage.Kind == StreamWireProtocolMessageKind.OpenStream, "Wrong kind of InboundBaseStreamWireMessage used to construct InboundOpenStreamName"); Diagnostics.Assert( inboundBaseMessage.Payload != null, "null streamName payload in InboundBaseStreamWireMessage used to construct InboundOpenStreamName"); var encoder = new StringEncoder(); var reader = new BinaryReader(new MemoryStream(inboundBaseMessage.Payload)); this.StreamName = new Uri(encoder.Decode(reader)); Diagnostics.Assert(reader.PeekChar() == -1, "streamName payload in InboundBaseStreamWireMessage contains more than streamName"); }
internal InboundResetPartnerStreamsResponseValue(InboundBaseStreamWireMessage inboundStreamMessage) : base(inboundStreamMessage, StreamWireProtocolMessageKind.ResetPartnerStreamsResponse) { }
internal InboundDeleteStreamResponseValue(InboundBaseStreamWireMessage inboundStreamMessage) : base(inboundStreamMessage, StreamWireProtocolMessageKind.DeleteStreamResponse) { }
/// <summary> /// Handler for messages received on the session, will also deal with exceptions that occur when the session is closed or aborted /// </summary> /// <param name="receiveTask"></param> private void ReceiveContinuation(Task <IOperationData> receiveTask) { // Check for aborted session and other failures if (receiveTask.Exception != null) { var realException = receiveTask.Exception.InnerException; if (realException is InvalidOperationException || realException is OperationCanceledException) { FabricEvents.Events.SessionReceiveFailure("SessionAborted@" + this.traceType, this.SessionId.ToString()); } else { Tracing.WriteExceptionAsError("InboundSessionDriver.ReceiveAsync.Failure", realException, "{0}", this.tracer); Diagnostics.Assert(false, "{0} InboundSessionDriver.ReceiveAsync Unexpected Exception {1}", this.tracer, receiveTask.Exception.GetType()); } } else { var wireMessage = receiveTask.Result; var inboundStreamMessage = new InboundBaseStreamWireMessage(wireMessage); var kind = inboundStreamMessage.Kind; // Check if Stream Manager is available for updates. if (this.streamManager.NotWritable()) { // drop the message FabricEvents.Events.WireMessageReceived( "DroppedNotWritable@" + this.traceType + "@" + this.SessionId.ToString(), inboundStreamMessage.StreamIdentity.ToString(), inboundStreamMessage.MessageSequenceNumber, kind.ToString()); return; } FabricEvents.Events.WireMessageReceived( this.traceType + "@" + this.SessionId.ToString(), inboundStreamMessage.StreamIdentity.ToString(), inboundStreamMessage.MessageSequenceNumber, kind.ToString()); /* * There are two basic approaches to dealing with messages here * If the message is intended for a stream and the stream is not found it means we are in the middle of a restore process * so we simply drop the message and build it into the protocol that it will be retried eventually * * If the message is intended for the stream manager to control restart or to drive stream lifecycle the stream manager * will ensure it is in operating state and if not has a mechanism to wait for that state */ switch (kind) { case StreamWireProtocolMessageKind.ServiceData: this.streamManager.MessageReceived(inboundStreamMessage) .ContinueWith(antecedent => Task.Run(() => this.MessageReceivedContinuation(antecedent, inboundStreamMessage.StreamIdentity))); break; case StreamWireProtocolMessageKind.SequenceAck: this.streamManager.AckReceived(inboundStreamMessage) .ContinueWith(antecedent => Task.Run(() => this.AckReceivedContinuation(antecedent, inboundStreamMessage.StreamIdentity))); break; case StreamWireProtocolMessageKind.OpenStream: this.streamManager.InboundStreamRequested(this.partnerKey, inboundStreamMessage) .ContinueWith(this.InboundStreamRequestedContinuation); break; case StreamWireProtocolMessageKind.CloseStream: this.streamManager.MessageReceived(inboundStreamMessage) .ContinueWith(antecedent => Task.Run(() => this.MessageReceivedContinuation(antecedent, inboundStreamMessage.StreamIdentity))); break; case StreamWireProtocolMessageKind.OpenStreamResponse: this.streamManager.CompleteOpenStreamProtocol(inboundStreamMessage) .ContinueWith(this.CompleteOpenStreamProtocolContinuation); break; case StreamWireProtocolMessageKind.CloseStreamResponse: this.streamManager.CompleteCloseStreamProtocol(inboundStreamMessage) .ContinueWith(this.CompleteCloseStreamProtocolContinuation); break; case StreamWireProtocolMessageKind.ResetPartnerStreams: // The StreamIdentity slot is actually carrying the Guid representing the era of the requester this.streamManager.ResetPartnerStreamsAsync(inboundStreamMessage.StreamIdentity, this.partnerKey) .ContinueWith(this.ResetPartnerStreamsAsyncContinuation); break; case StreamWireProtocolMessageKind.ResetPartnerStreamsResponse: var resetResponseValue = new InboundResetPartnerStreamsResponseValue(inboundStreamMessage); Diagnostics.Assert( resetResponseValue.Response == StreamWireProtocolResponse.ResetPartnerStreamsCompleted, "{0} Unexpected reset partner streams response", this.tracer); // The StreamIdentity slot is actually carrying the Guid representing the era when the ResetPartnerStreams request was sent // We will only process the respone if the request was sent in the current era if (inboundStreamMessage.StreamIdentity == this.streamManager.Era) { this.streamManager.RestartPartnerStreamsAsync(this.partnerKey, this.streamManager.RuntimeResources.OutboundStreams, false) .ContinueWith(this.RestartPartnerStreamsAsyncContinuation); } break; case StreamWireProtocolMessageKind.DeleteStream: this.streamManager.DeleteInboundStreamRequested(this.partnerKey, inboundStreamMessage) .ContinueWith( antecedent => Task.Run(() => this.DeleteInboundStreamRequestedContinuation(antecedent, inboundStreamMessage.StreamIdentity))); break; case StreamWireProtocolMessageKind.DeleteStreamResponse: this.streamManager.CompleteDeleteStreamProtocol(inboundStreamMessage) .ContinueWith( antecedent => Task.Run(() => this.CompleteDeleteStreamProtocolContinuation(antecedent, inboundStreamMessage.StreamIdentity))); break; default: Diagnostics.Assert( false, "{0} Unknown message kind in InboundSessionDriver Kind-as-int: {1}", this.tracer, (int)inboundStreamMessage.Kind); break; } // Restart the receive operation Diagnostics.Assert(this.inboundSession != null, "InboundSessionDriver.ReceiveContinuation.RestartReceive found null session"); this.ReceiveOnSession(); } }
// Hides the base SetResult method which we need to override // This method is called when the message is actually delivered to a ReceiveAsync call // The update to the CloseMessageSequenceNumber property of Inbound is transactional internal async Task SetResult(InboundBaseStreamWireMessage message) { var closeSeqNumber = this.stream.CloseMessageSequenceNumber; if (message.Kind == StreamWireProtocolMessageKind.CloseStream) { // The close message can be a repeat in recovery cases, but the close sequence number must be unique // It is also possible that the transaction surrounding this ReceiveAsync will abort and the message // will not in fact be consumed -- and will be received again, but the close sequence number must be unique Diagnostics.Assert( closeSeqNumber == StreamConstants.InitialValueOfLastSequenceNumberInStream || closeSeqNumber == message.MessageSequenceNumber, "{0} TraceId::{1} encountered multiple close sequence number values {2} and {3}", this.stream.Tracer, this.traceId, closeSeqNumber, message.MessageSequenceNumber); try { // this method is idempotent; the close sequence number for a stream is invariant await this.stream.SetCloseMessageSequenceNumber(message.MessageSequenceNumber, this.transactionContext); await this.stream.CloseInboundStream(this.transactionContext); } catch (Exception e) { if (e is FabricObjectClosedException) { FabricEvents.Events.DataMessageDelivery( string.Format(CultureInfo.InvariantCulture, "ObjectClosed@{0}{1}", this.traceId, this.stream.TraceType), this.stream.StreamIdentity.ToString(), message.MessageSequenceNumber, this.transactionContext.Id.ToString()); this.TrySetException(e); return; } if (e is FabricNotPrimaryException) { FabricEvents.Events.DataMessageDelivery( string.Format(CultureInfo.InvariantCulture, "NotPrimary@{0}{1}", this.traceId, this.stream.TraceType), this.stream.StreamIdentity.ToString(), message.MessageSequenceNumber, this.transactionContext.Id.ToString()); this.TrySetException(e); return; } if (e is FabricNotReadableException) { FabricEvents.Events.DataMessageDelivery( string.Format(CultureInfo.InvariantCulture, "NotReadable@{0}{1}", this.traceId, this.stream.TraceType), this.stream.StreamIdentity.ToString(), message.MessageSequenceNumber, this.transactionContext.Id.ToString()); this.TrySetException(e); return; } Tracing.WriteExceptionAsError( "DataMessageDelivery.Failure", e, "Partition::Replica {0} TraceId: {1} MessageNumber: {2} TransactionId: {3}", this.stream.TraceType, this.traceId, message.MessageSequenceNumber, this.transactionContext.Id); Diagnostics.Assert( false, "Unexpected Exception In {0} TraceId: {1} MessageNumber: {2} TransactionId: {3}", this.stream.TraceType, this.traceId, message.MessageSequenceNumber, this.transactionContext.Id); } } else { Diagnostics.Assert( closeSeqNumber == StreamConstants.InitialValueOfLastSequenceNumberInStream || closeSeqNumber > message.MessageSequenceNumber, "{0} received payload message with sequence number {1} which is greater than the close sequence number {2}", this.stream.Tracer, message.MessageSequenceNumber, closeSeqNumber); } var setResultSuccess = this.TrySetResult(message); if (setResultSuccess) { FabricEvents.Events.DataMessageDelivery( "Success::" + this.traceId + "@" + this.stream.TraceType, this.stream.StreamIdentity.ToString(), message.MessageSequenceNumber, this.transactionContext.Id.ToString()); } else { FabricEvents.Events.DataMessageDelivery( "Failure::" + this.traceId + "@" + this.stream.TraceType, this.stream.StreamIdentity.ToString(), message.MessageSequenceNumber, this.transactionContext.Id.ToString()); } }