Oracle Lock , How do they differ? - oracle

What is the difference between the below two erros.as far as i understood, they happen in case of a Lock. But do you know the differene in scenarios where one might occure.
ORA-04021: timeout occurred while waiting to lock object
and
ORA-00054: resource busy and acquire with NOWAIT specified

Example for ORA-04021 might be this: there's a package in your schema. It contains a procedure which does some job that takes 15 minutes to finish. Someone runs that procedure. Meanwhile, you'd want to fix something in that package, so you edit its code and want to compile it. Well, you can't - it is being used so you'll have to wait until it is released. Oracle tells you that timeout occurred while you're waiting to lock the package and compile it.
Example for ORA-00054: there's a table. You update some values in it, but didn't commit (nor rollback) as you have to do something else as well. In another session, another user wants to alter one of table's columns (for example, enlarge its size). ALTER will then raise 0RA-00054 which says that table is busy (you're updating it in another session, right?) so you'll have to wait until transaction commits (or rollbacks).

Related

alter table constraint on unlock oracle

I am trying to write a script to be executed by a client who has no real knowledge of pl/sql,
I do a bunch of transactions in PL/sql to clean up their landscape and then have to add in some constraints to keep this from happening again, I commit automatically after the cleaning, and would like to finish my PL block with adding these constraints
only issue is I have no guarantee that the tables will not be locked when trying to add constraints to them, is there a wait until unlock type of command in oracle?
thank you, new to oracle and cannot seem to find this, I have combed through a bit of API but am at the point of time sensitivity and is proving very difficult to find when i think it seems like it would be a pretty regular issue with DB management
in your pl/sql block before execute any DDL you should call via execute immedaite :
LOCK TABLE <table_name> IN EXCLUSIVE MODE WAIT <n>;
where is number of seconds to wait.
But bear in mind even if you get lock after your first ddl it will be released because DDL commits automatically. So there is no way to guarantee that list of DDL commands will be executed one-by-one as in one transaction.
The command would be:
LOCK TABLE tab IN EXCLUSIVE MODE;
This will wait indefinitely if there is another session with a similar lock (which there should not be).
You can lock a table explicitly (with "WAIT") prior to a DDL, but that won't necessarily solve all of your problems if you are worried about another session holding an exclusive lock. Sounds more like you may have experienced an ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired due to a DML transaction in another session. This will eventually finish, so issuing the above lock command prior to ALTER TABLE will help you here. Just be aware that if there is a hung session elsewhere the LOCK TABLE command can wait indefinitely unless you have set a timeout.
So lets do an example:
In session A I start a transaction on tab
SQL> INSERT INTO tab VALUES(...);
In session B I attempt to add a constraint to the table.
SQL> alter table tab add constraint uk_name unique(name);
alter table tab add constraint uk_name unique(name)
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expire
So I'd have to try again. But instead if you precede your DDL with a LOCK ... EXCLUSIVE (WAIT)
SQL> lock table tab in exclusive mode;
-- indefinite period while the session A transaction blocks session B
Then session A commits or rolls back
SQL> commit;
Session B immediately continues when we see the lock command return Table(s) locked
Table(s) Locked.
SQL> alter table tab add constraint uk_name unique(name);
Table altered. (**comment Lock is released by the implicit commit issued the DDL statement)
SQL>
Since the DDL statement releases the lock, each DDL will need to be preceded with a fresh LOCK statement. If you want to use this, I recommend adding a timeout (as suggested in comments by Justin). Lets wait up to a minute before giving up.
SQL> lock table tab in exclusive mode WAIT 60;
Although if this is an unattended script on a busy database, I'd probably go with something more than 60 seconds. Just log everything to a spool file, and check the log later for errors.
Anything more than this sort of maintenance probably requires that you quiesce your database first, else just deal with the contention on a case by case basis.

What is the reason for ORA-00054 error?

From Oracle's documentation:-
ORA-00054 resource busy and acquire with NOWAIT specified
Cause: Resource interested is busy.
Action: Retry if necessary.
In our code we issue a SELECT FOR UPDATE NOWAIT command to lock the row we are about to update.
Right now the logic is if it returns SQL error 54 then it is assumed that another user is trying to update that same record. Is this logic valid?
From Oracle's documentation it looks more like if the DB is overwhelmed then this might also cause this error to be thrown.
What are the possible reasons for this error, when we are only using the above SQL command?
The SELECT ... FOR UPDATE attempts to acquire an RS (Row Share) lock on the table and an X (eXclusive) lock on the row. If another session has an exclusive lock on the table (eg creating an index) or an exclusive lock on the row (update, delete, or select for update) then the query will wait for the other transaction to release the lock (commit or rollback generally) unless you have specified NOWAIT.
So one possibility is to not specify NOWAIT.
I don't recognise the situation where the database might throw this error due to being "overwhelmed".

Finding all statements involved in a deadlock from an Oracle trace file?

As I understand it, the typical case of a deadlock involving row-locking requires four SQL statements. Two in one transaction to update row A and row B, and then a further two in a separate transaction to update the same rows, and require the same locks, but in the reverse order.
Transaction 1 gets the lock on row A before transaction 2 can request it, transaction 2 gets the lock on row B before transaction 1 can get it, and neither can get the remaining required locks. One or either transaction has to be rolled back, so the other can complete.
When I review an Oracle trace file after a deadlock, it only seems to highlight two queries. These seem to be the last one out of each transaction.
How can I identify the other statements involved in each transaction, or is this missing in an Oracle trace file?
I can include relevant bits of the specific trace file if required.
You're correct, in a typical row-level deadlock, you'll have session 1 execute sql_a that will lock row 1. Then session 2 will execute sql_b that will lock row 2. Then session 1 will execute sql_c to attempt to lock row 2, but session 2 has not committed, and so session 1 starts waiting. Finally, session 2 comes along, and it issues sql_d, attempting to lock row 1, but, since session 1 holds that lock, it starts waiting. Three seconds later, the deadlock is detected, and one of the sessions will catch ORA-00060 and the trace file is written.
In this scenario, the trace file will contain sql_c and sql_d, but not sql_a or sql_b.
The problem is that information just really isn't available anywhere. Consider that you execute a DML, it starts a transaction if one doesn't exist, generates a bunch of undo and redo, and the change is made. But, once that happens, the session is no longer associated with that SQL statement. There's really no clean way to go back and find that information.
sql_c and sql_d, on the other hand, are the statements that were associated with those sessions when the deadlock occurred, so, clearly, Oracle can identify them, and include that in the trace file.
So, you're correct, the information about sql_a and sql_b is not in the trace, and it's really not readily available.
Hope that helps.

Avoid locks in Oracle UPDATE command

If I am trying to acquire a lock in Oracle 10g (e.g. with SELECT...FOR UPDATE), there is a NOWAIT option to get an error when the row is locked, instead of the query just hanging. Is there a way to achive this for a simple UPDATE statement? There is a DDL_LOCK_TIMEOUT option in Oracle 11g, I would need something similar for DML operations (and in 10g).
(Background: I have some unit tests which query the database (which is unfortunately not an isolated test database, but a developement DB used for various things), and I want them to throw an error instantly instead of hanging when anything goes wrong.)
No. There is no way to have a simple UPDATE statement in Oracle time out if some other session has locked the row it is trying to update. You could, of course, code your unit tests to do a SELECT ... FOR UPDATE WAIT <<n>> before doing the UPDATE. That would ensure that by the time you got to the UPDATE, you would be guaranteed to already have the lock.
I'm also a bit confused by the idea that you'd be running unit tests against rows that other sessions are modifying at the same time you are. That would seem to defeat the purpose of having unit tests since it would never be clear whether a test failed because the code did something wrong or because some other session modified the data in an unexpected way during the test.

Do the time of the COMMIT and ROLLBACK affect performance?

Suppose I have a set of ID . For each ID , I will insert many records to many different tables based on the ID .Between inserting difference tables, different business checks will be called . If any checking fail , all the records that are inserted based on this ID will be ROLLBACK .This bulk insert action is done through using PL/SQL . Do the time of the COMMIT and ROLLBACK affect the performance and how does it affect ? For example , should I COMMIT after finish the process for one ID or COMMIT after finish all ID?
This is not so much of a performance decision but a process design decision. Do you want the other IDs to stay in the database when you have to roll back a faulty ID?
For obvious reasons, rollback takes longer when more rows must be rolled back. Rollback usually takes longer (sometimes much longer!) than the operations that have to be rolled back. Commit is always fast in Oracle, so it probably doesn't matter how often you commit in that regard.
Your problem description indicates you have a large set of smaller logical transactions (each new ID is a transaction). You should commit each logical transaction. The two reasons to wait to commit the entire set of transactions are:
If the entire set of transactions is in fact a transaction itself - all inserts must succeed for any rows to be committed. In that context, your smaller "transactions" aren't truly transactions.
You don't have a restart capability in your bulk load process, which in effect makes this a special case of item 1. If your bulk load process aborts, you need a way to skip successfully applied ID's.
Tom Kyte's advice is to commit each logical unit of work - the transaction.
Don't take the transaction time longer. make it short as possible as you can. Because according to your query some locks have been created. This locks may cause perfomance issues... so do it ID by ID...
There are two "forces" at work....
locking
during your open transaction, oracle puts locks on the changed rows.
whenever another transaction needs to update any of the locked rows,
it has to wait.
in the worst case, you can even build a deadlock.
synchronous write
every commit performs a synchronous write.
(there are ways to disable that, but it is usually the thing everybody wants: integrity).
that synchronous write can take (much) longer then the a regular write (that can be buffered).
Not to forget that there is usually an additional network round trip involved with an commit.
so, the one force says "commit as soon as possible (considering your integrity requirements)" the other says "commit as as less often as possible".
There are some other issues to consider as well, e.g. the maximum transaction size. every uncommited transaction needs some temporary space. the bigger the transaction gets, the more you need. You can also run into ORA-01555 "snapshot too old".
If there is any advice to give, then it is to implement a configurable "commit frequency" so that you can easily change it as needed.
One option if you need to control the individual sets but retain the ability to commit or rollback the entire transaction is to use savepoints. You can set a savepoint at the beginning of the outermost loop, then rollback to it if an error occurs. You might end up with something like this:
begin
--Initial batch logging
for r_record in cur_cursor loop
savepoint s_cursor loop;
begin
--Process rows
exception
when others then
rollback to s_cursor;
end;
end loop;
--Final batch logging
exception
when others then
rollback;
raise;
end;

Resources