Restore failed Oracle JMS queue messages

Wiederherstellung von durchgefallenen JMS Nachrichten

Angenommen wir haben eine Oracle JMS Queue mit dem Namen „QUEUE_NAME“ und eine JMS Queue Tabelle „QUEUE_TABLE_NAME“, beim Erstellen von Queue hat Oracle automatisch eine Exception Queue angelegt mit dem Namen „AQ$_QUEUE_NAME_E“ (und Exception Queue wurde gestartet)…

Aus verschiedenen Gründen kann es passieren, dass die Messages in Exception Queue landen, zum Beispiel wegen dem Fehler auf der Empfänger Seite. Nach N Versuchen die Nachricht zuzustellen, wid die Nachricht in die Exception Queue verschoben, die meistens von der gleichen Tabelle bedient wird (Oracle verwendet Feld Q_NAME in Queue Tabelle als Queue Identifikator, deswegen kann die gleiche Tabelle für mehrere Queues’s verwendet werden)

Die Nachrichten können aber sehr wichtig sein, und müssen auf jeden Fall zugestellt und abgearbeitet werden.
Am einfachsten kann man Nachrichten zurück in die normale Queue verschieben, in dem man die entsprechende Felder in der Queue Tabelle anpasst (mit dem folgendem Script)

update QUEUE_TABLE_NAME
set q_name = 'QUEUE_NAME',
    state = 0, 
    retry_count = 0, 
    exception_queue = null
where state = 3
  and q_name like 'AQ$_QUEUE_NAME_E';

Alternative Methode (sauberer, aber nicht immer möglich), ist die Verwendung von PL/SQL und DBMS_AQ Package. Dabei werden alle Nachrichten in der Schleife aus einem Queue gelesen (in unserem Fall aus Exception Queue) und in ein anderes Queue geschrieben (zurück in das normalle Queue).

 DECLARE
  dequeue_options dbms_aq.dequeue_options_t;
  enqueue_options dbms_aq.enqueue_options_t;
  message_properties dbms_aq.message_properties_t;
  message_handle RAW(32);
  message  SYS.AQ$_JMS_OBJECT_MESSAGE; -- Type of your message queue e.g. AQ$_JMS_MAP_MESSAGE

  deq_qname varchar2(50) := 'AQ$_QUEUE_NAME_E';
  enq_qname varchar2(50) := 'QUEUE_NAME';

  ex_no_messages exception;
  ex_dequeue exception;
  pragma exception_init(ex_no_messages, -25263);
  pragma exception_init(ex_dequeue, -25228);
  msg_count number := 0;
 BEGIN
  dequeue_options.wait := DBMS_AQ.NO_WAIT;

  LOOP
    --Loop over all message in source queue
    dbms_aq.dequeue(queue_name         => deq_qname,
                    dequeue_options    => dequeue_options,
                    message_properties => message_properties,
                    payload            => message,
                    msgid              => message_handle);
    -- Put message into destination queue
    dbms_aq.enqueue(queue_name         => enq_qname,
                    enqueue_options    => enqueue_options,
                    message_properties => message_properties,
                    payload            => message,
                    msgid              => message_handle);
   
    msg_count := msg_count + 1;
  
    DBMS_OUTPUT.PUT_LINE('Processed ' || msg_count || ' messages');
    
  END LOOP;

 EXCEPTION
  -- exception handling
  WHEN ex_no_messages THEN
    DBMS_OUTPUT.PUT_LINE('No of Messages Moved: ' || msg_count);
    COMMIT;
  WHEN ex_dequeue THEN
    DBMS_OUTPUT.PUT_LINE('No of Messages Moved: ' || msg_count);
    COMMIT;
 END;

Bei dieser Methodik muss der User das Recht auf DBMS_AQ Package besitzen und beide Queues müssen mit entsprechenden enqueu/dequeue Einstellungen gestartet sein (was auch nicht immer erwünscht ist). Ausserdem, muss man eventuell die geänderte Reihenvolge der Nachrichten berücksichtigen, weil die Nachrichten in diesem Fall ein neues „enque“ Zeitstempel bekommen, was bei der Ersten Methodik nicht verändert wird.

Veröffentlicht in Java, Oracle