OracleCommand с OracleDependency ждет вечно

1

Примечание. Соответствующий вопрос здесь не имеет решения

Имейте в виду, что я не являюсь экспертом по Oracle или программированию против Oracle. Это моя тестовая среда. У меня есть одна таблица в схеме STVM, называемая STVM_NOTIFICATION. Вот как это выглядит:

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")
)

Я создал следующую последовательность и триггер для создания уникального идентификатора для каждой строки:

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;

Затем я установил следующие гранты для 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;

Вставка в таблицу работает очень хорошо. Ниже приведено простое тестовое приложение, предоставленное документацией Oracle по OracleDependency (с небольшими модификациями), которое я использую для проверки уведомлений:

namespace SqlDependencyTest
{
    class Program
    {
        private static string oraConnectionString = @"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 string oraQuery = "SELECT ID FROM STVM_NOTIFICATION";
        private static OracleDependency oraDependency;

        static void Main(string[] args)
        {
            using (OracleConnection oraConnection = new OracleConnection(oraConnectionString))
            {
                try
                {
                    // Open the connection
                    oraConnection.Open();

                    // Create the Select command retrieving 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 (string resource in args.ResourceNames)
            {
                Console.WriteLine(resource);
            }

            Console.WriteLine("\n Details:");
            Console.Write(new string('*', 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));
            }
        } 
    }
}

Это действительно работает! Однако: только до тех пор, пока другой процесс не выполнит вставку в той же таблице, по крайней мере, это мое наблюдение. Я выполнил вставку несколько раз из SQL Developer, проблема исправлена, и я сделал это более 10 раз.

Как только другой процесс выполняет указанную вставку, мое приложение зависает в следующем выражении: OracleDataReader reader = selectCommand.ExecuteReader(CommandBehavior.Default);

Я могу четко видеть, что обратный вызов уведомления регистрируется в DBA_CHANGE_NOTIFICATION_REGS: net8://(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.226)(PORT=64268))?PR=0

Соединение остается открытым на сервере Oracle в течение 30 минут, пока не истечет время:

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

Всякий раз, когда это происходит, единственным решением является уничтожение сеансов, REVOKE CHANGE NOTIFICATION и повторное включение. После этого мое приложение снова будет работать, пока другой процесс не выполнит вставку.

Единственный ключ, который у меня есть, - это событие enq: TM - contention, но большинство людей называют это индикатором неиндексированных внешних ключей в таблице. Учитывая, что в настоящее время у меня есть только одна таблица для целей тестирования, здесь не так много внешних ключей.

Есть ли у кого-нибудь идеи относительно соответствия enq: TM - раздора?

Я использую:

(На стороне сервера)

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

(Сторона клиента)

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

ОБНОВЛЕНИЕ: после нескольких дней попыток выяснить, в чем проблема, и не найти проблему, я решил перейти и искать другую технику. Великое предложение Кристиана не дало никаких результатов, и решение сомнительной сделки, как предположил Джастин, не привело меня к сожалению. Я знаю пару DBA Oracle на работе, которые изначально были готовы помочь, но они быстро отбросили меня, как только я упомянул.NET.

  • 0
    Процесс, выполняющий insert фиксирует изменение?
  • 0
    @JustinCave Я только что проверил, и в V $ TRANSACTION действительно есть активная запись. Я посмотрю, что произойдет, если я уберу это.
Показать ещё 4 комментария
Теги:
odp.net

1 ответ

0

Там ошибка в версии базы данных, которую вы используете, может быть связана. Вы можете проверить, действительно ли это происходит, выполнив следующие действия в виде SYSDBA: изменить системный набор событий 10867 контекста имени навсегда, уровень 1 ';

Это будет продолжаться до выключения. Если проблема уходит, вы попадаете в ошибку.

Вы не должны оставлять это включенным, скорее вам нужно обновить базу данных, а также исправить ODP.NET.

  • 0
    Я обязательно проверю это, как только смогу. Спасибо за предложение.
  • 0
    редактировать - я вижу, вы попробовали ...
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню