2010-02-24 2 views
20

Je cours un programme qui crée une table et insère ensuite des données.
Ceci est le seul programme qui accède à la base de données.
Je reçois ORA-08177 au hasard.
Le code réel est quelque peu complexe, mais j'ai écrit un programme simple qui reproduit ce comportement.Obtenir aléatoirement ORA-08177 avec une seule session active

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

Ce code exécute un cycle infini.
A chaque itération, il effectue les actions suivantes 10 fois:

  • séance ouverte

  • Insérer 100 lignes avec des données aléatoires

  • session Fermer

  • Affiche un message indiquant que aucune erreur n'est survenue

Si une erreur se produit, l'exception est interceptée et son message est imprimé et , puis l'itération suivante est exécutée.

Voici un exemple de sortie. Comme vous pouvez le voir ORA-08177 est entrelacé avec des interations réussies au hasard.

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 

Je cours Oracle 11.1.0.6.0 et en utilisant ODP.NET 2.111.6.20.
La modification du niveau d'isolation à ReadCommited résout le problème, mais je veux vraiment l'exécuter au niveau Serializable.
On dirait I'm not alone avec ce problème, mais la réponse n'a pas été donnée, alors je demande à nouveau.
Qu'est-ce que je fais de mal et comment pourrais-je résoudre ce problème?

modifier par APC

Pour empêcher quiconque d'aboyer autre le mauvais arbre, l'exemple de code affiché est juste un générateur d'erreurs ORA-8177. Apparemment, le code réel est différent. plus précisément, l'abandon et la recréation de tableaux est un faux-fuyant.

Répondre

5

Dans les commentaires utilisateur Gary a posté un lien vers le fil qui explique ce comportement étrange. Peu de temps, parfois, lors de la restructuration de l'index, l'annulation des données devient indisponible. Toute transaction qui s'exécute au niveau d'isolation sérialisable et demande les données qui sont en quelque sorte liées à cet index obtiendra ORA-08177. C'est une demi-fonctionnalité d'un demi-bug d'Oracle. ROWDEPENDENCIES réduit les chances d'obtenir cette erreur. Pour mon application, je suis simplement passé au niveau ReadCommited pour les gros téléchargements de données. Il semble qu'il n'y ait pas d'autre moyen d'échapper à ce problème complètement.

Merci, Gary, j'ai mis votre réponse à une autre question.

5

Réécriture totale (ayant abîmé le mauvais arbre la première fois).

Le niveau d'isolation SERIALIZABLE saisit un emplacement dans la liste des transactions concernées. Si Oracle ne peut pas obtenir de slot, il lance ORA-8177. Le nombre d'emplacements ITL disponibles est contrôlé par INITRANS et MAXTRANS. Selon the documentation:

Pour utiliser le mode sérialisable, INITRANS doit être réglé sur au moins 3.

Ce doit être défini pour la table et ses index. Alors, quels sont vos paramètres INITRANS? Votre exemple de code utilise certainement la valeur par défaut (1 pour les tables, 2 pour les index).

+0

C'est juste un exemple de programme qui démontre ce comportement étrange. L'application réelle ne supprime pas les tables à chaque démarrage. Mais la suite de tests pour cette application réelle recrée toutes les tables et télécharge ensuite les données de test. À ce moment-là, je suis confronté à ce problème. –

+0

@Denis: Ensuite, laissez les tables à cet endroit, et récupérez les erreurs "table existe déjà" dans le code. –

+0

De telles solutions de contournement ne sont pas ce que prévu ici. Il est nécessaire de connaître la cause réelle. // Je suis également conscient de la question –

Questions connexes