OraStory

Entries from August 2007

Interval Insanity

August 31, 2007 · 7 Comments

A colleague of mine has had some problem with using the INTERVAL datatype within date operations within some production code.

I believe that it is recently deployed code and it being the 31 August today, a production problem was highlighted.

The fundamental issue has been highlighted elsewhere previously, for example at oracle-wtf and asktom (of course).

I’m sorry, but this is cr4p.

Ladies and Gentlemen, I give you an interval of one month, and add it to yesterday, the 30th August 2007:

Add one month to the 30th August 2007 and you get 30th September 2007. Unsurprisingly.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production
SQL> select to_date('30/08/2007','DD/MM/YYYY') dt1
2 , to_date('30/08/2007','DD/MM/YYYY') + interval '1' month dt2
3 from dual;


DT1 DT2
--------- ---------
30-AUG-07 30-SEP-07

Add one month to 31st August 2007 and you’d expect to get, or at least I’d expect to also get, 30th September 2007. But no, you get an invalid date.


SQL> ed
Wrote file afiedt.buf


1 select to_date('31/08/2007','DD/MM/YYYY') dt1
2 , to_date('31/08/2007','DD/MM/YYYY') + interval '1' month dt2
3* from dual
SQL> /
, to_date('31/08/2007','DD/MM/YYYY') + interval '1' month dt2
*
ERROR at line 2:
ORA-01839: date not valid for month specified

It seems that INTERVAL functionality has been implemented to what is allegedly the letter of the ANSI standard but with what is surely an obvious flaw … madness.

Categories: interval · oracle

OraStory Activity

August 31, 2007 · 1 Comment

It is fascinating to review the blogstats for my site and see how visitors end up here – notably what the search terms that they use to end up with. This provides valuable insight into community issues.

I’m not a prolific poster and I don’t have a huge number of regular readers, so apart from when I have written something new and I appear on the main community feeds, e.g. OraNa and or community summaries like log buffer, most of my traffic is from search engines.

From the search engine terms that drive visitors here, my article on deadlocks and unindexed foreign keys has connected with loads of people searching for about this issue. And when I wrote it, I was thinking that most people would know about these things.

Unexpectedly, quite a few people have run into the same problem that I did with cursor_sharing, mdsaora and ora-00937.

A not insignificant number are interested in using shareplex to migrate to 10g.

There are so many posts about it and so unsurprisingly many are searching about varying in lists.

Finally, I’ve always maintained that there are two subject matters that are either unintuitive or come along so slowly but steadily that you can never recall what you learned previously – charactersets and nls conversions.

What would be great, especially when ideas for blog entries are a bit thin on the ground, would be to be able to look at the top x searches on Google that involve “Oracle” (searches in general rather than the lucky (?) few who actually end up here) and then use that to drive what you write about. I had a very brief look and I couldn’t see anything that provided this functionality.

Categories: oracle

Know thy database fundamentals

August 15, 2007 · Leave a Comment

It’s so important to know your database features and the idiosyncracies of the particular database that you are using.

On a recent project, this point was highlighted by some code designed to feed updated information from database A to database B and back again.

Both these feeds – feed 1 from database A to database B and feed 2 from database B to database A – relied on selecting records from a certain range.

Feed 1 is selecting by last update date. Everytime it runs, it stores the maximum last update date from the records selected and uses that to drive the minimum date when running the next time. So it selects the records with a last update date between that maximum date selected last time and the current time.

 In a similar vein, Feed 2 works off a sequence number. The table primary key of the table that drives this query is a sequence generated number. Each time Feed 2 runs, it stores the maximum sequence number from the range of data selected and uses that the next time it runs. So it selects the records with a sequence number between the maximum one selected last time and the sequence nextval.

Can you see what the problem is?

This affects both these feeds and relates to knowing how Oracle deals with concurrency and read consistency.

With three sqlplus windows each connected as the same user:


FEED@DOM@XE>
create table tab1 (col1 date);


FEED@DOM@XE>select to_char(col1,'DD-MM-YYYY HH24:MI:SS') from tab1;


no rows selected
.....

SESS2@DOM@XE>insert into tab1 values (sysdate);

1 row created.

SESS2@DOM@XE>exec dbms_lock.sleep(50)

.....

SESS1@DOM@XE>insert into tab1 values (sysdate);

1 row created.

SESS1@DOM@XE>commit;

Commit complete.

.....

FEED@DOM@XE>l
1 select to_char(col1,'DD-MM-YYYY HH24:MI:SS')
2* from tab1
FEED@DOM@XE>/

TO_CHAR(COL1,'DD-MM
-------------------
15-08-2007 09:12:26

.....

SESS2@DOM@XE>exec dbms_lock.sleep(50)

PL/SQL procedure successfully completed.

SESS2@DOM@XE>commit;

Commit complete.

.....

FEED@DOM@XE>l
1 select to_char(col1,'DD-MM-YYYY HH24:MI:SS')
2* from tab1
FEED@DOM@XE>/

TO_CHAR(COL1,'DD-MM
-------------------
15-08-2007 09:11:49
15-08-2007 09:12:26

So, in the feed situation I described above, the feed runs, it picks up the row inserted by SESS1, and it stores that date as the maximum date fed. Our slightly slower transaction in SESS2 was doing some more work or waiting on something (simulated by a call to dbms_lock) and will never be picked up. It’s last update date is before that of SESS1. Writers don’t block readers and it obviously wasn’t visible at the time that the feed ran as it was uncommitted. So the feed destination never gets notified.

It’s basic fundamentals at the end of the day.

The good news is that both these feeds are candidates to be replaced by an AQ queue.

Categories: oracle

Deadlock detection – unindexed foreign keys

August 14, 2007 · 4 Comments

Recently I have had a shortage of things to blog about. Nevertheless I have resisted the urge to join the cacophony by just announcing that 11g is in fact available for download on Linux.

Sure enough, eventually an opportunity has presented itself. Well, two opportunities but more of the other elsewhere later, but in this instance – deadlocks, specifically regular and numerous deadlocks caused by regular and numerous jobs, jobs which should have been non-conflicting.

However, dear reader, I shall spoil the surprise by revealing that these deadlocks were caused by unindexed foreign keys – more detail further down. It’s an old problem that’s been around for a long time, but it still happens (although, granted, more often than not in the databases that I see these days, this tends not to be a problem because people haven’t bothered with any sort of constraint in their database, but that’s a rant for another day).

I’m reluctant to admit it for fear of being shot down by the BAAG police ;-) , but at the time of my proclaimation that unindexed foreign keys was the issue, it was a hunch, a gut feeling backed by a decade of experience and the briefest of brief glances at the resulting trace file from the deadlock (is it just me or is there a blurred line between a guess and a hunch / gutfeeling / shortcut that experience suggests?) . Fortunately, a longer look at the trace file backed me up and the subsequent indexing of the columns in the foreign keys was a massive success.

From an end user perspective (including both developers connecting to the database via whatever tool and also application users), the symptoms initially were of an unresponsive development database, seemingly hanging every couple of minutes for a couple of minutes.

Immediately, to the alert log, where there were deadlock entries and in the background dump dest, where there were trace files from the jobs with more detailed information on the deadlocks detected.

Deadlock detection takes some time and so whilst waiting for these to be detected, the sessions on both sides of the deadlock were hogging the limelight and in the meantime bringing the database to its knees for everyone else.

In the trace files, the regular entries were all very similar, such as this:


*** SESSION ID:(37.18266) 2007-08-09 03:22:47.678
DEADLOCK DETECTED
Current SQL statement for this session:
UPDATE TABLE_A RC SET COLUMN_A = :B2 WHERE COLUMN_B = :B1
----- PL/SQL Call Stack -----
object line object
handle number name
000006FBE7909428 375 package body PACKAGE_A
000006FBE7909428 1443 package body PACKAGE A
000006FBF4399740 1 anonymous block
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TM-00024f42-00000000 17 37 SX SSX 24 22 SX SSX
TM-00024f42-00000000 24 22 SX SSX 17 37 SX SSX
session 37: DID 0001-0011-00023C06 session 22: DID 0001-0018-000032FA
session 22: DID 0001-0018-000032FA session 37: DID 0001-0011-00023C06

The key information was firstly the statement being issued. This was always the same – there was just this one statement causing all the problem:


UPDATE TABLE_A RC SET COLUMN_A = :B2 WHERE COLUMN_B = :B1

Secondly, the sessions that were deadlocking were using different values for :B1 – this was visible from the additional bespoke logging as part of this PACKAGE_A code module.

Next, the deadlocks caused by this one statement were on a variety of objects – all child tables of TABLE_A or primary key indexes on child tables of TABLE_A.

In this particular trace file:


---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TM-00024f42-00000000 17 37 SX SSX 24 22 SX SSX
TM-00024f42-00000000 24 22 SX SSX 17 37 SX SSX

the two sessions are trying to escalate a held TM lock from SX to SSX. A TM lock, a DML Enqueue, makes sure that the structure of a table is not altered whilst, in this example, you are updating a row in a table.
From the concepts guide:

A row exclusive table lock (also called a subexclusive table lock, SX) generally indicates that the transaction holding the lock has made one or more updates to rows in the table.
...
A share row exclusive table lock (also sometimes called a share-subexclusive table lock, SSX) is more restrictive than a share table lock... Only one transaction at a time can acquire a share row exclusive table lock on a given table. A share row exclusive table lock held by a transaction allows other transactions to query or lock specific rows using SELECT with the FOR UPDATE clause, but not to update the table... A share row exclusive table lock held by a transaction prevents other transactions from obtaining row exclusive table locks and modifying the same table.

What object is this TM lock being held on?
Note the lock name – TM-00024f42-00000000 – that 00024f42 is a hex number, 151362 is its decimal converstion. Look up object_id 151362 in dba_objects and you’ll find the details of the object. In the continued interest of obscuring client code and object names, in this example it was TABLE_B, a child of TABLE_A with a foreign key enforcing the constraint but no index.

So, to the solution – to index all the foreign keys between these handful of tables. A bit of a sledgehammer to crack a nut approach, but under the circumstances and the local urgency to get this done and out, an advisable one with the caveat that there might be some impact to blindly adding indexes left, right and centre.

In terms of SQL to identify unindexed foreign keys, I was lazy and did a search on google, albeit with this caveat in mind. Interestingly I found a script on experts-exchange that I wrote to do exactly the same thing nearly a decade ago. But it was a rubbish script, and I used the one linked to from Tom Kyte’s page.

Just as a conclusion on this topic, note some points that Tom Kyte makes on how things change, the subject matter of which is exactly this problem. Also, I managed to deadlock myself in a single session last week by using an autonomous transaction to update a table, the child of which had been modified by the outer transaction – not clever.

Categories: deadlock · oracle