Подтвердить что ты не робот

Случайно получая ORA-08177 только с одним активным сеансом

Я запускаю программу, которая создает таблицу, а затем вставляет некоторые данные.
Это единственная программа, которая обращается к базе данных.
Я получаю ORA-08177 случайным образом.
Фактический код несколько сложный, но я написал простую программу, которая воспроизводит это поведение.

using System;
using System.Data;
using Oracle.DataAccess.Client;

namespace orabug
{
  class Program
  {
    private const string ConnectionString = ""; // Valid connection string here

    // Recreates the table
    private static void Recreate()
    {
      using (var connection = new OracleConnection(ConnectionString)) {
        connection.Open();
        using (var command = connection.CreateCommand()) {
          command.CommandText = @"
            declare
              table_count binary_integer;
            begin
              select count(*) into table_count from sys.user_tables where table_name = 'TESTTABLE';
              if table_count > 0 then
                execute immediate 'drop table TestTable purge';
              end if;
              execute immediate 'create table TestTable(id nvarchar2(32) primary key)';
            end;";
          command.ExecuteNonQuery();
        }
        connection.Close();
      }
    }

    // Opens session sessionCount times, inserts insertCount rows in each session.
    private static void Insert(int sessionCount, int insertCount)
    {
      for (int sessionNumber = 0; sessionNumber < sessionCount; sessionNumber++)
        using (var connection = new OracleConnection(ConnectionString)) {
          connection.Open();
          using (var transaction = connection.BeginTransaction(IsolationLevel.Serializable)) {
            for (int insertNumber = 0; insertNumber < insertCount; insertNumber++)
              using (var command = connection.CreateCommand()) {
                command.BindByName = true;
                command.CommandText = "insert into TestTable (id) values(:id)";
                var id = Guid.NewGuid().ToString("N");
                var parameter = new OracleParameter("id", OracleDbType.NVarchar2) {Value = id};
                command.Parameters.Add(parameter);
                command.Transaction = transaction;
                command.ExecuteNonQuery();
              }
            transaction.Commit();
          }
          connection.Close();
        }
    }

    static void Main(string[] args)
    {
      int iteration = 0;
      while (true) {
        Console.WriteLine("Running iteration: {0}", iteration);
        try {
          Recreate();
          Insert(10, 100);
          Console.WriteLine("No error");
        }
        catch (Exception exception) {
          Console.WriteLine(exception.Message);
        }
        iteration++;
      }
    }
  }
}

Этот код запускает бесконечный цикл.
На каждой итерации он выполняет следующие действия 10 раз:

  • Открыть сеанс

  • Вставьте 100 строк со случайными данными

  • Закрытие сеанса

  • Отображает сообщение о том, что ошибки не было.

Если возникает ошибка, исключение поймано и его сообщение напечатано и то выполняется следующая итерация.

Вот пример вывода. Как вы можете видеть, ORA-08177 чередуется с успешными вмешательствами случайным образом.

Running iteration: 1
No error
Running iteration: 2
ORA-08177: can't serialize access for this transaction
Running iteration: 3
ORA-08177: can't serialize access for this transaction
Running iteration: 4
ORA-08177: can't serialize access for this transaction
Running iteration: 5
ORA-08177: can't serialize access for this transaction
Running iteration: 6
ORA-08177: can't serialize access for this transaction
Running iteration: 7
No error
Running iteration: 8
No error
Running iteration: 9
ORA-08177: can't serialize access for this transaction
Running iteration: 10
ORA-08177: can't serialize access for this transaction
Running iteration: 11
ORA-08177: can't serialize access for this transaction
Running iteration: 12
ORA-08177: can't serialize access for this transaction
Running iteration: 13
ORA-08177: can't serialize access for this transaction
Running iteration: 14
ORA-08177: can't serialize access for this transaction
Running iteration: 15
ORA-08177: can't serialize access for this transaction
Running iteration: 16
ORA-08177: can't serialize access for this transaction
Running iteration: 17
No error
Running iteration: 18
No error
Running iteration: 19
ORA-08177: can't serialize access for this transaction
Running iteration: 20
No error

Я запускаю Oracle 11.1.0.6.0 и используя ODP.NET 2.111.6.20.
Изменение уровня изоляции до ReadCommited устраняет проблему, но я действительно хочу запустить ее на уровне Serializable.
Похоже на Я не одинок с этой проблемой, но ответ не был дан, поэтому я снова спрашиваю.
Что я делаю неправильно и как я могу это исправить?

редактировать APC

Чтобы кто-то еще не лаял неправильное дерево, размещенный образец кода является всего лишь генератором ошибок ORA-8177. По-видимому, фактический код отличается; в частности, падение и воссоздание таблиц - это красная селедка.

4b9b3361

Ответ 1

В комментариях пользователь Gary опубликовал ссылку на тему, объясняющую это странное поведение. Вкратце, иногда при индексации реструктуризации данные отмены становятся недоступными. Любая транзакция, которая выполняется на уровне сериализации изоляции и запрашивает данные, которые каким-то образом связаны с этим индексом, получит ORA-08177. Это полуобъект с половиной ошибки Oracle. ROWDEPENDENCIES уменьшает вероятность получения этой ошибки. Для моего приложения я просто переключился на уровень ReadCommited для больших загрузок данных. Кажется, что нет другого способа полностью избежать этой проблемы.

Спасибо, Гэри, я подтвердил свой ответ на другой вопрос.

Ответ 2

Итого переписывать (сначала лаяли по неправильному дереву).

Уровень изоляции SERIALIZABLE захватывает слот в списке заинтересованных транзакций. Если Oracle не может получить слот, то он бросает ORA-8177. Количество доступных слотов ITL контролируется INITRANS и MAXTRANS. Согласно документация:

Чтобы использовать сериализуемый режим, INITRANS должно быть установлено как минимум 3.

Это должно быть установлено как для таблицы, так и для ее индексов. Итак, каковы ваши настройки INITRANS? Конечно, ваш пример кода использует значение по умолчанию (1 для таблиц, 2 для индексов).