{"id":893,"date":"2005-12-19T12:00:00","date_gmt":"2005-12-19T12:00:00","guid":{"rendered":"http:\/\/orcldoug.com\/blog\/?p=893"},"modified":"2005-12-19T12:00:00","modified_gmt":"2005-12-19T12:00:00","slug":"another-10046-success","status":"publish","type":"post","link":"http:\/\/orcldoug.com\/blog\/2005\/12\/19\/another-10046-success\/","title":{"rendered":"Another 10046 Success"},"content":{"rendered":"<p>We&#8217;re implementing a new packaged application at work. It includes a history import job that takes data in a flat-file and loads it into database tables. It performs some degree of data transformation but, in essence it inserts about 500,000 rows into one table and thousands in to a few others. During the testing, the job took 5 hours to run &#8211; slower than you&#8217;d expect for what appears to be a simple job, but acceptable to the users who had just got used to it.<\/p>\n<p>However, when it was executed against the new Production database, it was stopped when it was still running after 28 hours. Actually, it was only really after the job was stopped that I was dragged in to have a look. What the software house wanted to look at was what was &#8216;wrong&#8217; with the Production database (which I&#8217;d created!). I was asked to send a list of parameters for both databases so that they could be compared, check that the optimiser statistics were appropriate (because we had run into problems with out of date statistics during testing) and generally provide information to establish why it was different. There were no obvious differences.<\/p>\n<p>We ran the job again and took some Statspack snapshots and the over-riding view I was getting was that the database server was doing virtually nothing.<\/p>\n<p>Having helped out with this as far as seemed sensible, I suggested that the only way to get to the bottom of this problem was to trace the job. People always seem to view this as a bit of a &#8216;geeky&#8217; step that requires a lot of technical work and analysis, but it&#8217;s better than just flailing around guessing at what might be happening.<\/p>\n<p>Fortunately we were able to run the job against a subset of the data which resulted in a 20 minute execution time. We couldn&#8217;t wait 28 hours for each test! Getting the trace file proved to be a lot more difficult than I&#8217;d hoped because this application implements it&#8217;s own connection pooling so not only was it difficult to find the correct session, but to filter out irrelevant information. Eventually I managed to get a trace file and, to keep this post as short as possible, I&#8217;ll just show the timed summary at the end of the formatted output of tkprof.<\/p>\n<pre>********************************************************************************<br\/><p><\/p><br\/>OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS<br\/><p><\/p><br\/>call     count       cpu    elapsed       disk      query    current        rows<br\/>------- ------  -------- ---------- ---------- ---------- ----------  ----------<br\/>Parse    24544      1.41       1.34          0          0          0           0<br\/>Execute  12332      4.63       4.09        182      17480     158580       12213<br\/>Fetch      226      0.03       0.04          0       3840          0        1817<br\/>------- ------  -------- ---------- ---------- ---------- ----------  ----------<br\/>total    37102      6.07       5.48        182      21320     158580       14030<br\/><p><\/p><br\/>Misses in library cache during parse: 0<br\/><p><\/p><br\/>Elapsed times include waiting on following events:<br\/>Event waited on                             Times   Max. Wait  Total Waited<br\/>----------------------------------------   Waited  ----------  ------------<br\/>SQL*Net message to client                   24966        0.00          0.04<br\/>SQL*Net message from client                 24966       35.26       1065.79<br\/>db file sequential read                       182        0.00          0.02<br\/>log file sync                                   9        0.03          0.14<br\/>SQL*Net more data to client                    24        0.00          0.00<\/pre>\n<p>There are a few things to note here. One, there are more parse operations than executions, which seems wrong. Why would you parse a statement and then not execute it? The fact is that this application does. See <a href=\"http:\/\/tinyurl.com\/8e65s\">this asktom link<\/a> for more info on why this might be and for a hint of the CPU nightmare that might be in store for us when we have more users!<\/p>\n<p>Here&#8217;s some more evidence of this activity from the trace file. (Believe me, this is representative of many other statements.)<\/p>\n<pre>call     count       cpu    elapsed       disk      query    current        rows<br\/>------- ------  -------- ---------- ---------- ---------- ----------  ----------<br\/>Parse        7      0.00       0.00          0          0          0           0<br\/>Execute      0      0.00       0.00          0          0          0           0<br\/>Fetch        0      0.00       0.00          0          0          0           0<br\/>------- ------  -------- ---------- ---------- ---------- ----------  ----------<br\/>total        7      0.00       0.00          0          0          0           0<\/pre>\n<p>Second, look at how little logical and physical i\/o this session has performed. In 20 minutes?!<\/p>\n<p>Looking at the timed events there&#8217;s hardly any time to speak of apart from the &#8220;SQL*Net message from client&#8221; idle event. Then again, when you look at that total &#8211; 1065 seconds &#8211; that&#8217;s about 17 minutes and 45 seconds. Mmmm, in a (roughly) 20 minute job? Then it hit me what the difference between test and production was. The test database and application server are at the same site whereas the production database and app server are at different sites, communicating over a wide area network. When this was planned, we raised a concern about it but were told that there<br \/>\n&#8216;s no need to worry because it&#8217;s just a pilot for a small number of users. However, this job is making 24,000+ trips over the network in 20 minutes, so maybe the performance of those trips is a factor?<\/p>\n<p>Looking at the SQL in the trace file, there was a lot of this kind of thing.<\/p>\n<pre><br\/>INSERT INTO TABLE1 (Col1,Col2,Col3,Col4,Col5,<br\/>Col6,Col7,Col8,Col9,Col10,Col11,Col12,<br\/>Col13)<br\/>VALUES<br\/>(:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13);<\/pre>\n<p>It uses bind variables, so that&#8217;s good. However it&#8217;s still executing this statement once for every row that&#8217;s inserted &#8211; thousands of times in 20 minutes. There are no bulk operations so the application chatters away to the database. Essentially, the database is spending most of it&#8217;s time waiting for the next insert to arrive. Not good. Will the application be re-written to do this sensibly, perhaps with a dedicated PL\/SQL package that handles the activity in the right place &#8211; on the server? Not likely. Certainly not in time to meet our deadlines.<\/p>\n<p>Even having shown this, people remained unconvinced because they aren&#8217;t necessarily used to looking at trace files. I proposed that we perform the tracing on several different environments to see if we could narrow down the differences. Here are the results from the Test environment<\/p>\n<pre>Elapsed times include waiting on following events:<br\/>Event waited on                             Times   Max. Wait  Total Waited<br\/>----------------------------------------   Waited  ----------  ------------<br\/>SQL*Net message to client                   20573        0.00          0.02<br\/>SQL*Net message from client                 20573       20.70         66.12<br\/>db file sequential read                       189        0.01          0.51<br\/>log file sync                                   6        0.02          0.08<\/pre>\n<p>And from Production<\/p>\n<pre>Elapsed times include waiting on following events:<br\/>Event waited on                             Times   Max. Wait  Total Waited<br\/>----------------------------------------   Waited  ----------  ------------<br\/>SQL*Net message to client                   26533        0.00          0.04<br\/>SQL*Net message from client                 26532       19.79       2137.80<br\/>log file sync                                  33        0.03          0.61<br\/>db file sequential read                        79        0.00          0.00<br\/>SQL*Net more data to client                    24        0.00          0.00<br\/>SQL*Net more data from client                   1        0.00          0.00<\/pre>\n<p>Note that, because of the shared activity on individual sessions, the numbers don&#8217;t match up exactly, but it&#8217;s hopefully pretty obvious where the time is being lost. I asked the software house to run the same test in their lab and hoped to see their trace file. Instead they asked me to flush the shared pool, run the job and send them the results of a query against the v$sql. I think they were hoping to see higher logical reads for one or more of the statements to prove that they were doing something different. They weren&#8217;t and they now accept that the time lost on the network is the problem here. Actually, we don&#8217;t know it&#8217;s the network really, it could be a slow app server, but we know that there&#8217;s a reason the database appears to be dead quiet &#8211; it spends most of it&#8217;s time waiting for something to do!<\/p>\n<p>This has taken a bit of work &#8211; maybe about 2 weeks &#8211; but a lot of that was debate and perhaps only a day of tracing and proper analysis. It was well worth the small amount of effort but you need to be determined to stop, take stock and approach the problem properly. This can be a little difficult when the emails are flying around, suggesting six different directions to go in. The end results are<\/p>\n<ul>\n<li>We hope the performance will be better when the database and app servers are co-located<\/li>\n<li>We can pressurise the software house to rewrite this bit of the application to use bulk operations<\/li>\n<li>We have a clear idea of where the problem is and can stop wasting time investigating irrelevant areas of the server configuration<\/li>\n<\/ul>\n<p> As for this &#8216;idle&#8217; event, there&#8217;s a presentation <a href=\"http:\/\/www.oaktable.net\/getFile\/116\">here<\/a> on the subject. I wish I had attended it now &#128521; At least I was able to send the link to the software house to add some weight to my argument (as I did the earlier Tom Kyte link)<\/p>\n<p>To wrap up, the best book on this subject is <a href=\"http:\/\/tinyurl.com\/a3qmx\">this<\/a><\/p>\n<p>Oh, sorry, I almost forgot. Sorry to disagree with <a href=\"http:\/\/wedonotuse.blogspot.com\/2005\/12\/what-good-is-statspack-really.html\">Mogens<\/a>, but I found Statspack really useful when working on this problem too. Maybe I&#8217;ll write about that another time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&#8217;re implementing a new packaged application at work. It includes a history import job that takes data in a flat-file and loads it into database tables. It performs some degree of data transformation but, in essence it inserts about 500,000 rows into one table and thousands in to a few others. During the testing, the&hellip; <a class=\"more-link\" href=\"http:\/\/orcldoug.com\/blog\/2005\/12\/19\/another-10046-success\/\">Continue reading <span class=\"screen-reader-text\">Another 10046 Success<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-893","post","type-post","status-publish","format-standard","hentry","category-uncategorized","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":868,"url":"http:\/\/orcldoug.com\/blog\/2006\/02\/05\/we-never-learn-do-we\/","url_meta":{"origin":893,"position":0},"title":"We Never Learn, Do We?","date":"February 5, 2006","format":false,"excerpt":"I had an interesting day at work yesterday. We've run into major performance problems when upgrading a number of Production databases from 8i to 9i. How could that happen, when we've performed regression tests to ensure that the applications perform as well as or better than they did on 8i?Probably\u2026","rel":"","context":"With 5 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1306,"url":"http:\/\/orcldoug.com\/blog\/2007\/08\/07\/full-volume-database-refresh-part-3\/","url_meta":{"origin":893,"position":1},"title":"Full Volume Database Refresh &#8211; Part 3","date":"August 7, 2007","format":false,"excerpt":"After solving the jigsaw puzzle and waiting for tape drives to do their thing, we've finally got a running test instance accessing a full copy of production. (Well, there was some recovery, faffing around with database names, control files, tempfiles, putting the database in noarchivelog mode and the rest, but\u2026","rel":"","context":"With 10 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1304,"url":"http:\/\/orcldoug.com\/blog\/2007\/08\/04\/full-volume-database-refresh-part-1\/","url_meta":{"origin":893,"position":2},"title":"Full Volume Database Refresh &#8211; Part 1","date":"August 4, 2007","format":false,"excerpt":"(... or the Refresh from Hell) This weekend will be the final practice run (there's been one already) for a database refresh procedure I'll be working on for a further four weekends. It's a full copy of one of our production databases that forms part of a user acceptance test\u2026","rel":"","context":"With 17 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1044,"url":"http:\/\/orcldoug.com\/blog\/2006\/08\/04\/tracing-session-activity-over-a-remote-database-link\/","url_meta":{"origin":893,"position":3},"title":"Tracing session activity over a remote database link","date":"August 4, 2006","format":false,"excerpt":"Yesterday someone asked me how to trace a session that selects from a view in a remote database via a link. If they activated the trace on the local instance, they wouldn't see the bulk of the work which was happening on the remote instance - just a bunch of\u2026","rel":"","context":"With 2 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1191,"url":"http:\/\/orcldoug.com\/blog\/2007\/01\/27\/dba-documentation-catalogue\/","url_meta":{"origin":893,"position":4},"title":"DBA Documentation &#8211; Catalogue","date":"January 27, 2007","format":false,"excerpt":"Prompted by Linda's comment on a previous blog, I thought it might be worth writing a couple of postings on DBA documentation.The first thing you need is a Database Catalogue of some kind.Benefits1) Even an experienced DBA arriving on site won't know what servers exist, how to login to them,\u2026","rel":"","context":"With 11 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1305,"url":"http:\/\/orcldoug.com\/blog\/2007\/08\/06\/full-volume-database-refresh-part-2\/","url_meta":{"origin":893,"position":5},"title":"Full Volume Database Refresh &#8211; Part 2","date":"August 6, 2007","format":false,"excerpt":"First the good news. \"The Refresh from Hell\" (because it was) has become \"The Slightly Tricky Refresh\". It only took 6.5 hours on Sunday (in time to watch Celtic's first match of the season) and apart from a couple of minor panics along the way, it went well. Anyway, back\u2026","rel":"","context":"With 10 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts\/893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/comments?post=893"}],"version-history":[{"count":0,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts\/893\/revisions"}],"wp:attachment":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/media?parent=893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/categories?post=893"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/tags?post=893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}