/// <summary> /// Begins asynchronous processing of a single <see cref="T:MySpace.DataRelay.RelayMessage"/>. /// </summary> /// <param name="message">The <see cref="T:MySpace.DataRelay.RelayMessage"/>.</param> /// <param name="state">Callers can put any state they like here.</param> /// <param name="callback">The method to call upon completion.</param> /// <returns> /// Returns an <see cref="T:System.IAsyncResult"/>. /// </returns> public IAsyncResult BeginHandleMessage(RelayMessage message, object state, AsyncCallback callback) { if (!message.IsTwoWayMessage) { // cheat for now and just handle in messages synchronously // as long as the type doesn't use sync in messages then // we won't block on IO anyway. HandleMessage(message); return(SynchronousAsyncResult.CreateAndComplete(callback, state)); } SimpleLinkedList <Node> nodes = PrepareMessage(message, false); Node node; if (nodes.Pop(out node)) { var result = new AsynchronousResult <Node>((ar, n, m) => { n.EndHandleOutMessage(ar); int allowedRetries = NodeManager.Instance.GetRetryCountForMessage(message); while (message.ErrorType == RelayErrorType.NodeUnreachable && --allowedRetries >= 0) { if (PrepareMessage(message, true).Pop(out node)) { node.HandleOutMessage(message); } else { message.SetError(RelayErrorType.NoNodesAvailable); } } }, node, message); var origCallback = callback; if (callback != null) { callback = ar => { result.InnerResult = ar; origCallback(result); }; } result.InnerResult = node.BeginHandleOutMessage(message, callback, state); return(result); } else { message.SetError(RelayErrorType.NoNodesAvailable); } return(SynchronousAsyncResult.CreateAndComplete(callback, state)); }
private static void RetryHandleMessageOnError(RelayMessage message, Node node) { int allowedRetries = NodeManager.Instance.GetRetryCountForMessage(message); List <Node> attemptedNodes = null; while (--allowedRetries >= 0 && message.IsRetryable(NodeManager.Instance.GetRelayRetryPolicyForMessage(message))) { if (attemptedNodes == null) { attemptedNodes = new List <Node>(allowedRetries + 1); } attemptedNodes.Add(node); node = PrepareRetryMessage(message, attemptedNodes); if (node != null) { node.HandleOutMessage(message); } else { message.SetError(RelayErrorType.NoNodesAvailable); } } }
/// <summary> /// Begins asynchronous processing of a single <see cref="T:MySpace.DataRelay.RelayMessage"/>. /// </summary> /// <param name="message">The <see cref="T:MySpace.DataRelay.RelayMessage"/>.</param> /// <param name="state">Callers can put any state they like here.</param> /// <param name="callback">The method to call upon completion.</param> /// <returns> /// Returns an <see cref="T:System.IAsyncResult"/>. /// </returns> public virtual IAsyncResult BeginHandleMessage(RelayMessage message, object state, AsyncCallback callback) { if (!message.IsTwoWayMessage) { // cheat for now and just handle in messages synchronously // as long as the type doesn't use sync in messages then // we won't block on IO anyway. HandleMessage(message); return(SynchronousAsyncResult.CreateAndComplete(callback, state)); } LinkedListStack <Node> nodes = PrepareMessage(message); Node node; if (nodes.Pop(out node)) { var result = new AsynchronousResult <Node>((ar, n, m) => { try { n.EndHandleOutMessage(ar); RetryHandleMessageOnError(message, node); } catch (Exception ex) { log.Error(ex); } }, node, message); var origCallback = callback; if (callback != null) { callback = ar => { result.InnerResult = ar; origCallback(result); }; } result.InnerResult = node.BeginHandleOutMessage(message, callback, state); return(result); } message.SetError(RelayErrorType.NoNodesAvailable); return(SynchronousAsyncResult.CreateAndComplete(callback, state)); }
private static Node PrepareRetryMessage(RelayMessage message, IList <Node> attemptedNodes) { if (attemptedNodes.Count == 0) { throw new ArgumentException("PrepareRetryMessage must be called after the first attempt.", "attemptedNodes"); } var node = attemptedNodes[0]; if (node == null) { throw new ArgumentException("The first element is null.", "attemptedNodes"); } node = node.GetRetryNodeFromCluster(attemptedNodes); message.RelayTTL++; message.SetError(RelayErrorType.None); message.ResultOutcome = RelayOutcome.NotSent; return(node); }
/// <summary> /// Performs processing on single message /// </summary> /// <exception cref="SyncRelayOperationException"> /// When the type of an object is defined with settings /// <see cref="MySpace.DataRelay.Common.Schemas.TypeSettings"></see> with /// SyncInMessages=true and /// ThrowOnSyncFailure=true /// failed "in" executions will throw this exception /// </exception> /// <param name="message">Message to be processed</param> public void HandleMessage(RelayMessage message) { Node node; if (message.IsTwoWayMessage) { int allowedRetries = NodeManager.Instance.GetRetryCountForMessage(message); bool triedBefore = false; do { if (PrepareMessage(message, triedBefore).Pop(out node)) { triedBefore = true; node.HandleOutMessage(message); } else { message.SetError(RelayErrorType.NoNodesAvailable); } }while (message.ErrorType == RelayErrorType.NodeUnreachable && --allowedRetries >= 0); } else { SimpleLinkedList <Node> nodes = PrepareMessage(message, false); SerializedRelayMessage serializedMessage = new SerializedRelayMessage(message); SerializedRelayMessage serializedMessageInterZone = null; bool messageHandled = true; // start with "true" so that we do not pop // if there are no items in "nodes" if (nodes.Count == 0) { message.SetError(RelayErrorType.NoNodesAvailable); } else { while (nodes.Pop(out node)) { TypeSetting typeSetting = NodeManager.Instance.Config.TypeSettings.TypeSettingCollection[message.TypeId]; bool typesettingThrowOnSyncFailure = false; bool typesettingSyncInMessages = false; if (null != typeSetting && !node.NodeCluster.MeInThisCluster) { typesettingSyncInMessages = typeSetting.SyncInMessages; typesettingThrowOnSyncFailure = typeSetting.ThrowOnSyncFailure; } if (_myNodeDefinition != null && _myNodeDefinition.Zone != node.NodeDefinition.Zone) { // Message needs to cross Zone bounderies if (serializedMessageInterZone == null) { serializedMessageInterZone = new SerializedRelayMessage(RelayMessage.CreateInterZoneMessageFrom(message)); } if (message.ResultOutcome == null) { message.ResultOutcome = RelayOutcome.Queued; } node.HandleInMessage(serializedMessageInterZone); } else if (typesettingSyncInMessages) { messageHandled = node.HandleInMessageSync(message, typesettingSyncInMessages, typesettingThrowOnSyncFailure); } else { if (message.ResultOutcome == null) { message.ResultOutcome = RelayOutcome.Queued; } node.HandleInMessage(serializedMessage); } if (!messageHandled) { throw new SyncRelayOperationException(string.Format("Node {0} failed to process message {1}\r\n", node, message)); } } } } }
/// <summary> /// Begins asynchronous processing of a single <see cref="T:MySpace.DataRelay.RelayMessage"/>. /// </summary> /// <param name="message">The <see cref="T:MySpace.DataRelay.RelayMessage"/>.</param> /// <param name="state">Callers can put any state they like here.</param> /// <param name="callback">The method to call upon completion.</param> /// <returns> /// Returns an <see cref="T:System.IAsyncResult"/>. /// </returns> public IAsyncResult BeginHandleMessage(RelayMessage message, object state, AsyncCallback callback) { if (!message.IsTwoWayMessage) { // cheat for now and just handle in messages synchronously // as long as the type doesn't use sync in messages then // we won't block on IO anyway. HandleMessage(message); return SynchronousAsyncResult.CreateAndComplete(callback, state); } SimpleLinkedList<Node> nodes = PrepareMessage(message, false); Node node; if (nodes.Pop(out node)) { var result = new AsynchronousResult<Node>((ar, n, m) => { n.EndHandleOutMessage(ar); int allowedRetries = NodeManager.Instance.GetRetryCountForMessage(message); while (message.ErrorType == RelayErrorType.NodeUnreachable && --allowedRetries >= 0) { if (PrepareMessage(message, true).Pop(out node)) { node.HandleOutMessage(message); } else { message.SetError(RelayErrorType.NoNodesAvailable); } } }, node, message); var origCallback = callback; if (callback != null) { callback = ar => { result.InnerResult = ar; origCallback(result); }; } result.InnerResult = node.BeginHandleOutMessage(message, callback, state); return result; } else { message.SetError(RelayErrorType.NoNodesAvailable); } return SynchronousAsyncResult.CreateAndComplete(callback, state); }
/// <summary> /// Performs processing on single message /// </summary> /// <exception cref="SyncRelayOperationException"> /// When the type of an object is defined with settings /// <see cref="MySpace.DataRelay.Common.Schemas.TypeSettings"></see> with /// SyncInMessages=true and /// ThrowOnSyncFailure=true /// failed "in" executions will throw this exception /// </exception> /// <param name="message">Message to be processed</param> public void HandleMessage(RelayMessage message) { Node node; if (message.IsTwoWayMessage) { int allowedRetries = NodeManager.Instance.GetRetryCountForMessage(message); bool triedBefore = false; do { if (PrepareMessage(message, triedBefore).Pop(out node)) { triedBefore = true; node.HandleOutMessage(message); } else { message.SetError(RelayErrorType.NoNodesAvailable); } } while (message.ErrorType == RelayErrorType.NodeUnreachable && --allowedRetries >= 0); } else { SimpleLinkedList<Node> nodes = PrepareMessage(message, false); SerializedRelayMessage serializedMessage = new SerializedRelayMessage(message); SerializedRelayMessage serializedMessageInterZone = null; bool messageHandled = true; // start with "true" so that we do not pop // if there are no items in "nodes" if (nodes.Count == 0) { message.SetError(RelayErrorType.NoNodesAvailable); } else { while (nodes.Pop(out node)) { TypeSetting typeSetting = NodeManager.Instance.Config.TypeSettings.TypeSettingCollection[message.TypeId]; bool typesettingThrowOnSyncFailure = false; bool typesettingSyncInMessages = false; if (null != typeSetting && !node.NodeCluster.MeInThisCluster) { typesettingSyncInMessages = typeSetting.SyncInMessages; typesettingThrowOnSyncFailure = typeSetting.ThrowOnSyncFailure; } if (_myNodeDefinition != null && _myNodeDefinition.Zone != node.NodeDefinition.Zone) { // Message needs to cross Zone bounderies if (serializedMessageInterZone == null) { serializedMessageInterZone = new SerializedRelayMessage(RelayMessage.CreateInterZoneMessageFrom(message)); } if (message.ResultOutcome == null) message.ResultOutcome = RelayOutcome.Queued; node.HandleInMessage(serializedMessageInterZone); } else if (typesettingSyncInMessages) { messageHandled = node.HandleInMessageSync(message, typesettingSyncInMessages, typesettingThrowOnSyncFailure); } else { if (message.ResultOutcome == null) message.ResultOutcome = RelayOutcome.Queued; node.HandleInMessage(serializedMessage); } if (!messageHandled) { throw new SyncRelayOperationException(string.Format("Node {0} failed to process message {1}\r\n", node, message)); } } } } }
private IAsyncResult BeginDoHandleMessage(RelayMessage message, bool useSyncForInMessages, bool skipErrorQueueForSync, AsyncCallback callback, object asyncState) { var asyncTransport = _transport as IAsyncRelayTransport; if (asyncTransport == null) { return NodeSynchronousAsyncResult.CreateAndComplete(DoHandleMessage(message, useSyncForInMessages, skipErrorQueueForSync), callback, asyncState); } bool alwaysHandled = !useSyncForInMessages || !skipErrorQueueForSync; if (!Activated || message == null) { if (message != null) message.ResultOutcome = RelayOutcome.NotSent; return NodeSynchronousAsyncResult.CreateAndComplete(alwaysHandled, callback, asyncState); } if (DangerZone) { //this is only called for synchronous messages, which aren't error queued message.SetError(RelayErrorType.NodeInDanagerZone); return NodeSynchronousAsyncResult.CreateAndComplete(alwaysHandled, callback, asyncState); } var watch = GatherStats ? Stopwatch.StartNew() : null; try { NodeManager.Instance.Counters.CountMessage(message); var result = new AsynchronousResult(message, useSyncForInMessages, skipErrorQueueForSync); message.ResultOutcome = RelayOutcome.Queued; // close enough result.InnerResult = asyncTransport.BeginSendMessage(message, useSyncForInMessages, asyncResult => { if (watch != null) { watch.Stop(); CaculateStatisics(message, watch.ElapsedMilliseconds); } if (callback != null) { result.InnerResult = asyncResult; callback(result); } }, asyncState); return result; } catch (Exception ex) { //this is only called for get messages, which aren't error queued InstrumentException(ex); message.SetError(ex); NodeGroup.LogNodeException(message, this, ex); return NodeSynchronousAsyncResult.CreateAndComplete(alwaysHandled, callback, asyncState); } finally { if (watch != null) { watch.Stop(); CaculateStatisics(message, watch.ElapsedMilliseconds); } } }
/// <summary> /// Processes a single message /// </summary> /// <param name="message">Message to be processed</param> /// <param name="useSyncForInMessages">Default: false /// The type (from TypeSettings.config) can require synchronous handling for messages /// </param> /// <param name="skipErrorQueueForSync">Default: false /// The type (from TypeSettings.config) can require that should the message processing fail, /// the message will NOT be sent to the Error Queue for retry. Instead, the function returns /// false. /// Has no effect if useSyncForInMessages is false. /// </param> /// <returns> /// useSyncForInMessages = false always returns True (message processed Async) /// useSyncForInMessages = true, skipErrorQueueForSync = false always returns True (errors placed in queue for retry) /// useSyncForInMessages = true, skipErrorQueueForSync = true returns true if the message processing succeeded /// </returns> private bool DoHandleMessage(RelayMessage message, bool useSyncForInMessages, bool skipErrorQueueForSync) { try { if (!Activated || message == null) { if (skipErrorQueueForSync && useSyncForInMessages) { return false; } return true; } if (DangerZone) { //this is only called for synchronous messages, which aren't error queued message.SetError(RelayErrorType.NodeInDanagerZone); if (skipErrorQueueForSync && useSyncForInMessages) { return false; } return true; } bool messageHandled = true; try { if (GatherStats) { Stopwatch watch = Stopwatch.StartNew(); if (useSyncForInMessages) { // using the system this way allows us to continue on if the // Transport does not expose IRelayTransportExtended. // The old handling (Transport.SendMessage) will send "put" // messages one-way, preventing certain errors from being // reported, but this does not break any existing code IRelayTransportExtended TransportEx = _transport as IRelayTransportExtended; if (null != TransportEx) { TransportEx.SendSyncMessage(message); } else { _transport.SendMessage(message); } } else { _transport.SendMessage(message); } watch.Stop(); CaculateStatisics(message, watch.ElapsedMilliseconds); } else { if (useSyncForInMessages) { // using the system this way allows us to continue on if the // Transport does not expose IRelayTransportExtended. // The old handling (Transport.SendMessage) will send "put" // messages one-way, preventing certain errors from being // reported, but this does not break any existing code IRelayTransportExtended TransportEx = _transport as IRelayTransportExtended; if (null != TransportEx) { TransportEx.SendSyncMessage(message); } else { _transport.SendMessage(message); } } else { _transport.SendMessage(message); } } if (message.ErrorOccurred) { messageHandled = false; } NodeManager.Instance.Counters.CountMessage(message); } catch (Exception ex) { if (skipErrorQueueForSync && useSyncForInMessages) { messageHandled = false; } //this is only called for get messages, which aren't error queued InstrumentException(ex); message.SetError(ex); NodeGroup.LogNodeException(message, this, ex); } return messageHandled; } finally { if (message != null && message.ResultOutcome == null) { message.ResultOutcome = RelayOutcome.NotSent; } } }