| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- //------------------------------------------------------------------------------
- // <copyright file="SqlConnectionTimeoutErrorInternal.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.SqlClient
- {
- using System;
- using System.ComponentModel;
- using System.Data;
- using System.Data.Common;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Runtime.InteropServices.ComTypes;
- using System.Security;
- using System.Security.Permissions;
- using System.Threading;
- using SysTx = System.Transactions;
- using System.Data.SqlClient;
- using System.Text;
- // VSTFDevDiv# 643319 - Improve timeout error message reported when SqlConnection.Open fails
- internal enum SqlConnectionTimeoutErrorPhase
- {
- Undefined = 0,
- PreLoginBegin, // [PRE-LOGIN PHASE] Start of the pre-login phase; Initialize global variables;
- InitializeConnection, // [PRE-LOGIN PHASE] Create and initialize socket.
- SendPreLoginHandshake, // [PRE-LOGIN PHASE] Make pre-login handshake request.
- ConsumePreLoginHandshake, // [PRE-LOGIN PHASE] Receive pre-login handshake response and consume it; Establish an SSL channel.
- LoginBegin, // [LOGIN PHASE] End of the pre-login phase; Start of the login phase;
- ProcessConnectionAuth, // [LOGIN PHASE] Process SSPI or SQL Authenticate.
- PostLogin, // [POST-LOGIN PHASE] End of the login phase; And post-login phase;
- Complete, // Marker for the succesful completion of the connection
- Count // ** This is to track the length of the enum. ** Do not add any phase after this. **
- }
- internal enum SqlConnectionInternalSourceType
- {
- Principle,
- Failover,
- RoutingDestination
- }
- // DEVNOTE: Class to capture the duration spent in each SqlConnectionTimeoutErrorPhase.
- internal class SqlConnectionTimeoutPhaseDuration
- {
- Stopwatch swDuration = new Stopwatch();
- internal void StartCapture()
- {
- Debug.Assert(swDuration != null, "Time capture stopwatch cannot be null.");
- swDuration.Start();
- }
- internal void StopCapture()
- {
- //Debug.Assert(swDuration.IsRunning == true, "The stop opertaion of the stopwatch cannot be called when it is not running.");
- if (swDuration.IsRunning == true)
- swDuration.Stop();
- }
- internal long GetMilliSecondDuration()
- {
- // DEVNOTE: In a phase fails in between a phase, the stop watch may still be running.
- // Hence the check to verify if the stop watch is running hasn't been added in.
- return swDuration.ElapsedMilliseconds;
- }
- }
- internal class SqlConnectionTimeoutErrorInternal
- {
- SqlConnectionTimeoutPhaseDuration[] phaseDurations = null;
- SqlConnectionTimeoutPhaseDuration[] originalPhaseDurations = null;
- SqlConnectionTimeoutErrorPhase currentPhase = SqlConnectionTimeoutErrorPhase.Undefined;
- SqlConnectionInternalSourceType currentSourceType = SqlConnectionInternalSourceType.Principle;
- bool isFailoverScenario = false;
- internal SqlConnectionTimeoutErrorPhase CurrentPhase
- {
- get { return currentPhase; }
- }
- public SqlConnectionTimeoutErrorInternal()
- {
- phaseDurations = new SqlConnectionTimeoutPhaseDuration[(int)SqlConnectionTimeoutErrorPhase.Count];
- for (int i = 0; i < phaseDurations.Length; i++)
- phaseDurations[i] = null;
- }
- public void SetFailoverScenario(bool useFailoverServer)
- {
- isFailoverScenario = useFailoverServer;
- }
- public void SetInternalSourceType(SqlConnectionInternalSourceType sourceType)
- {
- currentSourceType = sourceType;
- if (currentSourceType == SqlConnectionInternalSourceType.RoutingDestination)
- {
- // When we get routed, save the current phase durations so that we can use them in the error message later
- Debug.Assert(currentPhase == SqlConnectionTimeoutErrorPhase.PostLogin, "Should not be switching to the routing destination until Post Login is completed");
- originalPhaseDurations = phaseDurations;
- phaseDurations = new SqlConnectionTimeoutPhaseDuration[(int)SqlConnectionTimeoutErrorPhase.Count];
- SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.PreLoginBegin);
- }
- }
- internal void ResetAndRestartPhase()
- {
- currentPhase = SqlConnectionTimeoutErrorPhase.PreLoginBegin;
- for (int i = 0; i < phaseDurations.Length; i++)
- phaseDurations[i] = null;
- }
- internal void SetAndBeginPhase(SqlConnectionTimeoutErrorPhase timeoutErrorPhase)
- {
- currentPhase = timeoutErrorPhase;
- if (phaseDurations[(int)timeoutErrorPhase] == null)
- {
- phaseDurations[(int)timeoutErrorPhase] = new SqlConnectionTimeoutPhaseDuration();
- }
- phaseDurations[(int)timeoutErrorPhase].StartCapture();
- }
- internal void EndPhase(SqlConnectionTimeoutErrorPhase timeoutErrorPhase)
- {
- Debug.Assert(phaseDurations[(int)timeoutErrorPhase] != null, "End phase capture cannot be invoked when the phase duration object is a null.");
- phaseDurations[(int)timeoutErrorPhase].StopCapture();
- }
- internal void SetAllCompleteMarker()
- {
- currentPhase = SqlConnectionTimeoutErrorPhase.Complete;
- }
- internal string GetErrorMessage()
- {
- StringBuilder errorBuilder;
- string durationString;
- switch(currentPhase)
- {
- case SqlConnectionTimeoutErrorPhase.PreLoginBegin:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_Begin());
- durationString = SQLMessage.Duration_PreLogin_Begin(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.InitializeConnection:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_InitializeConnection());
- durationString = SQLMessage.Duration_PreLogin_Begin(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_SendHandshake());
- durationString = SQLMessage.Duration_PreLoginHandshake(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_PreLogin_ConsumeHandshake());
- durationString = SQLMessage.Duration_PreLoginHandshake(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.LoginBegin:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_Begin());
- durationString = SQLMessage.Duration_Login_Begin(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_Login_ProcessConnectionAuth());
- durationString = SQLMessage.Duration_Login_ProcessConnectionAuth(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration());
- break;
- case SqlConnectionTimeoutErrorPhase.PostLogin:
- errorBuilder = new StringBuilder(SQLMessage.Timeout_PostLogin());
- durationString = SQLMessage.Duration_PostLogin(
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() +
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(),
- phaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration());
- break;
- default:
- errorBuilder = new StringBuilder(SQLMessage.Timeout());
- durationString = null;
- break;
- }
- // This message is to be added only when within the various stages of a connection.
- // In all other cases, it will default to the original error message.
- if ((currentPhase != SqlConnectionTimeoutErrorPhase.Undefined) || (currentPhase != SqlConnectionTimeoutErrorPhase.Complete))
- {
- // NOTE: In case of a failover scenario, add a string that this failure occured as part of the primary or secondary server
- if (isFailoverScenario)
- {
- errorBuilder.Append(" ");
- errorBuilder.AppendFormat((IFormatProvider)null, SQLMessage.Timeout_FailoverInfo(), currentSourceType);
- }
- else if (currentSourceType == SqlConnectionInternalSourceType.RoutingDestination) {
- errorBuilder.Append(" ");
- errorBuilder.AppendFormat((IFormatProvider)null, SQLMessage.Timeout_RoutingDestination(),
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PreLoginBegin].GetMilliSecondDuration() +
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.InitializeConnection].GetMilliSecondDuration(),
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake].GetMilliSecondDuration() +
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake].GetMilliSecondDuration(),
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.LoginBegin].GetMilliSecondDuration(),
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(),
- originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration());
- }
- // NOTE: To display duration in each phase.
- if (durationString != null)
- {
- errorBuilder.Append(" ");
- errorBuilder.Append(durationString);
- }
- }
- return errorBuilder.ToString();
- }
- }
- }
|