Как я могу измерить время входа в систему на SqlConnection при использовании пула?

Я хотел бы получить некоторую статистику о приложении, использующем SqlConnection, в частности, сколько времени оно тратит на физический вход на сервер.

Простой подход будет примерно таким:

using (SqlConnection connection = ...)
{
    Stopwatch loginTimer = Stopwatch.StartNew();
    connection.Open()
    loginTimer.Stop();
}

Хитрость в том, что я также использую пул соединений и не хочу его отключать. В результате мои показатели искажаются, потому что большинство вызовов .Open() фактически просто захватывают существующее открытое физическое соединение из пула, поэтому я увижу:

00:00:01.39
00:00:00.02
00:00:00.02
00:00:00.02
...

Приложение использует достаточно соединений и нацелено на SqlAzure, поэтому я действительно ожидаю, что физические входы будут происходить достаточно часто.

Я пробовал проверить соединение, прежде чем пытаться:

if (sqlConnection.State != ConnectionState.Open)
{
    // Time and call .Open()
}

К сожалению, логическое соединение SqlConnection не отражает состояние физического соединения, поэтому всегда выполняется блок if.

Я знаю, что можно создать свой собственный пул соединений, из которого я буду черпать и выполнять, но никогда не закрывать, поэтому я мог бы отслеживать фактическое состояние физического соединения через состояние логического соединения, но я бы действительно предпочитаю этого не делать.


person Jeff Wight    schedule 12.03.2013    source источник
comment
Поскольку он физически входит в систему только для вызовов Open () с самым длинным временем, почему бы не игнорировать все самые короткие времена? Например, сохраняйте среднее значение времен и отбрасывайте значения ниже среднего.   -  person David R Tribble    schedule 23.03.2013


Ответы (2)


Вероятно, вы можете использовать свойство ClientConnectionId для отслеживания подключений sql. Он восстанавливается каждый раз, когда создается новое физическое соединение, и сохраняется, когда соединение возвращается из пула. Но он доступен только начиная с .net 4.5.

Другая возможность - пропустить объединение соединений только для некоторых соединений, просто чтобы измерить физическое время, сохраняя объединение для других соединений.

Например, у вас может быть статический счетчик, который будет увеличиваться. Для каждого значения, кратного 10, вы можете добавить Pooling = 'false' в строку подключения, чтобы не добавлять его в пул. Это откроет новое соединение, и вы сможете измерить физическое время.

person Alexander    schedule 22.03.2013

Расширение ответа Александра некоторым конкретным кодом.

В рамках ограничений использования пула для всех подключений можно измерить время, потраченное физически на вход в сервер / базу данных, отслеживая значение SqlConnection.ClientConnectionId, сгенерированное SqlClient для каждого физического авторизоваться. Ниже приведен пример метода расширения для измерения и составления отчета на этот раз. Чтобы использовать его, измените вызовы SqlConnection.Open() на SqlConnection.Login(out openTime), где результат будет истинным, если произошел вход в систему, и ложью в противном случае.

internal static class SqlConnectionExtension
{
    private static readonly PropertyInfo _clientConnectionIdPropertyInfo = typeof(SqlConnection).GetProperty("ClientConnectionId");
    private static readonly HashSet<Guid> _clientConnectionIds = new HashSet<Guid>();

    /// <summary>
    /// Method that calls <see cref="SqlConnection.Open()"/>, measuring the time it takes.
    /// </summary>
    /// <param name="sqlConnection">The <see cref="SqlConnection"/> to open.</param>
    /// <param name="openTime">The total time that the call to <see cref="SqlConnection.Open()"/> took.</param>
    /// <returns>True if a login took place; false if a connection was returned from a connection pool.</returns>
    public static bool Login(this SqlConnection sqlConnection, out TimeSpan openTime)
    {
        Stopwatch loginTimer = Stopwatch.StartNew();
        sqlConnection.Open();
        loginTimer.Stop();

        openTime = loginTimer.Elapsed;

    #if NET_4_0_3
        Guid clientConnectionId = sqlConnection.ClientConnectionId;
    #else
        Guid clientConnectionId = Guid.Empty;
        if (_clientConnectionIdPropertyInfo != null)
        {
            clientConnectionId = (Guid)_clientConnectionIdPropertyInfo.GetValue(sqlConnection, null);
        }
    #endif
        if (clientConnectionId != Guid.Empty && !_clientConnectionIds.Contains(clientConnectionId))
        {
            lock (_clientConnectionIds)
            {
                if (_clientConnectionIds.Add(clientConnectionId))
                {
                    return true;
                }
            }
        }
        return false;
    }
}

В моей собственной среде мы все еще используем VS2010, и не все клиенты имеют пакет множественного таргетинга 4.0.3, поэтому раздел #if NET_4_0_3.

person Jeff Wight    schedule 03.04.2013