OracleCommand avec OracleDependency en attente pour toujours

Remarque: la question pertinente ici n’a pas de solution

N’oubliez pas que je ne suis pas un expert d’Oracle ni de la programmation contre Oracle. Ceci est mon environnement de test. J’ai une seule table dans le schéma STVM appelée STVM_NOTIFICATION. Voici à quoi ça ressemble:

CREATE TABLE STVM_NOTIFICATION ( "ID" NUMBER NOT NULL, "PROPERTYNAME" VARCHAR2(16 BYTE) NOT NULL, "PROPERTYVALUE" VARCHAR2(16 BYTE) NOT NULL, "ACTION" VARCHAR2(32 BYTE) NOT NULL, "POSTDATE" TIMESTAMP (6) NOT NULL, "SENT" CHAR(1 BYTE) NOT NULL, ADD CONSTRAINT "PK_ID" PRIMARY KEY ("ID") ) 

J’ai créé la séquence et le déclencheur suivants pour créer une identité unique pour chaque ligne:

 CREATE SEQUENCE STVM_NOTIF_SEQ START WITH 1 INCREMENT BY 1 CACHE 100; CREATE OR REPLACE TRIGGER STVM_NOTIF_ID_TRG BEFORE INSERT ON STVM_NOTIFICATION FOR EACH ROW BEGIN :NEW.ID := STVM_NOTIF_SEQ.NEXTVAL; END; 

J’ai ensuite défini les subventions suivantes pour la STVM:

 GRANT CREATE SESSION TO STVM; GRANT CREATE TABLE TO STVM; GRANT CREATE VIEW TO STVM; GRANT CREATE ANY TRIGGER TO STVM; GRANT CREATE ANY PROCEDURE TO STVM; GRANT CREATE SEQUENCE TO STVM; GRANT CREATE SYNONYM TO STVM; GRANT CHANGE NOTIFICATION TO STVM; 

L’insertion dans la table fonctionne très bien. Vous trouverez ci-dessous l’application de test simple fournie par la documentation Oracle sur OracleDependency (avec de petites modifications) que j’utilise pour tester les notifications:

 namespace SqlDependencyTest { class Program { private static ssortingng oraConnectionSsortingng = @"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.164)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=STVM;Password=STVM;"; private static ssortingng oraQuery = "SELECT ID FROM STVM_NOTIFICATION"; private static OracleDependency oraDependency; static void Main(ssortingng[] args) { using (OracleConnection oraConnection = new OracleConnection(oraConnectionSsortingng)) { try { // Open the connection oraConnection.Open(); // Create the Select command resortingeving all data from the STVM_NOTIFICATION table. OracleCommand selectCommand = new OracleCommand(oraQuery, oraConnection); // Create an OracleDependency object and set it to track the result set returned by selectCommand. oraDependency = new OracleDependency(selectCommand); // Setting object-based change notification registration oraDependency.QueryBasedNotification = false; // When the IsNotifiedOnce property is true, only the first change // of the traced result set will generate a notification. // Otherwise, notifications will be sent on each change // during the selectCommand.Notification.Timeout period. selectCommand.Notification.IsNotifiedOnce = true; // Set the event handler to the OnChange event. oraDependency.OnChange += new OnChangeEventHandler(OnChange); // When the select command is executed at the first time, a notification // on changes of the corresponding result set is registered on the server. //selectCommand.CommandTimeout = 5; OracleDataReader reader = selectCommand.ExecuteReader(CommandBehavior.Default); // Set and execute an insert command. The Dept table data will be changed, // and a notification will be sent, causing the OnChange event of the 'dependency' object. OracleCommand insertCommand = new OracleCommand ("INSERT INTO STVM_NOTIFICATION (PROPERTYNAME, PROPERTYVALUE, ACTION, POSTDATE, SENT) VALUES ('Heartbeat', 'NOK', 'REFRESH', SYSDATE, 'N')", oraConnection); insertCommand.ExecuteNonQuery(); // Pause the current thread to process the event. Console.Read(); } catch (Exception e) { Console.WriteLine("Exception encountered: {0}", e.Message); } // Always try to both remove the notification registration // oraConnection.Close() is autimatically called by .Dispose at the end of our 'using' statement finally { try { oraDependency.RemoveRegistration(oraConnection); } catch (Exception e) { Console.WriteLine("Exception encountered: {0}", e.Message); } } } } // A simple event handler to handle the OnChange event. // Prints the change notification details. private static void OnChange(Object sender, OracleNotificationEventArgs args) { DataTable dt = args.Details; Console.WriteLine("The following database objects were changed:"); foreach (ssortingng resource in args.ResourceNames) { Console.WriteLine(resource); } Console.WriteLine("\n Details:"); Console.Write(new ssortingng('*', 80)); for (int rows = 0; rows < dt.Rows.Count; rows++) { Console.WriteLine("Resource name: " + dt.Rows[rows].ItemArray[0]); string type = Enum.GetName(typeof(OracleNotificationInfo), dt.Rows[rows].ItemArray[1]); Console.WriteLine("Change type: " + type); Console.Write(new string('*', 80)); } } } } 

Cela fonctionne réellement! Cependant: seulement si un autre processus effectue une insertion sur la même table , c’est ce que je constate. J’ai effectué l’insertion plusieurs fois à partir de SQL Developer, le problème est reproductible et je l’ai déjà fait plus de 10 fois.

Dès qu’un autre processus effectue ladite insertion, mon application se bloque sur l’instruction suivante: OracleDataReader reader = selectCommand.ExecuteReader(CommandBehavior.Default);

Je vois clairement que le rappel de notification est enregistré dans DBA_CHANGE_NOTIFICATION_REGS : net8://(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.226)(PORT=64268))?PR=0

La connexion rest ouverte sur le serveur Oracle pendant 30 minutes jusqu’à l’expiration du délai imparti:

 SELECT USERNAME, PROGRAM, BLOCKING_SESSION_STATUS, BLOCKING_INSTANCE, BLOCKING_SESSION, EVENT FROM V$SESSION WHERE USERNAME = 'STVM' USERNAME PROGRAM BLOCKING_SESSION_STATUS BLOCKING_INSTANCE BLOCKING_SESSION EVENT STVM SQL Developer NO HOLDER (null) (null) SQL*Net message from client STVM OracleDependencyTest.vshost.exe VALID 1 50 enq: TM - contention 

Chaque fois que cela se produit, la seule solution consiste à supprimer les sessions, à annuler la notification de changement et à l’octroyer à nouveau. Après cela, mon application fonctionnera à nouveau, jusqu’à ce qu’un autre processus effectue une insertion.

Le seul indice que j’ai est l’événement enq: TM – conflit , mais la plupart des gens y voient un indicateur des clés étrangères non indexées dans la table. Étant donné que je n’ai actuellement qu’une seule table à des fins de test, il n’y a pas beaucoup de clés étrangères à utiliser ici.

Quelqu’un a-t-il une idée de la pertinence de la contention enq: TM ?

J’utilise:

(Du côté serveur)

 Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production PL/SQL Release 11.2.0.2.0 - Production "CORE 11.2.0.2.0 Production" TNS for Linux: Version 11.2.0.2.0 - Production NLSRTL Version 11.2.0.2.0 - Production Oracle.DataAccess 

(Côté client)

 Oracle Call Interface (OCI) 11.2.0.1.0 Oracle Client 11.2.0.1.0 Oracle Data Provider for .NET 11.2.0.1.0 Oracle JDBC/OCI Instant Client 11.2.0.1.0 Oracle JDBC/THIN Interfaces 11.2.0.1.0 Oracle SQL Developer 11.2.0.1.0 SQL*Plus 11.2.0.1.0 SQL*Plus Files for Instant Client 11.2.0.1.0 

MISE À JOUR : Après des jours passés à essayer de déterminer le problème et à ne pas le trouver, j’ai décidé de passer à autre chose et de rechercher une technique différente. La grande suggestion de Christian n’a donné aucun résultat, et la résolution de la transaction douteuse telle que suggérée par Justin ne m’a malheureusement pas amené davantage. Je connais quelques DBA Oracle au travail qui étaient prêts à aider au départ, mais ils m’ont rapidement renvoyé dès que j’ai mentionné .NET.

Il existe un bogue dans la version de la firebase database que vous utilisez qui pourrait être lié. Vous pouvez vérifier si tel est le cas en exécutant la commande suivante sous SYSDBA: modifiez les événements du jeu système ‘10867 contexte de nom de trace pour toujours, niveau 1’;

Cela durera jusqu’à l’arrêt. Si le problème disparaît, vous rencontrez le bogue.

Vous ne devriez pas laisser cette option activée, mais plutôt mettre à niveau la firebase database et aussi appliquer un correctif à ODP.NET