Exemplo n.º 1
0
        /// <summary>
        /// Running process for the background thread that reads and parses out server messages and triggers callbacks.
        /// </summary>
        private void BackgroundNetworkThreadRun()
        {
            try
            {
                byte[] message;
                while ((message = this.BaseStream.ReadMessage()) != null)
                {
                    // Read the execution id from the server message to pull the corresponding pending response from
                    // the internal queue.
                    long executionId = DataConverter.BigEndian.GetInt64(message, 0);

                    // Get the response from the stack
                    Response response = this.ExecutionCache.BeginRemoveItem(executionId);

                    // If the response wasn't already dealt with, finalize processing.
                    if (response != null)
                    {
                        // Trigger the callback.  The call is non-blocking: the callback is queued for execution in the
                        // ThreadPool.
                        response.OnExecutionComplete(message);
                        // Call completion handler for classes that provide additional tracking
                        this.OnExecutionComplete(
                            executionId
                            , response.Procedure
                            , response.ExecutionDuration
                            , response.Status
                            , message.LongLength
                            );
                    }
                    // else: message is discarded: the request timed-out or was cancelled by the client and wasn't
                    // hoped for anymore.
                }
            }
            // ThreadAborts will occur when the connection is closed or the application terminates - swallow them.
            catch (ThreadAbortException) { }
            catch (Exception x)
            {
                // If this thread dies, we have no alternative than to kill the connection entirely.
                this.Terminate();
                if (this.TerminalException == null)
                {
                    this.TerminalException = new VoltExecutionException(Resources.BackgroundNetworkThreadDied, x);
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Open the connection.
        /// </summary>
        /// <returns>A reference to the current connection instance for action chaining.</returns>
        public override VoltConnection Open()
        {
            // Synchronize access.
            lock (this.SyncRoot)
            {
                // Validate connection status.
                if (this.Status != ConnectionStatus.Closed)
                {
                    throw new InvalidOperationException(string.Format(Resources.InvalidConnectionStatus, this.Status));
                }

                // Set status to "connecting".
                this.Status = ConnectionStatus.Connecting;

                // Connect to the default endpoint (there should only be one anyways if this is a managed pool
                // connection, otherwise, you're on your own - this is a connection, not a Pool!).
                IPEndPoint endPoint = this.Settings.DefaultIPEndPoint;
                try
                {
                    // Create new socket stream and wrap a core protocol stream manager around it.
                    this.BaseStream = new VoltProtocolStream(endPoint, this.Settings.ConnectionTimeout);

                    // Now send login message.
                    using (Serializer serializer = new Serializer())
                    {
                        var msg = serializer
                                  .Write(this.Settings.ServiceType.ToString().ToLowerInvariant())
                                  .Write(this.Settings.UserID)
                                  .WriteRaw(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(this.Settings.Password)))
                                  .GetBytes();
                        this.BaseStream.WriteMessage(msg.Array, msg.Offset, msg.Count);
                    }

                    // Receive and process login response message.
                    var deserializer           = new Deserializer(this.BaseStream.ReadMessage());
                    LoginResponseStatus status = (LoginResponseStatus)deserializer.ReadSByte();
                    if (status != LoginResponseStatus.Connected)
                    {
                        // Re-package server response in an appropriate exception.
                        switch (status)
                        {
                        case LoginResponseStatus.ConnectionHandshakeTimeout:
                            throw new VoltConnectionException(
                                      Resources.LRS_ConnectionHandshakeTimeout
                                      , endPoint
                                      , status
                                      );

                        case LoginResponseStatus.CorruptedHandshake:
                            throw new VoltConnectionException(
                                      Resources.LRS_CorruptedHandshake
                                      , endPoint
                                      , status
                                      );

                        case LoginResponseStatus.InvalidCredentials:
                            throw new VoltPermissionException(
                                      Resources.LRS_InvalidCredentials
                                      , endPoint
                                      , status
                                      );

                        case LoginResponseStatus.ServerTooBusy:
                            throw new VoltConnectionException(
                                      Resources.LRS_ServerTooBusy
                                      , endPoint
                                      , status
                                      );

                        default:
                            throw new VoltConnectionException(Resources.LRS_Unknown, endPoint, status);
                        }
                    }
                    // Parse the rest of the response the get core cluster information.
                    try
                    {
                        this.ServerHostId          = deserializer.ReadInt32();
                        this.ConnectionId          = deserializer.ReadInt64();
                        this.ClusterStartTimeStamp = deserializer.ReadDateTimeFromMilliseconds();
                        this.LeaderIPEndPoint      = new IPEndPoint(
                            new IPAddress(deserializer.ReadRaw(4))
                            , endPoint.Port
                            );
                        this.BuildString = deserializer.ReadString();
                    }
                    catch (Exception x)
                    {
                        throw new VoltConnectionException(Resources.LR_FailedParsingResponse, x, endPoint);
                    }

                    // Now that we are successfully connected, set the socket timeout to infinite (we are constantly
                    // listening for new messages).
                    this.BaseStream.ResetTimeout(Timeout.Infinite);

                    // Create background threads.
                    this.BackgroundNetworkThread = new Thread(this.BackgroundNetworkThreadRun)
                    {
                        IsBackground = true,
                        Priority     = ThreadPriority.AboveNormal
                    };
                    this.BackgroundTimeoutThread = new Thread(this.BackgroundTimeoutThreadRun)
                    {
                        IsBackground = true
                    };

                    // Starting background processing threads.
                    this.BackgroundNetworkThread.Start();
                    this.BackgroundTimeoutThread.Start();

                    // Start Callback executor
                    this.CallbackExecutor.Start();

                    // Ensure terminal exception is reset
                    this.TerminalException = null;

                    // Connection is now ready.
                    this.Status = ConnectionStatus.Connected;

                    // Initialize statistics as needed.
                    if (this.Settings.StatisticsEnabled)
                    {
                        this.Stats = new Dictionary <string, Statistics>(StringComparer.OrdinalIgnoreCase);
                        // For lifetime statistics, keep track of previous connection cycles, if any.
                        if (this.LifetimeStats != null)
                        {
                            if (this.PastLifetimeStats != null)
                            {
                                this.PastLifetimeStats.SummarizeWith(this.LifetimeStats);
                            }
                            else
                            {
                                this.PastLifetimeStats = this.LifetimeStats;
                            }
                        }
                        this.LifetimeStats = new Statistics();
                    }

                    // Trace as needed
                    if (this.Settings.TraceEnabled)
                    {
                        VoltTrace.TraceEvent(
                            TraceEventType.Information
                            , VoltTraceEventType.ConnectionOpened
                            , Resources.TraceConnectionOpened
                            , this.ServerHostId
                            , this.ConnectionId
                            , endPoint
                            , this.LeaderIPEndPoint
                            , this.ClusterStartTimeStamp
                            , this.BuildString
                            );
                    }
                }
                catch (Exception x)
                {
                    try
                    {
                        // In case of failure, terminate everything immediately (will correct status).
                        this.Terminate();
                    }
                    catch { }
                    // Re-throw exception, wrapping if needed.
                    if (x is VoltException)
                    {
                        throw new VoltConnectionException(Resources.ConnectionFailure, x, endPoint);
                    }
                    else
                    {
                        throw x;
                    }
                }
            }
            return(this);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Running process for the background thread that monitors time-out conditions and triggers callbacks.
        /// </summary>
        private void BackgroundTimeoutThreadRun()
        {
            try
            {
                while (true)
                {
                    var expiredExecutionIds = this.ExecutionCache.GetExpiredItems();

                    foreach (long expiredExecutionId in expiredExecutionIds)
                    {
                        // Get the response from the stack.
                        Response response = this.ExecutionCache.BeginRemoveItem(expiredExecutionId);

                        // If the response wasn't already dealt with, finalize processing
                        if (response != null)
                        {
                            // Trigger the callback.  The call is non-blocking: the callback is queued for execution
                            // in the ThreadPool.
                            response.OnExecutionTimeout();
                            // Call completion handler for classes that provide additional tracking.
                            this.OnExecutionComplete(
                                expiredExecutionId
                                , response.Procedure
                                , response.ExecutionDuration
                                , response.Status
                                , 0                     // Not accurate: the response might come later...
                                );
                        }
                    }
                    // Sleep a while, just to prevent CPU 100%.  Note tat this means there is a 1ms+ inprecision on
                    // triggerring the callback.
                    // Thread.Sleep(1);

                    // PERF: So, we should have GetExpiredItems tell us when the next expiration date is
                    // It's a sort of long function (iterate over entire dictionary), and locks it for adds/removes
                    // PERF: sebc - Not sure I love that 100ms sleep when there is nothing in the queue.  With a bit of
                    // ill timing, this will introduce an artificial 100ms additional latency on the first procedure
                    // call that hits after a period of quietness - on low-volume application where the submission rate
                    // is slower than (100ms + avg-procedure-execution-duration), all client requests would be
                    // artificially delayed.  This said, we're talking about triggering timeout callbacks (not as
                    // triggering the calback for successful executions) and the gain in CPU footprint is major (20%).
                    // Might need to refine again in the future.
                    if (expiredExecutionIds.Count > 0)
                    {
                        Thread.Sleep(10);
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                }
            }
            // ThreadAborts will occur when the connection is closed or the application terminates - swallow them.
            catch (ThreadAbortException) { }
            catch (Exception x)
            {
                // If this thread dies, we have no alternative than to kill the connection entirely.
                this.Terminate();
                if (this.TerminalException == null)
                {
                    this.TerminalException = new VoltExecutionException(Resources.BackgroundTimeoutThreadDied, x);
                }
            }
        }
        /// <summary>
        /// Open the connection.
        /// </summary>
        /// <returns>A reference to the current connection instance for action chaining.</returns>
        public override VoltConnection Open()
        {
            // Synchronize access.
            lock (this.SyncRoot)
            {
                // Validate connection status.
                if (this.Status != ConnectionStatus.Closed)
                    throw new InvalidOperationException(string.Format(Resources.InvalidConnectionStatus, this.Status));

                // Set status to "connecting".
                this.Status = ConnectionStatus.Connecting;

                // Connect to the default endpoint (there should only be one anyways if this is a managed pool
                // connection, otherwise, you're on your own - this is a connection, not a Pool!).
                IPEndPoint endPoint = this.Settings.DefaultIPEndPoint;
                try
                {
                    // Create new socket stream and wrap a core protocol stream manager around it.
                    this.BaseStream = new VoltProtocolStream(endPoint, this.Settings.ConnectionTimeout);

                    // Now send login message.
                    using (Serializer serializer = new Serializer())
                    {
                        var msg = serializer
                                  .Write(this.Settings.ServiceType.ToString().ToLowerInvariant())
                                  .Write(this.Settings.UserID)
                                  .WriteRaw(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(this.Settings.Password)))
                                  .GetBytes();
                        this.BaseStream.WriteMessage(msg.Array, msg.Offset, msg.Count);
                    }

                    // Receive and process login response message.
                    var deserializer = new Deserializer(this.BaseStream.ReadMessage());
                    LoginResponseStatus status = (LoginResponseStatus)deserializer.ReadSByte();
                    if (status != LoginResponseStatus.Connected)
                    {
                        // Re-package server response in an appropriate exception.
                        switch (status)
                        {
                            case LoginResponseStatus.ConnectionHandshakeTimeout:
                                throw new VoltConnectionException(
                                                                   Resources.LRS_ConnectionHandshakeTimeout
                                                                 , endPoint
                                                                 , status
                                                                 );

                            case LoginResponseStatus.CorruptedHandshake:
                                throw new VoltConnectionException(
                                                                   Resources.LRS_CorruptedHandshake
                                                                 , endPoint
                                                                 , status
                                                                 );

                            case LoginResponseStatus.InvalidCredentials:
                                throw new VoltPermissionException(
                                                                   Resources.LRS_InvalidCredentials
                                                                 , endPoint
                                                                 , status
                                                                 );

                            case LoginResponseStatus.ServerTooBusy:
                                throw new VoltConnectionException(
                                                                   Resources.LRS_ServerTooBusy
                                                                 , endPoint
                                                                 , status
                                                                 );

                            default:
                                throw new VoltConnectionException(Resources.LRS_Unknown, endPoint, status);
                        }
                    }
                    // Parse the rest of the response the get core cluster information.
                    try
                    {
                        this.ServerHostId = deserializer.ReadInt32();
                        this.ConnectionId = deserializer.ReadInt64();
                        this.ClusterStartTimeStamp = deserializer.ReadDateTimeFromMilliseconds();
                        this.LeaderIPEndPoint = new IPEndPoint(
                                                                new IPAddress(deserializer.ReadRaw(4))
                                                              , endPoint.Port
                                                              );
                        this.BuildString = deserializer.ReadString();
                    }
                    catch (Exception x)
                    {
                        throw new VoltConnectionException(Resources.LR_FailedParsingResponse, x, endPoint);
                    }

                    // Now that we are successfully connected, set the socket timeout to infinite (we are constantly
                    // listening for new messages).
                    this.BaseStream.ResetTimeout(Timeout.Infinite);

                    // Create background threads.
                    this.BackgroundNetworkThread = new Thread(this.BackgroundNetworkThreadRun)
                                                    {
                                                        IsBackground = true,
                                                        Priority = ThreadPriority.AboveNormal
                                                    };
                    this.BackgroundTimeoutThread = new Thread(this.BackgroundTimeoutThreadRun)
                                                    {
                                                        IsBackground = true
                                                    };

                    // Starting background processing threads.
                    this.BackgroundNetworkThread.Start();
                    this.BackgroundTimeoutThread.Start();

                    // Start Callback executor
                    this.CallbackExecutor.Start();

                    // Ensure terminal exception is reset
                    this.TerminalException = null;

                    // Connection is now ready.
                    this.Status = ConnectionStatus.Connected;

                    // Initialize statistics as needed.
                    if (this.Settings.StatisticsEnabled)
                    {
                        this.Stats = new Dictionary<string, Statistics>(StringComparer.OrdinalIgnoreCase);
                        // For lifetime statistics, keep track of previous connection cycles, if any.
                        if (this.LifetimeStats != null)
                        {
                            if (this.PastLifetimeStats != null)
                                this.PastLifetimeStats.SummarizeWith(this.LifetimeStats);
                            else
                                this.PastLifetimeStats = this.LifetimeStats;
                        }
                        this.LifetimeStats = new Statistics();
                    }

                    // Trace as needed
                    if (this.Settings.TraceEnabled)
                        VoltTrace.TraceEvent(
                                              TraceEventType.Information
                                            , VoltTraceEventType.ConnectionOpened
                                            , Resources.TraceConnectionOpened
                                            , this.ServerHostId
                                            , this.ConnectionId
                                            , endPoint
                                            , this.LeaderIPEndPoint
                                            , this.ClusterStartTimeStamp
                                            , this.BuildString
                                            );
                }
                catch (Exception x)
                {
                    try
                    {
                        // In case of failure, terminate everything immediately (will correct status).
                        this.Terminate();
                    }
                    catch { }
                    // Re-throw exception, wrapping if needed.
                    if (x is VoltException)
                        throw new VoltConnectionException(Resources.ConnectionFailure, x, endPoint);
                    else
                        throw x;
                }
            }
            return this;
        }
        /// <summary>
        /// Running process for the background thread that monitors time-out conditions and triggers callbacks.
        /// </summary>
        private void BackgroundTimeoutThreadRun()
        {
            try
            {
                while (true)
                {
                    var expiredExecutionIds = this.ExecutionCache.GetExpiredItems();

                    foreach (long expiredExecutionId in expiredExecutionIds)
                    {

                        // Get the response from the stack.
                        Response response = this.ExecutionCache.BeginRemoveItem(expiredExecutionId);

                        // If the response wasn't already dealt with, finalize processing
                        if (response != null)
                        {
                            // Trigger the callback.  The call is non-blocking: the callback is queued for execution
                            // in the ThreadPool.
                            response.OnExecutionTimeout();
                            // Call completion handler for classes that provide additional tracking.
                            this.OnExecutionComplete(
                                                      expiredExecutionId
                                                    , response.Procedure
                                                    , response.ExecutionDuration
                                                    , response.Status
                                                    , 0 // Not accurate: the response might come later...
                                                    );
                        }
                    }
                    // Sleep a while, just to prevent CPU 100%.  Note tat this means there is a 1ms+ inprecision on
                    // triggerring the callback.
                    // Thread.Sleep(1);

                    // PERF: So, we should have GetExpiredItems tell us when the next expiration date is
                    // It's a sort of long function (iterate over entire dictionary), and locks it for adds/removes
                    // PERF: sebc - Not sure I love that 100ms sleep when there is nothing in the queue.  With a bit of
                    // ill timing, this will introduce an artificial 100ms additional latency on the first procedure
                    // call that hits after a period of quietness - on low-volume application where the submission rate
                    // is slower than (100ms + avg-procedure-execution-duration), all client requests would be
                    // artificially delayed.  This said, we're talking about triggering timeout callbacks (not as
                    // triggering the calback for successful executions) and the gain in CPU footprint is major (20%).
                    // Might need to refine again in the future.
                    if (expiredExecutionIds.Count > 0)
                    {
                        Thread.Sleep(10);
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                }
            }
            // ThreadAborts will occur when the connection is closed or the application terminates - swallow them.
            catch (ThreadAbortException) { }
            catch (Exception x)
            {
                // If this thread dies, we have no alternative than to kill the connection entirely.
                this.Terminate();
                if (this.TerminalException == null)
                    this.TerminalException = new VoltExecutionException(Resources.BackgroundTimeoutThreadDied, x);
            }
        }
        /// <summary>
        /// Running process for the background thread that reads and parses out server messages and triggers callbacks.
        /// </summary>
        private void BackgroundNetworkThreadRun()
        {
            try
            {
                byte[] message;
                while ((message = this.BaseStream.ReadMessage()) != null)
                {
                    // Read the execution id from the server message to pull the corresponding pending response from
                    // the internal queue.
                    long executionId = DataConverter.BigEndian.GetInt64(message, 0);

                    // Get the response from the stack
                    Response response = this.ExecutionCache.BeginRemoveItem(executionId);

                    // If the response wasn't already dealt with, finalize processing.
                    if (response != null)
                    {
                        // Trigger the callback.  The call is non-blocking: the callback is queued for execution in the
                        // ThreadPool.
                        response.OnExecutionComplete(message);
                        // Call completion handler for classes that provide additional tracking
                        this.OnExecutionComplete(
                                                  executionId
                                                , response.Procedure
                                                , response.ExecutionDuration
                                                , response.Status
                                                , message.LongLength
                                                );
                    }
                    // else: message is discarded: the request timed-out or was cancelled by the client and wasn't
                    // hoped for anymore.
                }
            }
            // ThreadAborts will occur when the connection is closed or the application terminates - swallow them.
            catch (ThreadAbortException) { }
            catch (Exception x)
            {
                // If this thread dies, we have no alternative than to kill the connection entirely.
                this.Terminate();
                if (this.TerminalException == null)
                    this.TerminalException = new VoltExecutionException(Resources.BackgroundNetworkThreadDied, x);
            }
        }