Environment:
Oracle 12.2 64-bit under Linux
parameters:
job_queue_processes = 4000
aq_tm_processes = 1
Can you please explain me –
why there is a delay between event processing in event-based job?
I fixed a 15-20 seconds delay between job calls.
Test process is simpliest – it places a message into the queue.
And job begins to run only after delay mentioned above.
I expected a milliseconds for such a simpliest scenario…
Test case:
create or replace type t_test_type
as object(id number, msg varchar2(50));
/
create or replace procedure test_q_proc
(
p_job_name in varchar2,
p_event_message in t_test_type
)
as
begin
dbms_output.put_line('msg= '||p_event_message.msg);
end;
/
begin
dbms_aqadm.create_queue_table('test_q_table','t_test_type',multiple_consumers => true);
dbms_aqadm.create_queue('test_q','test_q_table');
dbms_aqadm.start_queue('test_q');
dbms_scheduler.create_program('test_q_prog','stored_procedure','test_q_proc',2, false);
dbms_scheduler.define_metadata_argument('test_q_prog','job_name',1,'p_job_name');
dbms_scheduler.define_metadata_argument('test_q_prog','event_message',2,'p_event_message');
dbms_scheduler.enable('test_q_prog');
dbms_scheduler.create_job('test_q_job','test_q_prog',
event_condition=> 'tab.user_data is not null',
queue_spec=>'test_q',enabled=> true);
end;
/
declare
opts dbms_aq.enqueue_options_t;
props dbms_aq.message_properties_t;
pl t_test_type := t_test_type(1, 'test');
msg_id raw(16);
begin
dbms_aq.enqueue('test_q',opts, props, pl, msg_id);
commit;
end;
/
Any help would be very appreciated.
TIA, Andrew.
Related
I want to write a procedure that logs output to the Oracle concurrent manager log when run from a concurrent program, but writes to dbms_output when run "standalone".
Is there a way from PL/SQL to check whether my code is being run from a concurrent request? The best way I've been able to find is
select * from fnd_concurrent_requests
where oracle_session_id = userenv('SESSIONID');
but that's pretty slow. Is there a function or table I can query that gives me the information more efficiently?
You can best use fnd_global.conc_request_id like we do in our blitz report code:
procedure write_log(p_text in varchar2, p_log_level in number default 1) is
begin
if fnd_global.conc_request_id>0 then
fnd_file.put_line(fnd_file.log,p_text);
else
fnd_log.string(p_log_level,'XXEN',p_text); --or your dbms_output.put_line() call
end if;
end write_log;
Add a boolean flag argument to the procedure that you can use to tell it where you want to log to when you call the procedure and then pass different flags from your two different (concurrent/non-concurrent) programs:
CREATE PROCEDURE my_proc(
i_value1 IN NUMBER,
i_use_concurrent_logging IN BOOLEAN DEFAULT FALSE
)
IS
-- Helper function so you only check the flag in one place.
PROCEDURE log(value IN VARCHAR2)
IS
BEGIN
IF i_use_concurrent_logging THEN
-- put your concurrent logging code here.
NULL;
ELSE
DBMS_OUTPUT.PUT_LINE(value);
END IF;
END;
BEGIN
-- Do stuff.
log('Stuff done');
-- Do other stuff
log('Other Stuff done');
END;
/
If you want to use your check once in the procedure then you could use:
CREATE OR REPLACE PROCEDURE my_proc(
i_value1 IN NUMBER
)
IS
v_use_concurrent_logging BOOLEAN := FALSE;
PROCEDURE log(value IN VARCHAR2)
IS
BEGIN
IF v_use_concurrent_logging THEN
-- put your concurrent logging code here.
NULL;
ELSE
DBMS_OUTPUT.PUT_LINE(value);
END IF;
END;
BEGIN
DECLARE
v_exists INT;
BEGIN
SELECT 1
INTO v_exists
FROM fnd_concurrent_requests
WHERE oracle_session_id = userenv('SESSIONID')
AND ROWNUM = 1;
v_use_concurrent_logging := TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_use_concurrent_logging := FALSE;
END;
-- Do stuff.
log('Stuff done');
-- Do other stuff
log('Other Stuff done');
END;
/
db<>fiddle here
I am currently migrating from oracle to DB2. I have actively used sys_context in most of the applications to get the userID of logged in user through sessions(IIS and .net framework).
I am looking to convert the following set of scripts from oracle to DB2. So far not able to find any equivalent for oracle context in DB2.
SQL> create context my_ctx
2 using pkg_ctx;
Context created.
SQL> create package pkg_ctx
2 as
3 procedure set_context;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_ctx
2 as
3 procedure set_context
4 as
5 begin
6 dbms_session.set_context( 'MY_CTX', 'USERNAME', 'test' );
7 end;
8 end;
9 /
Package body created.
SQL> exec pkg_ctx.set_context;
PL/SQL procedure successfully completed.
SQL> select sys_context( 'MY_CTX', 'USERNAME' )
2 from dual;
SYS_CONTEXT('MY_CTX','USERNAME')
-------------------------------------------------------------------------------
test
Assuming you work with Db2 for LUW, you can use one of the CURRENT CLIENT_* registry variables to pass session context information from the client application to the server. For example, during the session initialization on the client side you would issue SET CURRENT CLIENT_USERID='stacky', then on the server that variable can be read by a routine or a trigger.
It worked like this, haven't tried it at application level. But from the database tried to register 2 users from 2 different instances of the same database on DB2.
call sysproc.wlm_set_client_info('stacky',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns stacky
from another instance of the same database
call sysproc.wlm_set_client_info('test',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns test.
Will try this from the application level through creating sessions of the users, will monitor how exactly this works with the special registers.
Not sure if this is the exact way to do this, will explore more and keep updated.
For replicating a similar situation in our environment, while doing the migration from oracle to DB2 LUW.
The below procedure sets up the all the context values in user_id (limited to varchar 255) field which we need as as 'key1:value1|key2:value2|'
Procedure to setup the context values
CREATE OR REPLACE PROCEDURE setup_context(pValues IN VARCHAR)
IS
lValues VARCHAR2(255) := pValues;
BEGIN
--save the as is
wlm_set_client_info(lValues, NULL, NULL, NULL, NULL);
END;
Procedure to set the value of the desired key
CREATE OR REPLACE FUNCTION sys_context(prefix IN VARCHAR) RETURN VARCHAR IS
lValues VARCHAR2(255);
lParam VARCHAR2(255);
lKey VARCHAR2(255);
lValue VARCHAR2(255);
lIndex PLS_INTEGER;
BEGIN
--get the data from the current session
SELECT CURRENT CLIENT_USERID INTO lValues FROM dual;
LOOP
EXIT WHEN lValues IS NULL OR LENGTH(lValues) = 0;
lIndex := instr(lValues, '|');
IF lIndex > 0 THEN
lParam := substr(lValues, 1, lIndex-1);
lValues := substr(lValues, lIndex+1);
ELSE
lParam := lValues;
lValues := NULL;
END IF;
lIndex := instr(lParam, ':');
lKey := substr(lParam, 1, lIndex-1);
lValue := substr(lParam, lIndex+1);
--get the matching value
IF(lKey = prefix ) Then
RETURN lValue;
END IF;
END LOOP;
RETURN '';
END;
reference : https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/sqlref/src/tpc/db2z_sp_wlmsetclientinfo.html
I'm checking Go ability to migrate an existing C++ application.
One of the main task is to listen actively (no polling) an Advanced Oracle Queue.
In Java and C++ there are existing libraries supporting it since a long time.
I could not find anything similar in Go (libraries & examples).
Could you help me with that?
I have an implementation whereby I'm using the "gopkg.in/goracle.v2" package to connect to Oracle, along with the generic Go library "database/sql".
The way I did it, I have the code to read from the AQ in a PL/SQL procedure which I'm calling from my Go code. Although this is not the best approach - and I'm actually going to change it so that it doesn't rely on a stored oracle procedure - it works. The code looks something like the following:
Oracle PL/SQL proc:
PROCEDURE GetAQMessage ( out_content OUT VARCHAR2, in_acknowledge IN VARCHAR2 DEFAULT 'N' )
IS
dyn_sql VARCHAR2(32000);
l_content VARCHAR2(4000);
BEGIN
dyn_sql := '
DECLARE
l_payload MESSAGE_TYPE := MESSAGE_TYPE (NULL);
l_msg_id RAW(16);
l_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
l_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
BEGIN
DBMS_AQ.DEQUEUE(
queue_name => '''|| v_queue_name ||''',
dequeue_options => l_dequeue_options,
message_properties => l_message_properties,
payload => l_payload,
msgid => l_msg_id
);
:b_output := l_payload.message;
END;';
EXECUTE IMMEDIATE dyn_sql USING OUT l_content;
-- Return the content to the OUT parameter
out_content := l_content;
-- Permanently removes the message from the Queue
IF in_acknowledge = 'Y' THEN
COMMIT;
END IF;
END GetAQMessage;
Go code:
func GetAQMessage(transaction *sql.Tx) (string, error) {
var outResult string
var resErr error
var debug int
configuration = conf.Read()
//Run the command
_, resErr = transaction.Exec(`BEGIN CONSENT.GETAQMESSAGE(:1,:2) ; END;`, sql.Out{Dest: &outResult}, "N")
return outResult, resErr
}
i want to add a variable to a stored procedure but i want to be able to run the stored procedure in one of 2 different ways:
- if it is null, then it runs everything otherwise just matching records
- if it is null, then make it default to a set value otherwise just matching records
var countyvar varchar2(50);
begin
if countyvar is null then 'yyyy';
end if;
end;
OR
var countyvar varchar2(50);
begin
if countyvar is null then run script for all locations;
if countyvar is 'yyyy' then run script for only that location;
end if;
end;
used here:
select locations, count(accountID) from dim_locations where location_name = :COUNTYVAR group by locations;
I don't know if this helps you, but you can use the "DEFAULT" keyword in the procedure parameter declaration. For example:
PROCEDURE Get_emp_names (Dept_num IN NUMBER DEFAULT 20)
IS ...
And then you can control the flow of your procedure from inside. Just check if "countyvar" is null or not inside the procedure.
Check this also: Default Values to Stored Procedure in Oracle
Just do this ... watch for index performance however, it may or may not perform a little slower than expected depending on your data size and such ... test it out and tune a little as needed.
select locations, count(accountID)
from dim_locations
where ( :COUNTYVAR IS NULL
OR location_name = :COUNTYVAR )
group by locations;
[[Explanation of DEFAULT option mentioned in other answer ]]
DEFAULT option only kicks in when the parameter is not provided, not when it's NULL ... here's a test sample showing the behaviour - try it out ;)
set serverout on
declare
lv_var varchar2(10);
procedure p_test ( in_parm IN varchar2 default 10 )
is
begin
dbms_output.put_line ( 'in_parm is: ' || nvl(in_parm,'<<NULL>>') );
end;
begin
p_test ( 123 );
p_test ();
p_test ( NULL );
lv_var := 234;
p_test ( lv_var );
lv_var := NULL;
p_test ( lv_var );
end;
/
in_parm is: 123
in_parm is: 10
in_parm is: <<NULL>>
in_parm is: 234
in_parm is: <<NULL>>
Is there a possibility to connect to Oracle (via OCI) from one process, then connect on the same database session from another process?
In my current app, there are two ways to access the database: a synchronous one and an asynchronous one (by using a separate process, communicating via sockets).
The problem is the two methods implement distinct sessions.
If I attempt e.g. an update on one session, then try to update the same table from the other session without committing, I get a hang on the OCI call.
Worse, if a session variable is set from one session - the other session does not see it (which is exactly what the name says...).
If you are using an 11g database, you could use the DBMS_XA package to allow one session to to join a transaction started by the first session. As Tim Hall deomonstrates, you can start a transaction in one session, join that transaction from another session, and read the uncommitted changes made in the transaction. Unfortunately, however, that is not going to help with session variables (assuming that "session variable" means package variable that have session scope).
Create the package and the table:
CREATE TABLE foo( col1 NUMBER );
create or replace package pkg_foo
as
g_var number;
procedure set_var( p_in number );
end;
create or replace package body pkg_foo
as
procedure set_var( p_in number )
as
begin
g_var := p_in;
end;
end;
In Session 1, we start a global transaction, set the package variable, and insert a row into the table before suspending the global transaction (which allows another session to resume it)
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_xid dbms_xa_xid := dbms_xa_xid( 1 );
3 l_ret integer;
4 begin
5 l_ret := dbms_xa.xa_start( l_xid, dbms_xa.tmnoflags );
6 pkg_foo.set_var(42);
7 dbms_output.put_line( 'Set pkg_foo.g_var to ' || pkg_foo.g_var );
8 insert into foo values( 42 );
9 l_ret := dbms_xa.xa_end( l_xid, dbms_xa.tmsuspend );
10* end;
SQL> /
Set pkg_foo.g_var to 42
PL/SQL procedure successfully completed.
In session 2, we resume the global transaction, read from the table, read the session variable, and end the global transaction. Note that the query against the table sees the row we inserted but the package variable change is not visible.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_xid dbms_xa_xid := dbms_xa_xid( 1 );
3 l_ret integer;
4 l_col1 integer;
5 begin
6 l_ret := dbms_xa.xa_start( l_xid, dbms_xa.tmresume );
7 dbms_output.put_line( 'Read pkg_foo.g_var as ' || pkg_foo.g_var );
8 select col1 into l_col1 from foo;
9 dbms_output.put_line( 'Read COL1 from FOO as ' || l_col1 );
10 l_ret := dbms_xa.xa_end( l_xid, dbms_xa.tmsuccess );
11* end;
SQL> /
Read pkg_foo.g_var as
Read COL1 from FOO as 42
PL/SQL procedure successfully completed.
To share session state between the sessions, would it be possible to use a global application context rather than using package variables? You could combine that with the DBMS_XA packages if you want to read both database tables and session state.
Create the context and the package with the getter and setter
CREATE CONTEXT my_context
USING pkg_foo
ACCESSED GLOBALLY;
create or replace package pkg_foo
as
procedure set_var( p_session_id in number,
p_in in number );
function get_var( p_session_id in number )
return number;
end;
create or replace package body pkg_foo
as
procedure set_var( p_session_id in number,
p_in in number )
as
begin
dbms_session.set_identifier( p_session_id );
dbms_session.set_context( 'MY_CONTEXT', 'G_VAR', p_in, null, p_session_id );
end;
function get_var( p_session_id in number )
return number
is
begin
dbms_session.set_identifier( p_session_id );
return sys_context('MY_CONTEXT', 'G_VAR');
end;
end;
In session 1, set the value of the context variable G_VAR to 47 for session 12345
begin
pkg_foo.set_var( 12345, 47 );
end;
Now, session 2 can read the value from the context
1* select pkg_foo.get_var( 12345 ) from dual
SQL> /
PKG_FOO.GET_VAR(12345)
----------------------
47