oracle odp.net ora-08177

oracle - Obtención aleatoria de ORA-08177 con una sola sesión activa



odp.net (2)

Estoy ejecutando un programa que crea una tabla y luego inserta algunos datos.
Este es el único programa que accede a la base de datos.
Estoy recibiendo ORA-08177 al azar.
El código real es algo complejo, pero he escrito un programa simple que reproduce este comportamiento.

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++; } } } }

Este código ejecuta ciclo infinito.
En cada iteración realiza las siguientes acciones 10 veces:

  • Sesión abierta

  • Insertar 100 filas con datos aleatorios

  • Cerrar la sesión

  • Muestra un mensaje que dice que no ocurrió ningún error.

Si se produce un error, la excepción se captura y su mensaje se imprime y, a continuación, se ejecuta la siguiente iteración.

Aquí está la salida de la muestra. Como puede ver, ORA-08177 se intercala aleatoriamente con interaciones exitosas.

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

Estoy ejecutando Oracle 11.1.0.6.0 y estoy usando ODP.NET 2.111.6.20.
Cambiar el nivel de aislamiento a ReadCommited soluciona el problema, pero realmente quiero ejecutar esto en el nivel Serializable .
Parece que no estoy solo con este problema, pero la respuesta no se dio, así que vuelvo a preguntar.
¿Qué estoy haciendo mal y cómo puedo arreglar esto?

editado por APC

Para evitar que alguien más ladre el árbol equivocado, el ejemplo de código publicado es solo un generador de errores ORA-8177. Al parecer, el código real es diferente; Específicamente, la eliminación y recreación de tablas es una pista falsa.


En los comentarios, el usuario Gary publicó un enlace al hilo que explica este extraño comportamiento. En breve, a veces, durante la reestructuración del índice, los datos de deshacer dejan de estar disponibles. Cualquier transacción que se ejecute en un nivel de aislamiento serializable y solicite los datos que de alguna manera estén relacionados con este índice obtendrá ORA-08177. Esta es una característica de medio error de Oracle. ROWDEPENDENCIES reduce la posibilidad de obtener este error. Para mi aplicación, simplemente cambié al nivel ReadCommited para cargas de datos grandes. Parece que no hay otra manera de escapar de este problema por completo.

Gracias, Gary, he subido tu respuesta a otra pregunta.


Reescritura total (habiendo ladrado el árbol equivocado la primera vez).

El nivel de aislamiento SERIALIZABLE toma un espacio en la Lista de transacciones interesadas. Si Oracle no puede obtener una ranura, lanza ORA-8177. El número de ranuras ITL disponibles está controlado por INITRANS y MAXTRANS. Según la documentación :

Para usar el modo serializable, INITRANS debe configurarse en al menos 3.

Esto debe establecerse tanto para la tabla como para sus índices. Entonces, ¿cuáles son sus ajustes INITRANS? Ciertamente, su código de muestra utiliza el valor predeterminado (1 para tablas, 2 para índices).