Oracle procedure to kill jobs that do not complete in given time - oracle

I have created an oracle procedure to reschedule job that do not complete in a given amount of time:
create or replace procedure kill_stuck_jobs
as
begin
for x in (
select j.sid,
s.spid,
s.serial#,
j.log_user,
j.job,
j.broken,
j.failures,
j.last_date,
j.this_date,
j.next_date,
j.next_date - j.last_date interval,
j.what
from
(select
djr.SID,
dj.LOG_USER,
dj.JOB,
dj.BROKEN,
dj.FAILURES,
dj.LAST_DATE,
dj.LAST_SEC,
dj.THIS_DATE, dj.THIS_SEC,
dj.NEXT_DATE, dj.NEXT_SEC, dj.INTERVAL, dj.WHAT
from dba_jobs dj, dba_jobs_running djr
where dj.job = djr.job ) j,
(select p.spid, s.sid, s.serial#
from v$process p, v$session s
where p.addr = s.paddr ) s
where j.sid = s.sid and
j.next_date+15/1440 < sysdate
) loop
EXEC DBMS_JOB.BROKEN(x.job,TRUE);
execute immediate 'alter system disconnect session '''|| x.sid|| ',' || x.serial# || ''' immediate';
EXEC DBMS_JOB.BROKEN(x.job,FALSE);
dbms_output.put_line( 'Alter session done' );
end loop;
end;
But this procedure compiles with error:
PLS-00103: Encountered the symbol "DBMS_JOB" when expecting one of the following:
:= . ( # % ;
The symbol ":=" was substituted for "DBMS_JOB" to continue.
Here came the discussion on dbms_job and dbms_scheduler_job. Actually the problem here is I created materialized view using linked db but sometime the query is stuck for whole day in session with SQL*Net more data from dblink . I used the above procedure to kill jobs that are created while creating materialized view and I am killing the session using :
create or replace procedure kill_stuck_refresh as
begin
for x in (
select username, osuser, sid, serial#, seconds_in_wait,
event, state, wait_class
from v$session
where username is not null
and seconds_in_wait > 600
and event = 'SQL*Net more data from dblink'
) loop
execute immediate 'alter system disconnect session '''|| x.sid
|| ',' || x.serial# || ''' immediate';
dbms_output.put_line( 'Alter session done' );
end loop;
end; -- end of kill_stuck_refresh;

Your error stack all derives from the fact that your cursor is invalid. The cause is the ORA-00942. This can mean the table name is spelled wrong, but as you're using the data dictionary it usually points to a permissions problem, that is, the tables (or views in this case) do not exist in your scope.
Which brings us to your comment:
"I can run the select query independently I think that mean I have
those priviledge. "
This suggests to me that your permissions have been granted to a role you have rights on. We can use permissions granted through roles in SQL but cannot use them to build permanent objects such as views or stored procedures. For this to work you need to have permissions on those views granted directly to your user. There is no workaround, this is the way the Oracle security model works.
Incidentally, you should investigate why these jobs are taking too long. I'm assuming this is a persistent problem (otherwise why are you building a stored proc to kill these jobs?). Killing jobs is a blunt instrument which wastes resources and obliterates helpful evidence. A better idea would be to find out the underlying cause of the poor performance and fix it. Perhaps there's a problem with blocking sessions and you need a locking strategy. Maybe you've got soem poorly tuned SQL. Some other job may be running at the same time and sucks up all the resources, in which case you need a decent scheduler. Or possibly you've just got more data now - the price of success - and you need to give the jobs more time to complete.

You do not need EXEC in this context (in a stored procedure/function). It's only needed when you want to call a stored procedure from SQL (from SQL*Plus). Just call dbms_job.broken() directly:
...
DBMS_JOB.BROKEN(x.job,TRUE);
execute immediate 'alter system disconnect session '''|| x.sid|| ',' || x.serial# || ''' immediate';
DBMS_JOB.BROKEN(x.job,FALSE);
dbms_output.put_line( 'Alter session done' );
...

As #David Aldridge mentioned, DBMS_Scheduler is largely meant to replace DBMS_Job (starting with 10g, actually). What he didn't mention is why this would help you.
DBMS_Scheduler provides a lot more control over it's jobs, including better logging. However, most significantly for you, DBMS_Scheduler adds a max_run_duration parameter to the job. If a job's run-time surpasses that duration, the job is automatically ended (and probably in a more graceful fashion than killing the session). Then, as long as the max_failures parameter isn't set, the job will be attempted again at it's next scheduled time.
If you can migrate your current jobs to DBMS_Scheduler, you can save yourself the trouble of writing code to provide functionality that already exists.

If you are using Oracle 11g then you ought to be using DBMS_Scheduler, not DBMS_Job. Look first at migrating to the new package, and rethink your requirement in that context.

Related

Oracle - How to execute this script in parallel

I have a script to kill all sessions in Oracle:
declare
begin
for rec in (
SELECT username,machine,sid,serial#,inst_id from gv$session where type <> 'BACKGROUND'
and sid <> (select sys_context('userenv','sid') from dual) and status <> 'KILLED'
and username not in ('SYSRAC','SYS')
)
loop
execute immediate 'alter system disconnect session '''|| rec.sid|| ',' || rec.serial# || ',#' ||rec.inst_id||''' immediate';
end loop;
end;
/
Once a month I need kill all session before execute several scripts to add/remove/modify columns, grants and revokes, create new objects, drop objects etc..
This database usually has more than 2000 connections and when I execute this script it takes from 15 to 20 minutes to kill all sessions. How can I execute this script in parallel and kill all sessions faster?
A few things to absorb there ...
2000 connections sounds like overkill, especially if you're killing all of them which suggests they all belong to the same app. I'd be checking your connection pooling stats to ensure this isn't too many.
It might be worth checking out edition based redefinition to allow deployment without any downtime
If you're killing everything anyway, just do shutdown abort/startup and you're done
If you really want to kill those sessions, then you can use DBMS_PARALLEL_EXECUTE to get the job scheduler to run these concurrently.
But step back and take a look at the whole picture here, because generally, we don't kill all sessions to deploy code.

Oracle 'after create' trigger to grant privileges

I have an 'after create on database' trigger to provide select access on newly created tables within specific schemas to different Oracle roles.
If I execute a create table ... as select statement and then query the new table in the same block of code within TOAD or a different UI I encounter an error, but it works if I run the commands separately:
create table schema1.table1 as select * from schema2.table2 where rownum < 2;
select count(*) from schema1.table1;
If I execute them as one block of code I get:
ORA-01031: insufficient privileges
If I execute them individually, I don't get an error and am able to obtain the correct count.
Sample snippet of AFTER CREATE trigger
CREATE OR REPLACE TRIGGER TGR_DATABASE_AUDIT AFTER
CREATE OR DROP OR ALTER ON Database
DECLARE
vOS_User VARCHAR2(30);
vTerminal VARCHAR2(30);
vMachine VARCHAR2(30);
vSession_User VARCHAR2(30);
vSession_Id INTEGER;
l_jobno NUMBER;
BEGIN
SELECT sys_context('USERENV', 'SESSIONID'),
sys_context('USERENV', 'OS_USER'),
sys_context('USERENV', 'TERMINAL'),
sys_context('USERENV', 'HOST'),
sys_context('USERENV', 'SESSION_USER')
INTO vSession_Id,
vOS_User,
vTerminal,
vMachine,
vSession_User
FROM Dual;
insert into schema3.event_table VALUES (vSession_Id, SYSDATE,
vSession_User, vOS_User, vMachine, vTerminal, ora_sysevent,
ora_dict_obj_type,ora_dict_obj_owner,ora_dict_obj_name);
IF ora_sysevent = 'CREATE' THEN
IF (ora_dict_obj_owner = 'SCHEMA1') THEN
IF DICTIONARY_OBJ_TYPE = 'TABLE' THEN
dbms_job.submit(l_jobno,'sys.execute_app_ddl(''GRANT SELECT
ON '||ora_dict_obj_owner||'.'||ora_dict_obj_name||' TO
Role1,Role2'');');
END IF;
END IF;
END IF;
END;
Jobs are asynchronous. Your code is not.
Ignoring for the moment the fact that if you're dynamically granting privileges that something in the world is creating new tables live in production without going through a change control process (at which point a human reviewer would ensure that appropriate grants were included) which implies that you have a much bigger problem...
When you run the CREATE TABLE statement, the trigger fires and a job is scheduled to run. That job runs in a separate session and can't start until your CREATE TABLE statement issues its final implicit commit and returns control to the first session. Best case, that job runs a second or two after the CREATE TABLE statement completes. But it could be longer depending on how many background jobs are allowed to run simultaneously, what other jobs are running, how busy Oracle is, etc.
The simplest approach would be to add a dbms_lock.sleep call between the CREATE TABLE and the SELECT that waits a reasonable amount of time to give the background job time to run. That's trivial to code (and useful to validate that this is, in fact, the only problem you have) but it's not foolproof. Even if you put in a delay that's "long enough" for testing, you might encounter a longer delay in the future. The more complicated approach would be to query dba_jobs, look to see if there is a job there related to the table you just created, and sleep if there is in a loop.

Oracle: automate dropping and recreating connected user

I am trying to DROP a connected user with Oracle. This question has been asked well over 700 million times, thanks to Oracle being a great software product. All answers I find, however, require user input and can not be automated as posted. The typical answer is well stated here: https://stackoverflow.com/a/15665694/177293
I want to do this in a script without a human having to intervene. So, I'm trying like this:
BEGIN
FOR i IN (
SELECT sid, serial# from v$session where username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE 'alter system kill session ''' || i.sid || ',' || i.serial# || ''';';
END LOOP;
END;
Which, hilariously, in sqlplus results in:
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 5
Trying a slightly different tactic:
BEGIN
FOR i IN (
SELECT
'alter system kill session ''' || sid || ',' || serial# || ''';'
from v$session where username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE ''|| i ||'';
END LOOP;
END;
results in:
EXECUTE IMMEDIATE ''|| i ||'';
*
ERROR at line 7:
ORA-06550: line 7, column 21:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 7, column 3:
PL/SQL: Statement ignored
I'm hoping to have a finished script that looks something like this:
#!/bin/sh
sqlplus system/manager <<EOT
BEGIN
FOR i IN (
SELECT sid, serial# FROM v$session WHERE username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE 'alter system kill session ''' || i.sid || ',' || i.serial# || ''';';
END LOOP;
END;
DROP USER MYUSER CASCADE;
DROP TABLESPACE MYUSER INCLUDING CONTENTS AND DATAFILES;
CREATE USER MYUSER IDENTIFIED BY password;
ALTER USER MYUSER IDENTIFIED BY password;
GRANT connect, resource TO MYUSER;
exit;
EOT
What is wrong with the above?
The main problem with the above scripts is that ; should not be in dynamic SQL.
But don't let that fool you, this is still an incredibly difficult problem. Unfortunately, it is rare for Oracle systems to frequently drop and re-create users. This means you will run into lots of weird bugs.
I've built scripts to do this, but unfortunately cannot share them here. Here are some of the fun things you have to look forward to:
Killing connected sessions.
Killing other sessions that are blocking objects owned by your session.
Truncating temporary tables because they have weird locking rules. And killing sessions using them, see HOW TO DIAGNOSE AN ORA-14452 DURING DROP OF TEMPORARY TABLE (Doc ID 800506.1).
Using INST_ID in the KILL syntax, for RAC.
Looping through the whole process a few times, in case an application is spawning new sessions that lock objects.
If you're using object-relational code, execute dbms_session.reset_package. Amazingly, some objects can persist even after dropping and recreating their owners.
Expect to write about a hundred lines of code, if it's a complicated application. Expect to spend a few weeks of testing to shake out some weird bugs. Expect it to still fail occasionally.
Yes, this is ridiculous. Oracle spends a lot of effort on ways to quickly create new databases; transportable tablespaces, pluggable databases, virtualization, etc. You'd think there would be a good way to reliably drop and recreate a user!

How to see and kill a list of sessions being made to an ORACLE schema?

I am attempting to diagnose an issue and for that need to see the list of connections being made to a specific Oracle Schema.
Assuming that I have DBA privileges, what are the queries that I should run to :
List the sessions (active and otherwise) to an Oracle schema and,
Kill these sessions
Thank you!
Here are the queries you will need to execute:
-- 1. Check connected sessions
select sid, serial#, username, machine,
to_char(logon_time+5/24,'ddMon hh24:mi') login,
SQL_HASH_VALUE, PREV_HASH_VALUE,
status
from v$session
where
lower(username) like '%SCHEMA_NAME%'
--and lower(status) not like '%killed%'
--and machine like '%SOURCE_MACHINE_NAME%'
order by logon_time;
-- 2. Same as above, but just show the count of sessions
select count(1)
from v$session
where lower(username) like lower('%SCHEMA_NAME%')
--and lower(status) not like '%inactive%'
order by logon_time;
-- 3. Kill connected sessions
ALTER SYSTEM ENABLE RESTRICTED SESSION;
begin
for x in (
select Sid, Serial#, machine, program
from v$session
where lower(username) like '%SCHEMA_NAME%'
) loop
execute immediate 'Alter System Kill Session '''|| x.Sid
|| ',' || x.Serial# || ''' IMMEDIATE';
end loop;
end;
ALTER SYSTEM DISABLE RESTRICTED SESSION;
-- May have to wait for a bit for the killed sessions to be cleaned up
I have been using the above on an Oracle 11g database so would expect them to work for you too.
Note that I have included some commented out where clauses in the first two queries which would allow you to refine the search criteria.
Hope this is what you were looking for.

Dropping a connected user from an Oracle 10g database schema

Is there a better way to forcefully disconnect all users from an Oracle 10g database schema than restarting the Oracle database services?
We have several developers using SQL Developer connecting to the same schema on a single Oracle 10g server. The problem is that when we want to drop the schema to rebuild it, inevitably someone is still connected and we cannot drop the database schema or the user while someone is still connected.
By the same token, we do not want to drop all connections to other schemas because other people may still be connected and testing with those schemas.
Anyone know of a quick way to resolve this?
To find the sessions, as a DBA use
select sid,serial# from v$session where username = '<your_schema>'
If you want to be sure only to get the sessions that use SQL Developer, you can add and program = 'SQL Developer'. If you only want to kill sessions belonging to a specific developer, you can add a restriction on os_user
Then kill them with
alter system kill session '<sid>,<serial#>'
(e.g. alter system kill session '39,1232')
A query that produces ready-built kill-statements could be
select 'alter system kill session ''' || sid || ',' || serial# || ''';' from v$session where username = '<your_schema>'
This will return one kill statement per session for that user - something like:
alter system kill session '375,64855';
alter system kill session '346,53146';
Find existing sessions to DB using this query:
SELECT s.inst_id,
s.sid,
s.serial#,
p.spid,
s.username,
s.program
FROM gv$session s
JOIN gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id
WHERE s.type != 'BACKGROUND';
you'll see something like below.
Then, run below query with values extracted from above results.
ALTER SYSTEM KILL SESSION '<put above s.sid here>,<put above s.serial# here>';
Ex:
ALTER SYSTEM KILL SESSION '93,943';
my proposal is this simple anonymous block:
DECLARE
lc_username VARCHAR2 (32) := 'user-name-to-kill-here';
BEGIN
FOR ln_cur IN (SELECT sid, serial# FROM v$session WHERE username = lc_username)
LOOP
EXECUTE IMMEDIATE ('ALTER SYSTEM KILL SESSION ''' || ln_cur.sid || ',' || ln_cur.serial# || ''' IMMEDIATE');
END LOOP;
END;
/
Make sure that you alter the system and enable restricted session before you kill them or they will quickly log back into the database before you get your work completed.
Just my two cents : the best way (but probably not the quickest in the short term) would probably be for each developer to work on his own database instance (see rule #1 for database work).
Installing Oracle on a developer station has become a no brainer since Oracle Database 10g Express Edition.
Have you tried ALTER SYSTEM KILL SESSION? Get the SID and SERIAL# from V$SESSION for each session in the given schema, then do
ALTER SCHEMA KILL SESSION sid,serial#;
just use SQL :
disconnect;
conn tiger/scott as sysdba;

Resources