Avoid waiting on sending HTTP request via Oracle database - oracle

I have an application that is connected to an Oracle database where it can execute select, insert and update statements. I want to call a function in that application that would tell the Oracle database to send a http request.
My objectives:
Fire and forget - the application and database don't care about the response or whether the request was successful
The application and database should never wait for the response
I was thinking about these approaches:
Application inserts a row to a table, trigger after insert calls a db function that uses UTL_HTTP to perform the request
Application calls a db function that uses UTL_HTTP to perform the request via a select statement
What are the advantages/pitfalls of said approaches with regard to my objectives?

Here are some thoughts of the top of my head, I'm sure there are other considerations:
If you do it in a trigger, the http request will go out even if you then decide to rollback the insert. So are you okay with having the http request sent but no row ends up in the table? If so, a trigger would be fine.
If you do it in a trigger, then any other interfaces or humans manually needing to load rows to the table will cause the http request to fire. Is that what you want? Then a trigger is great. If not, a trigger isn't so great.
Triggers are dropped if the table is dropped. If you do maintenance of the kind we do all the time in data warehousing (CTAS a new table, drop the old, rename the new to the old, etc..) you can easily lose your code because the code is contained in the trigger and the trigger disappears with the table drop. Triggers are not safe places for complex code. If you do end up using a trigger, consider having the trigger simply call a procedure, and store all your real code in that procedure.
If you decide to not use a trigger at all, you are better off writing a procedure than a function you call through a select statement. The point of a function is to return something, and in this case, you don't have anything from the http process you are wishing to return. And the select statement would be arbitrary. Just use a normal procedure call.

Related

PL/SQL - retrieve output

Is there a way to retrieve output from PL/SQL continuously rather than wait until the SP completes its execution. Continuously mean as when it executes the execute immediate.
Any other mechanism to retrieve pl/sql output?
As per Oracle docs
Output that you create using PUT or PUT_LINE is buffered in the SGA. The output cannot be retrieved until the PL/SQL program unit from which it was buffered returns to its caller. So, for example, Enterprise Manager or SQL*Plus do not display DBMS_OUTPUT messages until the PL/SQL program completes.
As far as I know, there is a way, but not with DBMS_OUTPUT.PUT_LINE. Technique I use is:
create a log table which will accept values you'd normally display using DBMS_OUTPUT.PUT_LINE. Columns I use are
ID (a sequence, to be able to sort data)
Date (to know what happened when; might not be enough for sorting purposes because operations that take very short time to finish might have the same timestamp)
Message (a VARCHAR2 column, large enough to accept the whole information)
create a logging procedure which will be inserting values into that table. It should be an autonomous transaction so that you could COMMIT within (and be able to access data from other sessions), without affecting the main transaction
Doing so, you'd
start your PL/SQL procedure
call the logging procedure whenever appropriate (basically, where you'd put the DBMS_OUTPUT.PUT_LINE call)
in another session, periodically query the log table as select * from log_table order by ID desc
Additionally, you could write a simple Apex application with one report page which selects from the logging table and refreshes periodically (for example, every 10 seconds or so) and view the main PL/SQL procedure's execution.
The approach that Littlefoot has provided is what I normally use as well.
However, there is another approach that you can try for a specific use case. Let's say you have a long-running batch job (like a payroll process for example). You do not wish to be tied down in front of the screen monitoring the progress. But you want to know as soon as the processing of any of the rows of data hits an error so that you can take action or inform a relevant team. In this case, you could add code to send out emails with all the information from the database as soon as the processing of a row hits an error (or meets any condition you specify).
You can do this using the functions and procedures provided in the 'UTL_MAIL' package. UTL_MAIL Documentation from Oracle
For monitoring progress without the overhead of logging to tables and autonomous transactions. I use:
DBMS_APPLICATION.SET_CLIENT_INFO( TO_CHAR(SYSDATE, 'HH24:MI:SS') || ' On step A' );
and then monitor in v$session.client_infofor your session. It's all in memory and won't persist of course but it is a quick and easy ~zero cost way of posting progress.
Another option (Linux/UNIX) for centralised logging that is persistent and again avoids logging in the database more generally viewable that I like is interfacing to syslog and having Splunk or similar pick these up. If you have Splunk or similar then this makes the monitoring viewable without having to connect to the database query directly. See this post here for how to do this.
https://community.oracle.com/thread/2343125

Can Coldfusion ORM access an Oracle trigger

I have ORM setup and working with Oracle on an existing database and have been able to get inserts to work when I access the sequence but because triggers were used in the original application the sequence skips a number.
Is there a way to get ORM to use the trigger?
Disabling the trigger is not an option since it is used by the existing app and cannot be disabled during migration.
component persistent="true" table="table_name" schema="schema_name" {
property name="table_id" column="table_id" fieldtype="id" generator="sequence" sequence="schema_name.sequence_name";
...
}
Triggers are not accessible program units. The only way to "call" a trigger is to execute the appropriate DML against the owning table.
There are two possible resolutions to your problem.
Rewrite the trigger. You say another application still needs the trigger to populate the ID, but you could change the trigger's logic with a conditional....
if :new.id is null then
:new.id := whatever_seq.nextval; --11g syntax for brevity
end if;
This will populate the ID when the other application insert into the table but won't overwrite your value.
Stop worrying. Sequences are merely generators of unique identifiers. The numbers ascend but it really doesn't matter if there are gaps. Unless you are handling billions of rows it is extremely unlikely your sequence will run out of numbers before your applications get retired.
Do you mean that the DB normally assigns an ID, using an insert trigger? That would explain why you're skipping a number. You could try generator="select" which will get hibernate to read the ID back after the insert has occurred (and the trigger has been fired). It's there to handle exactly the situation I think you're describing.

select query to wait for insertion of other record

In my application multiple requests simultaneously read record from one table and based on that insert new record in table.
I want to execute request serially so that the second request reads the latest value inserted by the first request.
I tried to achieve this using select for update query but it lock only row to be wait for update, as I can't update existing record it got same value as previous request got.
Is it possible using Oracle locking mechanism? How?
Dude - that's what transactions are for!
Strong suggestion:
Put your code into a PL/SQL stored procedure
Wrap the select/insert in a "begin tran/commit"
Don't even think about locks, if you can avoid it!

How can I close Oracle DbLinks in JDBC with XA datasources and transactions to avoid ORA-02020 errors?

I have a JDBC-based application which uses XA datasources and transactions which span multiple connections, connected to an Oracle database. The app sometimes needs to make some queries using join with a table from another (Oracle) server using a shared DbLink. The request works if I don't do it too often, but after 4 or 5 requests in rapid succession I get an error (ORA-02020 - too many links in use). I did some research, and the suggested remedy is to call "ALTER SESSION CLOSE DATABASE LINK ". If I call this request after the query that joins the DbLnk table, I get the error ORA-2080 (link is in use). If I call it before the query, I get ORA-2081 (link closed). Does this call do any good at all? The JDBC connection is closed long before the transaction commit (which is managed either by servlet or by EJB container, depending on the circumstances). I get the impression that when the connection closes, Oracle marks the link as closed, but it takes a minute or two for it to return to the pool of available links. I understand I could enlarge the pool of links (using the open_links property in the config file), but that won't guarantee that I won't have the same problem under a heavier load. Is there something I can do differently to get the dblinks to close more rapidly?
Any distributed SQL, even a select, will open a transaction that must be closed before you can close the database link. You need to either rollback or commit before you call ALTER SESSION CLOSE DATABASE LINK.
But it sounds like you've already got something else handling your transactions. If it's not possible to manually rollback or commit, you should try to increase the number of open links. The OPEN_LINKS parameter is the maximum number of links per session. The number of links you need isn't really dependent on the load, it should be based on the maximum number of distinct remote databases.
Edit:
The situation you describe in your comment shouldn't happen. I don't understand enough about your system to know what's really happening with the transactions. Anyway, if you can't figure out exactly what the system is doing maybe you can replace "alter session close database link" with a procedure like this:
create or replace procedure rollback_and_close_db_links authid current_user is
begin
rollback;
for links in (select db_link from v$dblink) loop
execute immediate 'alter session close database link '||links.db_link;
end loop;
end;
/
You'll probably need this grant:
grant select on v_$dblink to [relevant user];

Oracle: How to execute an insert trigger without delaying the insert response?

The trigger below is delaying my insert response. How can I prevent this?
create or replace
TRIGGER GETHTTPONINSERT
BEFORE INSERT ON TABLENAME
FOR EACH ROW
Declare
--
BEGIN
-- The inserted data is transfered via HTTP to a remote location
END;
EDIT People are telling me to do batch jobs, but I would rather have the data earlier than having 100% consistency. The advantage of the trigger is that it happens as soon as the data arrives, but I can't afford the insert response delay.
One approach is to have the trigger create a dbms_job that runs once (each) time to perform the http transfer. The dbms_job creation is relatively quick and you can think of this as effectively spawning a new thread in parallel.
See http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:7267435205059 for further info - his example deals with sending email, but the idea is the same.
There is a perfect solution for this exact situation called Database Change Notification.
You can think of it almost exactly like an async trigger.
You use the DBMS_Change_Notification package to tell oracle which tables to watch and what to do when a change occurs. You can monitor for DML and DDL, you can have Oracle batch the changes (i.e. wait for 10 changes to occur before firing). It will call a sproc with an object containing all the rowids of the changed rows... you can decide how to handle, including calling HTTP. It will not have to finish for the insert to commit.
Documentation for 10gR2
Maybe you could create a local table that store the info do you have to transfer, and create a job that executes every X minutes. The job read from the table, transfer all the data and delete the transfered data from the table.
Isn't it possible to use the Oracle replication options? You send your inserted data via http to a remote location in an after or before statement trigger. What will happen when there is a rollback? Your hhtp send message will not be rollbacked so you have inconsistent data.
well obviously, you could prevent the delay by removing the Trigger....
Else, the trigger will ALWAYS be executed before your insert, thats what the TRIGGER BEFORE INSERT is made for.
Or maybe you could give us more details on what you need exactly?
If you are getting to this question after 2020, look at DBMS_CQ_NOTIFICATION:
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_CQ_NOTIFICATION.html

Resources