Объясните, пожалуйста, вот этот абзац из документации
WAITANY procedure. If you use the WAITANY procedure, and if a signalling session does a signal but does not commit within one second of the signal, a polling loop is required so that this uncommitted alert does not camouflage other alerts. The polling loop begins at a one second interval and exponentially backs off to 30-second intervals.
Я правильно понимаю, что тут говорится о том, что при вызове WAITANY на сервере поток опрашивает наличие событий, через определенные интервалы? И если я вызвал WAITANY с достаточно большим таймаутом, то при возникновении события ко мне придет уведомление только после истечения текущего интервала запроса? Т.е. на сервере выполняется примерно такой код
function WaitAny(ATimeout) {
const intervals = [0, 1, ....., 30);
for (i = 0; i < intervals.length; i++) {
Sleep(min(intervals[i], ATimeout))
if (IsExistsEvents())
return 0;
ATimeout -= intervals[i];
if (ATimeout <= 0)
return 1;
}
maxInterval = intervals[intervals.length - 1];
while (ATimeout > 0) {
Sleep(min(maxInterval, ATimeout))
if (IsExistsEvents())
return 0;
ATimeout -= maxInterval;
}
return 1;
}
Ответ
TL;DR: Нет, при получении события, оповещение о нём будет практически сразу же передано в обработчик, то есть возврат из функции waitany. Но в определённых случаях задержка возможна (см. далее).
Передача события (signal) между отправителем и приёмником в dbms_alert транзакциональна и состоит из двух частей:
Не транзакциональная передача события посредством database pipe механизма (реализовано на dbms_pipe).
Подтверждение события посредством commit в отправителе. В приёмнике это реализовано стандартным lock меанизмом БД.
В нормальном случае, приёмник ждёт получение события из pipe. При его получении, оно только регистрируется как принятое, но не подтверждённое. При наличии таких событий в приёмнике, waitany опрашивает статус сессии на предмет завершения транзакции, в которой событие было послано.
Если транзакция завершается с commit, то waitany завершается и передаёт текущее событие в приёмник для дальнейшей обработки, в противном случае - rollback - событие удаляется из очереди принятых в waitany.
В исключительном, случае, о котором идёт речь в цитате из вопроса, событие ещё не зафиксировано. т.е. не подтверждено в сессии отправителя. Так как бесконечное ожидание на подтверждение будет блокировать получение других событий из pipe, начинается опрос с интервалом начиная с 1-ой с возрастанием до 30-ти секунд. То есть приёмник ожидает подтверждения завершения транзакции с текущим интервалом ожидания (1..30 сек.) и если оно не происходит, возвращается к считыванию из pipe новых событий, после чего цикл повторяется.
Почему последний случай исключительный? Если событие отправленно, но не зафиксировано сразу же, то это, как будет видно в примере ниже, не обеспечит гарантированной асинхронной обработки событий. Поэтому, или устранить задержку между signal и commit, или, если это не представляется возможным, отказаться от dbms_alert и посмотреть в сторону Advanced gueuing (dbms_aq).
Пример для наглядности. Понадобится три сесси: приёмнк, "ленивый" отправитель (PROCL=lazy SID=26) и "правильный" отправитель (PROCQ=quick SID=270).
Запустим приёмник событий:
SQL> set serveroutput on size unlimited
exec dbms_alert.register ('PROCQ'); -
dbms_alert.register ('PROCL'); -
dbms_alert.register ('EXIT');
exec receiveSignals
В отправителе SID=26 пошлём событие с задержкой подтверждения:
SQL> exec sendSignal ('PROCL',delayed=>true)
PROCL sent 14:02:10.482 from 26 delayed
В отправителе SID=270 пошлём событие и зафиксируем его сразу:
SQL> exec sendSignal ('PROCQ',delayed=>false)
PROCQ sent 14:03:20.842 from 270
Вернёмся в SID=26 и подтвердим событие:
SQL> exec sendSignal (null,delayed=>false)
none sent 14:03:34.272 from 26
В отправителе SID=270 пошлём событие EXIT:
SQL> exec sendSignal ('EXIT',delayed=>false)
EXIT sent 14:03:41.849 from 270
Вывод в приёмнике после приёма события EXIT:
stat=0,sig=PROCL,received=14:03:34.274: msg=sent 14:02:10.482 from 26 delayed
stat=0,sig=PROCQ,received=14:03:34.275: msg=sent 14:03:20.842 from 270
stat=0,sig=EXIT,received=14:03:41.852: msg=sent 14:03:41.849 from 270
Видно, что задержка в SID=26 вызвана поздним подтверждением и была получена в приёмнике сразу же после commit в отправителе (14:03:34). Но ожидание на подтверждение привело к задержке почти 15 сек на получения события от SID=270. (отпр. 14:03:20, получ. 14:03:34), т.е функция waitany в момент отправки подтверждённого события от SID=270, находилась в ожидании подтверждения от SID=26.
PS: Этот ответ не является переводом, ни прямым, ни вольным, ответа сотрудника компании Oracle на аналогичный вопрос ТС на engl. SO, он скорее всего попытка более наглядного пояснения ситуации.
Функции, которые использованы в воиспроизводимом примере выше:
create or replace procedure receiveSignals is
sig varchar2 (32);
stat number;
msg varchar2 (64);
begin
loop
dbms_alert.waitany (sig, msg, stat);
dbms_output.put_line (
'stat='||stat||',sig='||sig||',received='||to_char (systimestamp, 'hh24:mi:ss.ff3')||': msg='||msg);
exit when sig = 'EXIT';
end loop;
end;
/
create or replace procedure sendSignal (sig varchar2, delayed boolean := true) is
msg varchar2 (1800) := 'sent '||to_char (systimestamp, 'hh24:mi:ss.ff3')||' from '||
sys_context ('USERENV', 'SID')||case when delayed then ' delayed' end;
begin
if sig is not null then dbms_alert.signal (sig, msg); end if;
if not delayed then commit; end if;
dbms_output.put_line (case when sig is null then 'none' else sig end||' '||msg);
end;
/