Azure — SqlBulkCopy создает исключение с истекшим временем ожидания

Я использую базу данных azure sql (v12) на виртуальной машине. У меня есть два разных экземпляра базы данных - один для подготовки и один для производства. Я пытаюсь получить данные из промежуточной стадии и вставить их в производство одним нажатием кнопки. Этот код успешно работает «иногда», что означает, что он будет успешным случайным образом. В противном случае я возвращаю ошибку:

BULK COPY Тип исключения фиксации: {0}System.Data.SqlClient.SqlException Сообщение BULK COPY: {0}Время ожидания истекло. Время ожидания истекло до завершения операции или сервер не отвечает. Этот сбой произошел при попытке подключения к месту назначения маршрутизации. Продолжительность попытки подключения к исходному серверу составила: [Pre-Login] initialization=1; рукопожатие=17; [Вход] инициализация=0; аутентификация=0; [После входа] завершено=0;

Вот код, который я использую для выполнения этой задачи, возможно, есть недостаток, которого я не вижу. Выгрузив StringBuilder, я вижу, что запрос SELECT работает и запрос DELETE работает, но ошибка возникает, когда я пытаюсь скопировать данные с помощью SqlBulkCopy. Любая помощь будет принята с благодарностью. Я уже безуспешно просмотрел кучу документов MSDN -> добавление более длинных CommandTimeout, добавление более длительного BulkCopyTimeout и перенастройка портов на моем брандмауэре. Все равно не повезло.

Ресурсы, которые я использовал: https://social.msdn.microsoft.com/Forums/en-US/1467d64f-69ae-4c1f-91a2-349fc5d514ae/sqlbulkcopy-fails-with-timeout-expired-error?forum=adodotnetdataproviders

https://azure.microsoft.com/nb-no/documentation/articles/sql-database-develop-direct-route-ports-adonet-v12/

Время ожидания истекло с SqlBulkCopy

public static object SyncData()
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("Internal Connection...");
    string internalConnectionString = GetConnectionString("ConnectionString");
    using (SqlConnection internalConnection = new SqlConnection(internalConnectionString))
    {
        internalConnection.Open();              
        SqlCommand selectCommand = internalConnection.CreateCommand();
        selectCommand.CommandTimeout = 180;
        try
        {
            selectCommand.CommandText = "SELECT * FROM dbo.test";
            SqlDataReader reader = selectCommand.ExecuteReader();

            sb.AppendLine("External Connection...");
            string externalConnectionString = GetConnectionString("ExternalConnectionString");
            using (SqlConnection externalConnection = new SqlConnection(externalConnectionString))
            {
                externalConnection.Open();              
                SqlCommand CRUDCommand = externalConnection.CreateCommand();
                CRUDCommand.CommandTimeout = 180;
                SqlTransaction transaction = externalConnection.BeginTransaction("test");
                CRUDCommand.Connection = externalConnection;
                CRUDCommand.Transaction = transaction;
                try
                {
                    CRUDCommand.CommandText = "DELETE FROM dbo.test";
                    sb.AppendLine("DELETE: Number of rows affected = " + CRUDCommand.ExecuteNonQuery());
                    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(externalConnection, SqlBulkCopyOptions.KeepIdentity, transaction))
                    {
                        try
                        {
                            bulkCopy.DestinationTableName = "dbo.test";
                            bulkCopy.BatchSize = 100;
                            bulkCopy.BulkCopyTimeout = 180;
                            bulkCopy.WriteToServer(reader);

                            sb.AppendLine("Table data copied successfully");

                            transaction.Commit();
                            sb.AppendLine("Transaction committed.");
                        }
                        catch (Exception ex)
                        {
                            sb.AppendLine("BULK COPY Commit Exception Type: {0}" + ex.GetType());
                            sb.AppendLine("  BULK COPY Message: {0}" + ex.Message);
                            try
                            {
                                transaction.Rollback();
                            }
                            catch (Exception ex2)
                            {
                                sb.AppendLine("Rollback Exception Type: {0}" + ex2.GetType());
                                sb.AppendLine("  Message: {0}" + ex2.Message);
                            }
                        }
                        finally
                        {
                            reader.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    sb.AppendLine("Commit Exception Type: {0}" + ex.GetType());
                    sb.AppendLine("  Message: {0}" + ex.Message);

                    try
                    {
                        transaction.Rollback();
                    }
                    catch (Exception ex2)
                    {
                        sb.AppendLine("Rollback Exception Type: {0}" + ex2.GetType());
                        sb.AppendLine("  Message: {0}" + ex2.Message);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine("Commit Exception Type: {0}" + ex.GetType());
            sb.AppendLine("  Message: {0}" + ex.Message);
        }
    }
    return sb.ToString();
}

person c22joe    schedule 07.10.2016    source источник
comment
Оба сервера находятся в одной виртуальной сети? Пробовали ли вы экспериментировать с тайм-аутами и, возможно, увеличивать размер пакета, чтобы не было так много циклов? У меня есть аналогичный процесс, хотя я использую Dapper, Reactive Extensions и Simple.Data для выполнения того же самого. В моем случае мне просто пришлось поиграть с настройками, пока я не нашел наилучшие результаты. Также у меня есть логика повторных попыток, которая помогает, но ИМХО нет серебряной пули.   -  person jcwrequests    schedule 07.10.2016
comment
да, обе базы данных находятся на одном сервере Azure. я пытался увеличить время ожидания до 30 минут, но это просто заставляет их зависать на 30 минут ... Таблица, которую я сейчас копирую, составляет всего 400 строк, поэтому изменение размера пакета не повлияло на меня.   -  person c22joe    schedule 07.10.2016
comment
Процесс запускает код в той же виртуальной сети?   -  person jcwrequests    schedule 07.10.2016


Ответы (1)


При создании экземпляра SqlBulkCopy вы передаете строку подключения externalConnectionString и, таким образом, открываете новое подключение. Это может вызвать проблему взаимоблокировки, когда оба соединения пытаются изменить одну и ту же таблицу.

Пробовали ли вы передать существующее соединение externalConnection конструктору SqlBulkCopy вместо строки подключения?

person sakai    schedule 07.10.2016
comment
Хорошая идея, я попробую это. - person c22joe; 07.10.2016
comment
Вы совершенно правы, когда я создал экземпляр SqlBulkCopy, я заблокировал таблицу из-за моего предыдущего запроса DELETE той же самой таблицы. Все, что мне нужно было сделать, это изменить конструктор на using (SqlBulkCopy bulkCopy = new SqlBulkCopy(externalConnection, SqlBulkCopyOptions.KeepIdentity, transaction)), и все заработало отлично. Спасибо! - person c22joe; 07.10.2016