0

We are developing a C++ server which will process based on time based events. For example if a particular user has configured a particular task that has to be processed at a particular time, then an event or notification needs to be sent to the C++ server from the database, to initiate the task. The timming configuration is stored in the database, and the C++ server shouldnt poll the database at an interval, but event should be notified from the database at the configured timming.

We are using odatabase and odynaset libraries to connect and access the oracle database 11g.

Kindly request you to provide a solution for the above problem.

user1
  • 687
  • 7
  • 13
  • 24

2 Answers2

2

One approach is to use Oracle's Advanced Queuing. For that purpose you need to set up a queue (and a queue table) and write a PL/SQL procedure that waits for the next message in the queue.

The C++ side then calls the PL/SQL procedure, which returns when the next event has occurred.

On the Oracle side, you will need to use DBMS_SCHEDULER or a similar facility to create the event, i.e. to insert a new message into the queue at the appropriate time.

It's still a polling approach. However, there's absolutely no activity between two events.

Update:

Here's some sample code.

Initial setup of the queue (the message contains a numeric and a text value):

grant AQ_ADMINISTRATOR_ROLE to appuser;
grant EXECUTE ON DBMS_AQ to appuser;
grant EXECUTE ON DBMS_AQ to appuser;


CREATE TYPE sample_payload_type AS OBJECT
(
  cmd  VARCHAR2(20),
  id   NUMBER
);


BEGIN
  DBMS_AQADM.CREATE_QUEUE_TABLE (
    queue_table        => 'sample_queue_table',
    queue_payload_type => 'sample_payload_type',
    sort_list          => 'ENQ_TIME',
    compatible         => '10.0'
  );
END;
/

BEGIN
  DBMS_AQADM.CREATE_QUEUE (
    queue_name         => 'sample_queue',
    queue_table        => 'sample_queue_table'
  );

  DBMS_AQADM.START_QUEUE (
    queue_name         => 'sample_queue'
  );
END;
/

Package header:

create or replace package sample_queue_pkg
as

  procedure get_next_msg(
    i_max_wait      number
   ,o_cmd      out  varchar2
   ,o_id       out  number
  );


  procedure put_msg(
    i_cmd           varchar2
   ,i_id            number
  );

end sample_queue_pkg;
/

Package body:

create or replace package body sample_queue_pkg
as

  procedure get_next_msg(
    i_max_wait      number
   ,o_cmd      out  varchar2
   ,o_id       out  number
  )
  is
    dequeue_options dbms_aq.dequeue_options_t;
    message_properties dbms_aq.message_properties_t;
    message_handle RAW(16);
    message sample_payload_type;

    NO_MESSAGE_RECEIVED EXCEPTION;
    PRAGMA EXCEPTION_INIT(NO_MESSAGE_RECEIVED, -25228);

  begin
    dequeue_options.wait := i_max_wait;
    DBMS_AQ.DEQUEUE (
      queue_name => 'appuser.sample_queue',
      dequeue_options => dequeue_options,
      message_properties => message_properties,
      payload => message,
      msgid => message_handle
    );

    o_cmd := message.cmd;
    o_id := message.id;

  exception
    when NO_MESSAGE_RECEIVED then
      o_cmd := null;
      o_id := null;

  end get_next_msg;


  procedure put_msg(
    i_cmd           varchar2
   ,i_id            number
  )
  is
    enqueue_options dbms_aq.enqueue_options_t;
    message_properties dbms_aq.message_properties_t;
    message_handle RAW(16);
    message sample_payload_type;
    message_id NUMBER;

  begin
    message := sample_payload_type(i_cmd, i_id);
    DBMS_AQ.ENQUEUE(
      queue_name => 'appuser.sample_queue',
      enqueue_options => enqueue_options,
      message_properties => message_properties,
    payload => message,
      msgid => message_handle
    );
  end put_msg;

end sample_queue_pkg;
/

The database server can send a message using the following code:

sample_queue_pkg.put_msg('run_task', 8234);
commit;

The C++ server can wait for message (and receive them) calling the stored sample_queue_pkg.get_next_msg. The parameter i_max_wait specifies the maximum time to wait for the next message in seconds. You probably want to implement a loop that waits for the next message and processes it until it receives a signal that the server is about to quit.

Codo
  • 75,595
  • 17
  • 168
  • 206
  • Is CREATE_QUEUE_TABLE and CREATE QUEUE to be executed like a normal sql query? like this "EXECUTE DBMS_AQADM.CREATE_QUEUE_TABLE (CREATE_QUEUE_TABLE ( queue_table => 'sample_queue_table', queue_payload_type => 'sample_payload_type', sort_list => 'ENQ_TIME', compatible => '10.0' );" if not how to do it?Plz hlp me out. – user1 Dec 14 '12 at 05:38
  • Yes, instead of using BEGIN/END you can also use EXECUTE (or EXEC), which is a short cut for wrapping the rest of the line in a BEGIN/END pair. And I recommend reading the [Advanced Queuing User's Guide](http://docs.oracle.com/cd/E14072_01/server.112/e11013.pdf) for learning more about the concepts such as queues and queue tables. – Codo Dec 14 '12 at 06:58
  • I executed the quetable like this "EXECUTE Proc_GetTypeCS.CREATE_QUEUE_TABLE(queue_table => 'queue_message_table',queue_payload_type => 'queue_message_type'); but its throwing error "'PROC_GETTYPECS' reference is out of scope"but that procedure is created successfully. – user1 Dec 14 '12 at 07:33
  • You cannot replace DBMS_AQADM with Proc_GetTypeCS. DBMS_AQADM is the Oracle package for advanced queuing. It contains the relevant procedures to create and manage queues. You really need to study the Oracle documentation. – Codo Dec 14 '12 at 07:41
  • Hi codo in the above example u posted enqueuing is not happening automatically right, i need it to happen automatically and how can i do in this case, plz help me out. – user1 Dec 17 '12 at 11:16
  • Do you mean that the message not enqueued at a scheduled time? I thought you already had that part. If not, then have a look at the Oracle package DBMS_SCHEDULER. It can help to fill the missing piece. – Codo Dec 18 '12 at 07:07
  • Hi @Codo, Im not clear about this, here in DBMS_SCHEDULER we have CREATE_PROGRAM CREATE_JOB CREATE_SCHEDULE etc., after reading the oracle doc still im unclear what to use, On the Oracle side, i am going to use DBMS_SCHEDULER to insert a new message into the queue at the appropriate time, i planned to create scheduler to execute it on particular time and then create program to execute my PL/SQL block which will enqueue the message in the queue, please guide me whether i am doing correctly, if not please correct me. Thankyou – user1 Dec 20 '12 at 05:52
  • Or instead of using CREATE_SCHEDULE and CREATE_PROGRAM, CREATE_JOB does both the jobs, which to use? – user1 Dec 21 '12 at 04:55
  • If you're stuck with DMBS_SCHEDULER, you better open a new specific question on StackOverflow. Comments aren't well suited for adding code. – Codo Dec 21 '12 at 08:50
1

One way would be to use dbms_pipe or dbms_alert. ie you connect from your C++ server session to the db (PRO C) and call dbms_pipe/alert which will block until the db, in another session sends data down the pipe that the c++ server will read and then process. for example, if its a simple "GO RUN NOW!" you want to send, then dbms_alert will do.

for example:

SQL> declare
  2    v_name  varchar2(200);
  3    v_msg   varchar2(200);
  4    v_sts   number; -- 0 = alert occured, 1 = timeout
  5  begin
  6    dbms_alert.register('RUN_PROGRAM_A');
  7    dbms_alert.register('RUN_PROGRAM_B');
  8
  9    loop
 10      dbms_alert.waitany(v_name,
 11                         v_msg,
 12                         v_sts,
 13                         dbms_alert.maxwait);
 14
 15      if (v_sts = 0)
 16      then
 17        dbms_output.put_line('i got alert: ' ||v_name);
 18        dbms_output.put_line(' with assoc message: ' ||v_msg);
 19      end if;
 20      if (v_name  = 'RUN_PROGRAM_B')
 21      then
 22        exit;
 23      end if;
 24    end loop;
 25  end;
 26  /
i got alert: RUN_PROGRAM_A
with assoc message: whatever you want to transmit.
i got alert: RUN_PROGRAM_A
with assoc message: whatever you want to transmit.
i got alert: RUN_PROGRAM_B
with assoc message: whatever you want to transmit.

PL/SQL procedure successfully completed.

where the db controlling session issued this:

SQL> exec dbms_alert.signal('RUN_PROGRAM_A', 'whatever you want to transmit.');

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> exec dbms_alert.signal('RUN_PROGRAM_A', 'whatever you want to transmit.');

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> exec dbms_alert.signal('RUN_PROGRAM_B', 'whatever you want to transmit.');

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL>

another approach is a scheduler approach (dbms_queue), which is a message queue approach, in which you'd be polling (dequeueing) against a queue table and would do something when a message arrives.

DazzaL
  • 21,638
  • 3
  • 49
  • 57
  • I am done with enqueue and dequeue, manual dequeuing is happening successfully, can someone please help me how to dequeue automatically with notification if some data get enqueued in queue. – user1 Jan 03 '13 at 10:24