세션 간 통신 DBMSALERT

41249 단어 alert
친구가 쓴 기술 문장을 전달하는 것이 비교적 괜찮다
Inter-Session Communication DBMS_ALERT 세션 간 통신 DBMSALERT
 
Inter-Session Communication DBMS_ALERT
세션 간 통신 DBMSALERT
질문: 어떻게 프로그램이 데이터가 바뀌었다는 것을 알 수 있습니까?
대답:
1. 조회표, 조회count(*) 등 기록이 증가했는지 확인
2. 감사표(audit table)를 조회하고count(*) 또는 타임 스탬프 필드를 조회하여 표가 업데이트되었는지 확인한다.
3.DBMS_ALERT
4.DBMS_AQ ...
dbms_alert는 Oracle 버전 7에 처음 등장하여 데이터베이스 세션 간의 통신을 실현하는 방식이다.
다중 사용자에게 데이터베이스 이벤트(database 이벤트) 즉 경보(alerts)를 방송하는 메커니즘을 제공하였다.
의존dbmspipe 및dbmslock은 실현했다
사용 가능, 때로는 데이터베이스 테이블의 변화를 감시하려면 응용 프로그램이 반복적으로 조회해야 하기 때문에 비용이 많이 든다
dbms_alert의 메커니즘은 변화가 발생할 때 데이터베이스가 주동적으로 응용 프로그램에 통지할 수 있도록 한다
참조 문서:
13 DBMS_ALERT :
Alerts are transaction-based. This means that the waiting session is not alerted until the transaction signalling the alert commits. There can be any number of concurrent signalers of a given alert, and there can be any number of concurrent waiters on a given alert.
경보는 사무에 기초한 것이다.이것은 경보를 발동한 세션이 업무를 제출할 때까지 경보를 기다리는 세션이 경보를 받았다는 것을 의미한다.지정된 경보는 임의의 발기자가 동시에 있을 수도 있고, 임의의 수용자가 있을 수도 있다.
A waiting application is blocked in the database and cannot do any other work.
대기 중인 프로그램이 막혀서 다른 동작을 할 수 없습니다
An application can register for multiple events and can then wait for any of them to occur using the WAITANY procedure.
하나의 응용 프로그램은 여러 개의 이벤트를 등록한 다음에 WAITANY 저장 프로세스를 사용하여 그들 중 임의의 이벤트를 기다릴 수 있다(여러 개의 관계, 한 개의 경보는 여러 개의 수신자, 한 개의 수신자는 여러 개의 경보를 받을 수 있다)
An application can also supply an optional timeout parameter to the WAITONE or WAITANY procedures. A timeout of 0 returns immediately if there is no pending alert.
하나의 응용 프로그램도 WAITONE이나 WAITANY 저장 프로세스에 시간 초과 파라미터를 지정할 수 있습니다.만약 결정되지 않은 경보가 없다면, 시간 초과 파라미터가 0이면 즉시 되돌아옵니다.
The signalling session can optionally pass a message that is received by the waiting session.
신호를 보내는 세션은 대기 세션에 메시지를 전달할 수 있다.
Alerts can be signalled more often than the corresponding application wait calls. In such cases, the older alerts are discarded. The application always gets the latest alert (based on transaction commit times).
프로그램의 대기 호출보다 경보가 더 많이 울릴 수 있습니다.이런 상황에서 낡은 경보는 버려졌다.응용 프로그램은 항상 최신 경보를 받는다.
If the application does not require transaction-based alerts, the DBMS_PIPE package may provide a useful alternative.
애플리케이션에 트랜잭션 기반 경고가 필요하지 않으면 DBMSPIPE 패키지를 선택할 수 있습니다.
If the transaction is rolled back after the call to SIGNAL, no alert occurs.
SIGNAL 호출 후 트랜잭션이 롤백된 경우 경고가 발생하지 않습니다.
It is possible to receive an alert, read the data, and find that no data has changed. This is because the data changed after the prior alert, but before the data was read for that prior alert.
경보를 받고 데이터를 읽으면 데이터가 바뀌지 않을 수도 있다.이것은 데이터가 경보를 발령한 후 데이터를 읽기 전에 바뀌었기 때문이다(이게 어떻게 가능하지? 경보를 먼저 발령한 다음에 데이터를 바꿉니까? 경보를 발령하는 것이 사무 제출보다 빠를 수 있습니까?)
Usually, Oracle is event-driven; this means that there are no polling loops. There are two cases where polling loops can occur:
일반적으로 Oracle은 이벤트로 구동됩니다.이것은 윤문 순환이 없다는 것을 의미한다.교대 훈련 순환에는 두 가지 상황이 있다.
Shared mode. If your database is running in shared mode, a polling loop is required to check for alerts from another instance. The polling loop defaults to one second and can be set by the SET_DEFAULTS procedure.
공유 모드.만약 데이터베이스가 공유 모드에서 실행된다면, 다른 실례의 경보를 순환해서 검사해야 한다.기본 폴링 주기는 SETDEFAULT 스토리지 프로시저 설정
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 프로세스를 사용하고 경보가 발령되어 1초 안에 업무를 제출하지 않으면, 이 미제출 경보가 다른 경보를 막지 못하도록 윤문이 필요합니다.폴링 간격은 1초로 시작한 후 지수로 30초까지 증가한다.
테스트:
1. 권한 부여
conn / as sysdba
grant execute on dbms_alert to a;

2. 경고를 받는 세션을 새로 엽니다.
conn a/a
set pages 50000 line 130
set serveroutput on size unlimited

경고 등록
exec dbms_alert.register('alert_test');

3. 경고 정보 보기 SYS 사용자 실행
SQL> select * from dbms_alert_info;

NAME                           SID                            C
------------------------------ ------------------------------ -
MESSAGE
--------------------------------------------------------------------------------
ALERT_TEST                     065C00C00001                   N



SQL> desc dbms_alert_info
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 NAME                                      NOT NULL VARCHAR2(30)
 SID                                       NOT NULL VARCHAR2(30)
 CHANGED                                            VARCHAR2(1)
 MESSAGE                                            VARCHAR2(1800)

SQL>

DBMS_ALERT_INFO NAME: 경고 이름.같은 이름의 세션이 있을 수 있습니다. 즉, 여러 세션이 같은 경보 SID를 등록하고 받을 수 있습니다. DBMS 와 같습니다.SESSION.UNIQUE_SESSION_ID. 세 부분으로 나뉘는데 SID+SERIAL#+InstanceNumber How Is SID In DBMS_ALERT_INFO Related To SID In V$Session
select name
       , to_number(substr(sid,1,4),'xxxx') sid
       , to_number(substr(sid,5,4),'xxxx') serial#
       , to_number(substr(sid,9,4),'xxxx') instance#
  from dbms_alert_info;
NAME                                  SID    SERIAL#  INSTANCE#
------------------------------ ---------- ---------- ----------
ALERT_TEST                           1628        192          1

CHANGED: N: 경보가 발령되지 않았거나 경보가 수신되었습니다. Y: 경보가 발령되었습니다. 아직 수신되지 않았습니다. MESSAGE: 경보 메시지4.등록 후 경고 수신 대기
var v_messge varchar2(1000)
var v_status number
exec dbms_alert.waitone('alert_test', :v_messge, :v_status);
print :v_status :v_messge
 
 调用waitone后没有返回, 一直在等待警报


5.
打开另一个会话, 发出警报
conn a/a
set pages 50000 line 130
set serveroutput on size unlimited
exec dbms_alert.signal('alert_test', 'hello world!');
commit;
 
 事务提交后才发出警报


6.
等待接收警报的会话接收到了警报, 打印出信息
SQL> print :v_status :v_messge

  V_STATUS
----------
         0


V_MESSGE
----------------------------------------------------------------------------------------------------------------------------------
hello world!


조회dbmsalert_info 정보
SQL> select * from dbms_alert_info;

NAME                           SID                            C
------------------------------ ------------------------------ -
MESSAGE
--------------------------------------------------------------------------------
ALERT_TEST                     065C00C00001                   N
hello world!



여기는 CHANGED인지 N인지 경보가 울리자마자 바로 들어왔기 때문에 경보가 안 울리면 CHANGED는 Y7.질문: 등록 시 끊기 등록 경보가dbms 호출되면alert.register 때 멈췄습니다. 설명을 되돌려 주지 않기 전에 다른 세션에서 경고를 보냈지만 업무를 제출하지 않았습니다. 그러면 등록 과정이 막혔습니다. 이 업무를 제출하거나 굴러가면 해결할 수 있습니다. 참고: PL/SQL Session Hangs When Executing "Dbms_alert.Register(''Varchar'')" Statement DBMS_ALERT.WAITONE HANGS WAITING ON DBMS_ALERT.SIGNAL 108.여러 번 경고
exec dbms_alert.signal('alert_test', 'hello one')
commit;
exec dbms_alert.signal('alert_test', 'hello two')
commit;
SQL> select * from dbms_alert_info;

NAME                           SID                            C
------------------------------ ------------------------------ -
MESSAGE
--------------------------------------------------------------------------------
ALERT_TEST                     065C00C00001                   Y
hello two



최신 경보만 발효되어 경보를 받는 것을 보았다
SQL> exec dbms_alert.waitone('alert_test', :v_messge, :v_status);

PL/SQL procedure successfully completed.

SQL> print :v_status :v_messge

  V_STATUS
----------
         0


V_MESSGE
----------------------------------------------------------------------------------------------------------------------------------
hello two


또한 최신 수신 후 CHANGED 필드는 N9.삭제 경고 삭제 전에 정의된 경고는 등록된 세션에서 실행되어야 합니다.
exec dbms_alert.remove('alert_test')

경보를 등록한 세션이 종료되면 다른 세션으로 삭제할 수 없습니다.dbmsalert_info표에 이 기록이 존재합니다
SQL> select * from dbms_alert_info;

NAME                           SID                            C
------------------------------ ------------------------------ -
MESSAGE
--------------------------------------------------------------------------------
ALERT_TEST                     065C00C00001                   N
hello 2



참고 문서 How To Remove Alerts From DBMS_ALERTS_INFO Table에서 다시 등록하면 삭제할 수 있다고 했습니다. 이전 세션이 종료되었기 때문입니다. 이 때 다시 등록oracle는 자동으로 원래 기록을 삭제하고 새로운 (또는 덮어쓰기)를 추가하기 때문에 10.파이프를 삭제하면 경보가 삭제되지만dbmsalert에서 만든 파이프는 아직 시스템에 있으며 삭제되지 않았습니다.v$db 보기pipes:
SQL> col name for a40
SQL> select * from v$db_pipes;

   OWNERID NAME                                     TYPE     PIPE_SIZE
---------- ---------------------------------------- ------- ----------
           ORA$ALERT$065C00C20001                   PUBLIC        1687
           ORA$ALERT$066300730001                   PUBLIC        1687
           ORA$ALERT$066C021C0001                   PUBLIC        1687
           ORA$ALERT$066C021A0001                   PUBLIC        1687
           ORA$ALERT$065D00670001                   PUBLIC        1687

SQL>

DBMSALERT는 DBMS 를 사용하는 암시적 파이핑을 사용합니다.PIPE.PURGE가 비워지고 직접 삭제되지 않으며 시스템이 사용 가능한dbms 를 자동으로 삭제할 때까지 기다립니다.pipe.remove_pipe 지우기
declare
  v int;
begin
  for x in (select name from v$db_pipes where name like 'ORA$ALERT$%')
  loop
    v := dbms_pipe.remove_pipe(x.name);
  end loop;
end;
/

11. 소스 코드
package dbms_alert is

  ------------
  --  OVERVIEW
  --
  --  This package provides support for the asynchronous (as opposed to
  --  polling) notification of database events.  By appropriate use of
  --  this package and database triggers, an application can cause itself
  --  to be notified whenever values of interest in the database are
  --  changed.
  --
  --  For example, suppose a graphics tool is displaying a graph of some
  --  data from a database table.  The graphics tool can, after reading and
  --  graphing the data, wait on a database alert ('dbms_alert.waitone')
  --  covering the data just read.  The tool will automatically wake up when
  --  the data is changed by any other user.  All that is required is that a
  --  trigger be placed on the database table which then performs a signal
  --  ('dbms_alert.signal') whenever the trigger is fired.
  --
  --  Alerts are transaction based.  This means that the waiting session
  --  does not get alerted until the transaction signalling the alert commits.
  --
  --  There can be any number of concurrent signallers of a given alert, and
  --  there can be any number of concurrent waiters on a given alert.
  --
  --  A waiting application will be blocked in the database and cannot do
  --  any other work.
  --
  --  Most of the calls in the package, except for 'signal', do commits.
  --

  -----------
  --  EXAMPLE
  --
  --  Suppose the application wishes to graph average salaries, say by
  --  department, for all employees.  So the application needs to know
  --  whenever 'emp' is changed.  The application would look like this:
  --
  --      dbms_alert.register('emp_table_alert');
  --    readagain:
  --      
  --      dbms_alert.waitone('emp_table_alert', :message, :status);
  --      if status = 0 then goto readagain; else 
  --
  --  The 'emp' table would have a trigger similar to the following:
  --
  --    create trigger emptrig after insert or update or delete on emp
  --    begin
  --      dbms_alert.signal('emp_table_alert', 'message_text');
  --    end;
  --
  --  When the application is no longer interested in the alert, it does
  --    dbms_alert.remove('emp_table_alert');
  --  This is important since it reduces the amount of work required by
  --  the alert signaller.
  --
  --  If a session exits (or dies) while there exist registered alerts,
  --  they will eventually be cleaned up by future users of this package.
  --
  --  The above example guarantees that the application will always see
  --  the latest data, although it may not see every intermediate value.


  --------------
  --  VARIATIONS
  --
  --  The application can register for multiple events and can then wait for
  --  any of them to occur using the 'waitany' call.
  --
  --  An application can also supply an optional 'timeout' parameter to the
  --  'waitone' or 'waitany' calls.  A 'timeout' of 0 returns immediately
  --  if there is no pending alert.
  --
  --  The signalling session can optionally pass a message which will be
  --  received by the waiting session.
  --
  --  Alerts may be signalled more often than the corresponding application
  --  'wait' calls.  In such cases the older alerts are discaded.  The
  --  application always gets the latest alert (based on transaction commit
  --  times).
  --
  --  If the application does not require transaction based alerts, then the
  --  'dbms_pipe' package may provide a useful alternative
  --
  --  If the transaction is rolled back after the call to 'dbms_alert.signal',
  --  no alert will occur.
  --
  --  It is possible to receive an alert, read the data, and find that no
  --  data has changed.  This is because the data changed after the *prior*
  --  alert, but before the data was read for that *prior* alert.


  --------------------------
  --  IMPLEMENTATION DETAILS
  --
  --  In most cases the implementation is event-driven, i.e., there are no
  --  polling loops.  There are two cases where polling loops can occur:
  --
  --    1) Parallel mode.  If your database is running parallel mode then
  --       a polling loop is required to check for alerts from another
  --       instance.  The polling loop defaults to one second and is settable
  --       by the 'set_defaults' call.
  --    2) Waitany call.  If you use the 'waitany' call, and a signalling
  --       session does a signal but does not commit within one second of the
  --       signal, then 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.
  --
  --  This package uses the dbms_lock package (for synchronization between
  --  signallers and waiters) and the dbms_pipe package (for asynchronous
  --  event dispatching).

  -------------------------------------------------------
  --  INTERACTION WITH MULTI-THREADED AND PARALLEL SERVER
  --
  --  When running with the parallel server AND multi-threaded server, a
  --  multi-threaded (dispatcher) "shared server" will be bound to a
  --  session (and therefore not shareable) during the time a session has
  --  any alerts "registered", OR from the time a session "signals" an
  --  alert until the time the session commits.  Therefore, applications
  --  which register for alerts should use "dedicated servers" rather than
  --  connecting through the dispatcher (to a "shared server") since
  --  registration typically lasts for a long time, and applications which
  --  cause "signals" should have relatively short transactions so as not
  --  to tie up "shared servers" for too long.

  ------------
  --  SECURITY
  --
  --  Security on this package may be controlled by granting execute on
  --  this package to just those users or roles that you trust.  You may
  --  wish to write a cover package on top of this one which restricts
  --  the alertnames used.  Execute privilege on this cover package can
  --  then be granted rather than on this package.


  -------------
  --  RESOURCES
  --
  --  This package uses one database pipe and two locks for each alert a
  --  session has registered.


  ---------------------
  --  SPECIAL CONSTANTS
  --
  maxwait constant integer :=  86400000; -- 1000 days
  --  The maximum time to wait for an alert (essentially forever).


  ----------------------------
  --  PROCEDURES AND FUNCTIONS
  --
  procedure set_defaults(sensitivity in number);
  --  Set various defaults for this package.
  --  Input parameters:
  --    sensitivity
  --      In case a polling loop is required (see "Implementation Details"
  --      above), this is the time to sleep between polls.  Deafult is 5 sec.
  --
  procedure register(name in varchar2);
  --  Register interest in an alert.  A session may register interest in
  --    an unlimited number of alerts.  Alerts should be de-registered when
  --    the session no longer has any interest (see 'remove').  This call
  --    always performs a 'commit'.
  --  Input parameters:
  --    name
  --      The name of the alert in which this session is interested.
  --      WARNING:  Alert names beginning with 'ORA$' are reserved for use for
  --      products provided by Oracle Corporation.  Name must be 30 bytes
  --      or less.  The name is case-insensitive.
  --
  procedure remove(name in varchar2);
  --  Remove alert from registration list.  Do this when the session is no
  --    longer interested in an alert.  Removing an alert is important
  --    since it will reduce the amount of work done by signalers of the alert.
  --    If a session dies without removing the alert, that alert will
  --    eventually (but not immediately) be cleaned up.  This call always
  --    performs a commit.
  --  Input parameters:
  --    name
  --      The name of the alert to be removed from registration list. The
  --      name is case-insensitive.
  --
  procedure removeall;
  --  Remove all alerts for this session from registration list.  Do this
  --    when the session is no longer interested in any alerts.  Removing
  --    alerts is important since it will reduce the amount of work done
  --    by signalers of the alert.  If a session dies without removing all
  --    of its alerts, the alerts will eventually (but not immediately)
  --    be cleaned up.  This call always performs a commit.
  --
  --    This procedure is called automatically upon first reference to this
  --    package during a session.  Therefore no alerts from prior sessions
  --    which may have terminated abnormally can affect this session.
  procedure waitany(name out varchar2,
                    message out varchar2,
                    status out integer,
                    timeout in number default maxwait);
  --  Wait for an alert to occur for any of the alerts for which this
  --    session is registered.  Although probably unusual, the same session
  --    that waits for the alert may also first signal the alert.  In this
  --    case remember to commit after the signal and prior to the wait.
  --    Otherwise a lock request exception (status 4) will occur.  This
  --    call always performs a commit.
  --  Input parameters:
  --    timeout
  --      The maximum time to wait for an alert.  If no alert occurs before
  --      timeout seconds, then this call will return with status of 1.
  --  Output parameters:
  --    name
  --      The name of the alert that occurred, in uppercase.
  --    message
  --      The message associated with the alert.  This is the message
  --      provided by the 'signal' call.  Note that if multiple signals
  --      on this alert occurred before the waitany call, then the message
  --      will correspond to the most recent signal call.  Messages from
  --      prior signal calls will be discarded.
  --    status
  --      0 - alert occurred
  --      1 - timeout occurred
  --  Errors raised:
  --    -20000, ORU-10024: there are no alerts registered.
  --       Cause: You must register an alert before waiting.
  --
  procedure waitone(name in varchar2,
                    message out varchar2,
                    status out integer,
                    timeout in number default maxwait);
  --  Wait for specified alert to occur. If the alert was signalled since
  --    the register or last waitone/waitany, then this call will return
  --    immediately.  The same session that waits for the alert may also
  --    first signal the alert.  In this case remember to commit after the
  --    signal and prior to the wait.  Otherwise a lock request exception
  --    (status 4) will occur.  This call always performs a commit.
  --  Input parameters:
  --    name
  --      The name of the alert to wait for. The name is case-insensitive.
  --    timeout
  --      The maximum time to wait for this alert.  If no alert occurs before
  --      timeout seconds, then this call will return with status of 1.
  --      If the named alert has not been registered then the this call
  --      will return after the timeout period expires.
  --  Output parameters:
  --    message
  --      The message associated with the alert.  This is the message
  --      provided by the 'signal' call.  Note that if multiple signals
  --      on this alert occurred before the waitone call, then the message
  --      will correspond to the most recent signal call.  Messages from
  --      prior signal calls will be discarded.  The message may be up to
  --      1800 bytes.
  --    status
  --      0 - alert occurred
  --      1 - timeout occurred
  --
  procedure signal(name in varchar2,
                   message in varchar2);
  --  Signal an alert.
  --  Input parameters:
  --    name
  --      Name of the alert to signal.  The effect of the signal call only
  --      occurs when the transaction in which it is made commits.  If the
  --      transaction rolls back, then the effect of the signal call is as
  --      if it had never occurred.  All sessions that have registered
  --      interest in this alert will be notified.  If the interested sessions
  --      are currently waiting, they will be awakened.  If the interested
  --      sessions are not currently waiting, then they will be notified the
  --      next time they do a wait call.  Multiple sessions may concurrently
  --      perform signals on the same alert.  However the first session
  --      will block concurrent sessions until the first session commits.
  --      Name must be 30 bytes or less. It is case-insensitive.  This call
  --      does not perform a commit.
  --    message
  --      Message to associate with this alert.  This will be passed to
  --      the waiting session.  The waiting session may be able to avoid
  --      reading the database after the alert occurs by using the
  --      information in this message.  The message must be 1800 bytes or less.

end;

PACKAGE BODY dbms_alert IS
  P_INT           NUMBER         := 5;
  THIS_SESSION_ID   VARCHAR2(30)   := DBMS_SESSION.UNIQUE_SESSION_ID;
  PARALLEL          BOOLEAN        := DBMS_UTILITY.IS_CLUSTER_DATABASE;
  SIGPIPE           VARCHAR2(30)   := 'ORA$ALERT$' || THIS_SESSION_ID;
  MSGSEQ            BINARY_INTEGER := 0;
  FIRSTREGISTER     BOOLEAN        := TRUE;
  INSTANTIATING_PKG BOOLEAN        := TRUE;


FUNCTION MINIMUM(V1 NUMBER, V2 NUMBER) RETURN NUMBER IS
BEGIN
  IF V1 < V2 THEN
    RETURN V1;
  ELSE
    RETURN V2;
  END IF;
END;


PROCEDURE SET_DEFAULTS(SENSITIVITY IN NUMBER) IS
BEGIN
  IF SENSITIVITY >= 0 THEN
    P_INT := SENSITIVITY;
  END IF;
END;


PROCEDURE REGISTER(NAME IN VARCHAR2) IS
  STATUS  INTEGER;
  LSTATUS INTEGER;
  LOCKID  INTEGER;
  CURSOR  C1 IS
    SELECT DISTINCT SUBSTR(KGLNAOBJ,11) SID FROM X$KGLOB
     WHERE KGLHDNSP = 7
     AND   KGLNAOBJ LIKE 'ORA$ALERT$%'
     AND   BITAND(KGLHDFLG,128)!=0
    UNION
    SELECT DISTINCT SID FROM DBMS_ALERT_INFO;
BEGIN
  IF INSTANTIATING_PKG THEN
    REMOVEALL;
    INSTANTIATING_PKG := FALSE;
  END IF;
  IF (FIRSTREGISTER) THEN
    IF DBMS_UTILITY.IS_CLUSTER_DATABASE THEN
      FOR REC IN C1 LOOP
        LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.SID, 2000002048, 2048);
        LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.X_MODE,
                     TIMEOUT => 0, RELEASE_ON_COMMIT => TRUE);
        IF LSTATUS = 0 THEN
          DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID);
          DELETE DBMS_ALERT_INFO WHERE SID = REC.SID;
          COMMIT;
        ELSIF LSTATUS NOT IN (1,2,4) THEN
          RAISE_APPLICATION_ERROR(-20000,
            'ORU-10025: lock request error, status: ' || TO_CHAR(LSTATUS));
        END IF;
      END LOOP;
      LSTATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(THIS_SESSION_ID,
        2000002048,
        2048),
        DBMS_LOCK.S_MODE, TIMEOUT => 60);
      IF LSTATUS != 0  AND LSTATUS != 4 THEN
        RAISE_APPLICATION_ERROR(-20000,
          'ORU-10021: lock request error, status: ' || TO_CHAR(LSTATUS));
      END IF;
    ELSE
      FOR REC IN C1 LOOP
        IF NOT DBMS_SESSION.IS_SESSION_ALIVE(REC.SID) THEN
          DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID);
          DELETE DBMS_ALERT_INFO WHERE SID = REC.SID;
          COMMIT;
        END IF;
      END LOOP;
    END IF;
    FIRSTREGISTER := FALSE;
  END IF;
  STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME),
    2000000000, 2048), DBMS_LOCK.X_MODE,
    DBMS_LOCK.MAXWAIT, RELEASE_ON_COMMIT => TRUE);
  IF STATUS != 0 THEN
    RAISE_APPLICATION_ERROR(-20000,
      'ORU-10002: lock request error, status: ' || TO_CHAR(STATUS));
  END IF;
  INSERT INTO DBMS_ALERT_INFO VALUES (UPPER(REGISTER.NAME), THIS_SESSION_ID,
    'N', NULL);
  COMMIT;
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN COMMIT;
END;


PROCEDURE REMOVE(NAME IN VARCHAR2) IS
BEGIN
  IF INSTANTIATING_PKG THEN
    REMOVEALL;
    INSTANTIATING_PKG := FALSE;
  END IF;
  DELETE FROM DBMS_ALERT_INFO
   WHERE NAME  = UPPER(REMOVE.NAME)
     AND SID   = THIS_SESSION_ID;
  COMMIT;
END;


PROCEDURE PIPE_WAIT(MAXTIME NUMBER, CUMTIME IN OUT NUMBER) IS
  STATUS INTEGER;
  TMO    NUMBER := MAXTIME;
BEGIN
  IF PARALLEL THEN
    TMO := MINIMUM(TMO, P_INT);
  END IF;
  IF TMO = MAXWAIT THEN
    TMO := DBMS_PIPE.MAXWAIT;
  END IF;
  STATUS := DBMS_PIPE.RECEIVE_MESSAGE(SIGPIPE, TMO);
  IF STATUS = 1 THEN
    CUMTIME := CUMTIME + TMO;
    RETURN;
  END IF;
  IF STATUS <> 0 THEN
    RAISE_APPLICATION_ERROR(-20000, 'ORU-10015: error:' || TO_CHAR(STATUS)
      || ' waiting for pipe message.');
  END IF;
  RETURN;
END;


PROCEDURE OPTIMISTIC(
  NAME    OUT VARCHAR2,
  MESSAGE OUT VARCHAR2,
  STATUS  OUT INTEGER)
IS
  LOCKID  INTEGER;
  LSTATUS INTEGER;
  CURSOR  C1 IS
    SELECT NAME FROM DBMS_ALERT_INFO
     WHERE SID = THIS_SESSION_ID
     AND   CHANGED = 'Y';
BEGIN
  STATUS := 1;
  FOR REC IN C1 LOOP
    LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.NAME, 2000000000, 2048);
    LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, TIMEOUT => 0,
      RELEASE_ON_COMMIT => TRUE);
    IF LSTATUS <> 1 THEN
      IF LSTATUS <> 0 THEN
        RAISE_APPLICATION_ERROR(-20000, 'ORU-10019: error ' ||
          TO_CHAR(LSTATUS) || ' on lock request.');
      END IF;
      UPDATE DBMS_ALERT_INFO SET CHANGED = 'N'
       WHERE SID = THIS_SESSION_ID
       AND   NAME = REC.NAME;
      SELECT MESSAGE INTO MESSAGE FROM DBMS_ALERT_INFO
       WHERE SID = THIS_SESSION_ID
       AND   NAME = REC.NAME;
      COMMIT;
      DBMS_PIPE.PURGE(SIGPIPE);
      NAME := REC.NAME;
      STATUS := 0;
      RETURN;
    END IF;
  END LOOP;
  RETURN;
END;


PROCEDURE WAITANY(
  NAME    OUT VARCHAR2,
  MESSAGE OUT VARCHAR2,
  STATUS  OUT INTEGER,
  TIMEOUT IN  NUMBER    DEFAULT MAXWAIT)
IS
  WAITIME  NUMBER        := 0;
  CUMTIME  NUMBER        := 0;
  LOCKID   INTEGER;
  ST       INTEGER;
  LSTATUS  INTEGER;
  TIMEDOUT BOOLEAN;
  CHANGED  VARCHAR2(1);
  FOUNDONE BOOLEAN;
  CURSOR   C1 IS
    SELECT NAME FROM DBMS_ALERT_INFO
     WHERE SID = THIS_SESSION_ID;
BEGIN
  IF INSTANTIATING_PKG THEN
    REMOVEALL;
    INSTANTIATING_PKG := FALSE;
  END IF;
  OPTIMISTIC(NAME, MESSAGE, ST);
  IF ST = 0 THEN
    STATUS := ST;
    RETURN;
  END IF;
  WAITIME := 1;
  CUMTIME := 0;
  LOOP
    TIMEDOUT := FALSE;
    FOUNDONE := FALSE;
    FOR REC IN C1 LOOP
      FOUNDONE := TRUE;
      LOCKID := DBMS_UTILITY.GET_HASH_VALUE(REC.NAME, 2000000000, 2048);
      LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, WAITIME,
        RELEASE_ON_COMMIT => TRUE);
      IF LSTATUS = 1 THEN
        OPTIMISTIC(NAME, MESSAGE, ST);
        IF ST = 0 THEN
          STATUS := 0;
          RETURN;
        END IF;
        CUMTIME := CUMTIME + WAITIME;
        IF CUMTIME >= TIMEOUT THEN
          STATUS := 1;
          RETURN;
        END IF;
        TIMEDOUT := TRUE;
        GOTO CONTINUE;
      ELSIF LSTATUS <> 0 THEN
        RAISE_APPLICATION_ERROR(-20000,
          'ORU-10020: error ' || TO_CHAR(LSTATUS) || ' on lock request.');
      ELSE
        SELECT CHANGED, MESSAGE INTO CHANGED, MESSAGE FROM DBMS_ALERT_INFO
         WHERE SID = THIS_SESSION_ID
         AND   NAME = REC.NAME;
        IF CHANGED = 'Y' THEN
          UPDATE DBMS_ALERT_INFO SET CHANGED = 'N'
           WHERE SID = THIS_SESSION_ID
           AND   NAME = REC.NAME;
          COMMIT;
          NAME := REC.NAME;
          STATUS := 0;
          DBMS_PIPE.PURGE(SIGPIPE);
          RETURN;
        END IF;
        LSTATUS := DBMS_LOCK.RELEASE(LOCKID);
      END IF;
      <<continue>>
      NULL;
    END LOOP;
    IF NOT FOUNDONE THEN
      RAISE_APPLICATION_ERROR(-20000,
        'ORU-10024: there are no alerts registered.');
    END IF;
    IF TIMEDOUT THEN
      WAITIME := MINIMUM(WAITIME*2, 32);
      WAITIME := MINIMUM(WAITIME, TIMEOUT-CUMTIME);
    ELSE
      PIPE_WAIT(TIMEOUT-CUMTIME, CUMTIME);
    END IF;
    IF CUMTIME >= TIMEOUT THEN
      STATUS := 1;
      RETURN;
    END IF;
  END LOOP;
END;


PROCEDURE WAITONE(
  NAME    IN  VARCHAR2,
  MESSAGE OUT VARCHAR2,
  STATUS  OUT INTEGER,
  TIMEOUT IN  NUMBER    DEFAULT MAXWAIT)
IS
  CUMTIME NUMBER  := 0;
  LOCKID  INTEGER := DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME),
    2000000000, 2048);
  LSTATUS INTEGER;
BEGIN
  IF INSTANTIATING_PKG THEN
    REMOVEALL;
    INSTANTIATING_PKG := FALSE;
  END IF;
  LOOP
    LSTATUS := DBMS_LOCK.REQUEST(LOCKID, DBMS_LOCK.SX_MODE, TIMEOUT-CUMTIME,
      RELEASE_ON_COMMIT => TRUE);
    IF LSTATUS = 1 THEN
      STATUS := 1;
      RETURN;
    END IF;
    IF LSTATUS = 4 THEN
      RAISE_APPLICATION_ERROR(-20000,
        'ORU-10037: attempting to wait on uncommitted signal from same session');
    END IF;
    IF LSTATUS <> 0 THEN
      RAISE_APPLICATION_ERROR(-20000,
        'ORU-10023: error ' || TO_CHAR(LSTATUS) || ' on lock request.');
    END IF;
    UPDATE DBMS_ALERT_INFO SET CHANGED = 'N'
     WHERE NAME    = UPPER(WAITONE.NAME)
       AND SID     = THIS_SESSION_ID
       AND CHANGED = 'Y';
    IF SQL%ROWCOUNT != 0 THEN
      SELECT MESSAGE INTO MESSAGE FROM DBMS_ALERT_INFO
       WHERE NAME    = UPPER(WAITONE.NAME)
         AND SID     = THIS_SESSION_ID;
      COMMIT;
      DBMS_PIPE.PURGE(SIGPIPE);
      STATUS := 0;
      RETURN;
    END IF;
    LSTATUS := DBMS_LOCK.RELEASE(LOCKID);
    PIPE_WAIT(TIMEOUT, CUMTIME);
    IF CUMTIME >= TIMEOUT THEN
      STATUS := 1;
      RETURN;
    END IF;
  END LOOP;
END;


PROCEDURE SIGNAL_PIPE(PIPENAME VARCHAR2) IS
  MSGID    VARCHAR2(40);
  TMPMSGID VARCHAR2(40);
  STATUS   INTEGER;
BEGIN
  MSGID := THIS_SESSION_ID || ':' || TO_CHAR(MSGSEQ);
  MSGSEQ := MSGSEQ + 1;
  DBMS_PIPE.PACK_MESSAGE(MSGID);
  STATUS := DBMS_PIPE.SEND_MESSAGE(PIPENAME);
  IF STATUS <> 0 THEN
    RAISE_APPLICATION_ERROR(-20000,
      'ORU-10016: error:' || TO_CHAR(STATUS) || ' sending on pipe ' ||
      PIPENAME);
  END IF;
  STATUS := DBMS_PIPE.RECEIVE_MESSAGE(PIPENAME, 0);
  IF STATUS = 1 THEN
    RETURN;
  END IF;
  IF STATUS <> 0 THEN
    RAISE_APPLICATION_ERROR(-20000,
      'ORU-10017: error:' || TO_CHAR(STATUS) || ' receiving on pipe ' ||
      PIPENAME);
  END IF;
  DBMS_PIPE.UNPACK_MESSAGE(TMPMSGID);
  IF TMPMSGID = MSGID THEN
    DBMS_PIPE.PACK_MESSAGE(MSGID);
    STATUS := DBMS_PIPE.SEND_MESSAGE(PIPENAME);
    IF STATUS <> 0 THEN
      RAISE_APPLICATION_ERROR(-20000,
        'ORU-10018: error:' || TO_CHAR(STATUS) || ' sending on pipe ' ||
        PIPENAME);
    END IF;
  END IF;
END;


PROCEDURE SIGNAL(NAME IN VARCHAR2, MESSAGE IN VARCHAR2) IS
  STATUS  INTEGER;
  CURSOR  C2(ALERTNAME VARCHAR2) IS
    SELECT SID FROM DBMS_ALERT_INFO
     WHERE NAME = UPPER(ALERTNAME);
BEGIN
  STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(UPPER(NAME),
    2000000000, 2048), DBMS_LOCK.S_MODE,
    DBMS_LOCK.MAXWAIT, RELEASE_ON_COMMIT => TRUE);
  IF STATUS != 0  AND STATUS != 4 THEN
    RAISE_APPLICATION_ERROR(-20000,
      'ORU-10001: lock request error, status: ' || TO_CHAR(STATUS));
  END IF;
  UPDATE DBMS_ALERT_INFO SET CHANGED = 'Y', MESSAGE = SIGNAL.MESSAGE
   WHERE NAME = UPPER(SIGNAL.NAME);
  IF DBMS_UTILITY.IS_CLUSTER_DATABASE THEN
    FOR REC IN C2(NAME) LOOP
      STATUS := DBMS_LOCK.REQUEST(DBMS_UTILITY.GET_HASH_VALUE(REC.SID,
        2000002048,
        2048),
        DBMS_LOCK.SX_MODE, TIMEOUT => 0,
        RELEASE_ON_COMMIT => TRUE);
      IF STATUS = 0 THEN
        DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID);
        STATUS := DBMS_LOCK.RELEASE(DBMS_UTILITY.GET_HASH_VALUE(REC.SID,
          2000002048,
          2048));
      ELSE
        IF STATUS != 1 AND STATUS != 4 THEN
          RAISE_APPLICATION_ERROR(-20000,
            'ORU-10022: lock request error, status: ' || TO_CHAR(STATUS));
        END IF;
        SIGNAL_PIPE('ORA$ALERT$' || REC.SID);
      END IF;
    END LOOP;
  ELSE
    FOR REC IN C2(NAME) LOOP
      IF  NOT DBMS_SESSION.IS_SESSION_ALIVE(REC.SID) THEN
        DBMS_PIPE.PURGE('ORA$ALERT$' || REC.SID);
      ELSE
        SIGNAL_PIPE('ORA$ALERT$' || REC.SID);
      END IF;
    END LOOP;
  END IF;
END;


PROCEDURE REMOVEALL IS
BEGIN
  DELETE FROM DBMS_ALERT_INFO WHERE SID = THIS_SESSION_ID;
  DBMS_PIPE.PURGE(SIGPIPE);
  COMMIT;
END;


END;


12. 동시성 소스에서 볼 수 있듯이 DBMSALERT는 데이터베이스 테이블(DBMS ALERT INFO) 및 DBMS 기반LOCK, DBMS_PIPE가 구현한 테이블 DBMSALERT_INFO 작업 전에 자물쇠를 추가했고 경고의 이름에 따라 자물쇠를 신청했다. 업무가 제출된 후에야 풀렸기 때문에 경고에 대한 작업은 직렬이다. 예를 들어 알림 데이터가 수정된 트리거가 하나 있는데 만약에 여러 세션이 동시에 데이터를 수정하면 동시에 트리거하고 경보를 보낸다.이 중 한 세션만 잠금 신청을 할 수 있고 다른 세션은 막혔습니다.자물쇠를 신청한 세션은 경보를 보내고 자물쇠를 풀면 다른 세션 중 한 세션은 자물쇠를 신청하는데 성공하고 나머지 세션은 막혀서...이런 식으로 추론하면 DBMS 를 사용할 수 있다JOB 이 문제 해결, DBMSJOB는 흔히 볼 수 있는 조작을 병행화하는 작은 기교이다. 그러나 직렬화 문제를 해결하고 대량으로 병행하는 상황에서 DBMS를 빈번하게 조작한다ALERT_INFO 테이블은 성능상의 문제를 가져올 수 있습니다. 외부 링크: dbms_alert DBMS_ALERT: Broadcasting Alerts to Users Using DBMS_ALERT To Notify Sessions Of Database Events PACKAGE DBMS_ALERT Specification Telling a Forms Application that a change has been made on the database.은 프로그램이 데이터를 어떻게 바꾸는지 알려주는 방법을 소개했습니다. 이것은 Enterprise DB를 말합니다. 어떤 데이터베이스인지 모르겠습니다. Postgres 같은 종류인 것 같습니다. Oracle과 DBMS_PIPE & DBMS_ALERT In EnterpriseDB을 닮았습니다.

좋은 웹페이지 즐겨찾기