<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Striving for Optimal PerformanceStriving for Optimal Performance &#187; 10gR2</title>
	<atom:link href="http://www.antognini.ch/category/oracledatabase/10gr2/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.antognini.ch</link>
	<description></description>
	<lastBuildDate>Fri, 18 May 2012 11:35:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>COMMIT_WAIT and COMMIT_LOGGING</title>
		<link>http://www.antognini.ch/2012/04/commit_wait-and-commit_logging/</link>
		<comments>http://www.antognini.ch/2012/04/commit_wait-and-commit_logging/#comments</comments>
		<pubDate>Thu, 05 Apr 2012 06:15:18 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1766</guid>
		<description><![CDATA[Recently I used the COMMIT_WAIT and COMMIT_LOGGING parameters for solving (or, better, working around) a problem I faced while optimizing a specific task for one of my customers. Since it was the first time I used them in a production system, I thought to write this post not only to shortly explain the purpose of [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I used the COMMIT_WAIT and COMMIT_LOGGING parameters for solving (or, better, working around) a problem I faced while optimizing a specific task for one of my customers. Since it was the first time I used them in a production system, I thought to write this post not only to shortly explain the purpose of the these two parameters, but also to show a case where it is sensible to use them.</p>
<p>The purpose of the two parameters is the following:</p>
<p><strong>COMMIT_WAIT</strong></p>
<ul>
<li>Simply put this parameter specifies whether a server process that issues a commit waits for the log writer while it writes the redo data in the redo log files. </li>
<li>If it’s set to WAIT, the default value, the server process waits. And, in most situations, this is the best thing to do. </li>
<li>If it’s set to NOWAIT, it doesn’t wait. This means that, in case of a crash just after a commit, the <a href="http://en.wikipedia.org/wiki/ACID#Durability">D</a> of <a href="http://en.wikipedia.org/wiki/ACID">ACID</a> might be violated! Hence, in general, it is not advised to use this value.</li>
<li>If it’s set to FORCE_WAIT the behaviour is similar to WAIT. The only difference is that settings at a lower level are ignored. In other words, if it is set at the system level it overrides the setting at the session and transaction level. If it’s set at session level it overrides the setting at the transaction level.</li>
</ul>
<p><strong>COMMIT_LOGGING</strong></p>
<ul>
<li>Simply put this parameter specifies whether the log writer writes redo data in batches.</li>
<li>If it’s set to IMMEDIATE, the default value, it basically performs a write operation for each commit.</li>
<li>If it’s set to BATCH, it writes redo data in batches. Since with this value less but larger write operations might be performed, in case of small transactions the log writer should be able to write the redo data in a more efficient way.  </li>
</ul>
<p>Note that in 10.2, the version that introduced these features, there is a single parameter (COMMIT_WRITE) to control them. For example, it is possible to set it to &#8220;NOWAIT, BATCH&#8221;.</p>
<p>To illustrates how these two parameters work I wrote two scripts: <a href="/images/commit.sh">commit.sh</a> and <a href="/images/commit.sql">commit.sql</a>. Their purpose is to show the number of times specific system calls are executed by the log writer and a server process that executes the following PL/SQL block:</p>
<pre>DECLARE
  l_dummy INTEGER;
BEGIN
  FOR i IN 1..1000
  LOOP
    INSERT INTO t VALUES (i, rpad('*',100,'*'));
    COMMIT;
    SELECT count(*) INTO l_dummy FROM dual;
  END LOOP;
END;</pre>
<p>The two system calls I was interested in were the following:</p>
<ul>
<li>semtimedop(2): simply put this one is used by the server process to wait for the log writer</li>
<li>io_submit(2): simply put this one is used by the log writer to write redo data in the redo log files</li>
</ul>
<p>Let’s have a look the output generated by the scripts for three particular cases:</p>
<ul>
<li><strong>COMMIT_WAIT = WAIT and COMMIT_LOGGING = IMMEDIATE</strong>: Notice that the server process executes 1005 times semtimedop(2) and that the log writer executes 1016 times io_submit(2). In other words, for both of them the number of executions is approximately the number of commits performed by the PL/SQL block.</li>
</ul>
<pre>   oracle@helicon:~/commit/ [DBM11203] ./commit.sh chris ian wait immediate 2> /dev/null

   ***** Server Process *****

   % time     seconds  usecs/call     calls    errors syscall
   ------ ----------- ----------- --------- --------- ----------------
   100.00    0.069561          69      1005         1 semtimedop
   ------ ----------- ----------- --------- --------- ----------------
   100.00    0.069561                  1005         1 total

   ***** Log Writer *****

   % time     seconds  usecs/call     calls    errors syscall
   ------ ----------- ----------- --------- --------- ----------------
   100.00    0.013919          14      1016           io_submit
   ------ ----------- ----------- --------- --------- ----------------
   100.00    0.013919                  1016           total</pre>
<ul>
<li><strong>COMMIT_WAIT = NOWAIT and COMMIT_LOGGING = IMMEDIATE</strong>: Notice that, for the server process, the executions of semtimedop(2) dropped to 5. No noticeable difference is observable for the log writer.</li>
</ul>
<pre>oracle@helicon:~/commit/ [DBM11203] ./commit.sh chris ian nowait immediate 2> /dev/null

  ***** Server Process *****

  % time     seconds  usecs/call     calls    errors syscall
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.002195         439         5         1 semtimedop
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.002195                     5         1 total

  ***** Log Writer *****

  % time     seconds  usecs/call     calls    errors syscall
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.010073          10      1015           io_submit
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.010073                  1015           total</pre>
<ul>
<li><strong>COMMIT_WAIT = NOWAIT and COMMIT_LOGGING = BATCH</strong>: Notice that this time not only the number of executions to io_submit(2) dropped to 15, but, in total, much less time was spent for writing the redo data (10*1015 >> 36*15).
</li>
</ul>
<pre>   oracle@helicon:~/commit/ [DBM11203] ./commit.sh chris ian nowait batch 2> /dev/null

  ***** Server Process *****

  % time     seconds  usecs/call     calls    errors syscall
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.002132         533         4         1 semtimedop
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.002132                     4         1 total

  ***** Log Writer *****

  % time     seconds  usecs/call     calls    errors syscall
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.000533          36        15           io_submit
  ------ ----------- ----------- --------- --------- ----------------
  100.00    0.000533                    15           total</pre>
<p>Now that I explained what the purpose of these parameters is, let me describe a case where I successfully used them.</p>
<p>Few weeks ago one of my customers migrated its DMS to a new version. During the migration data had to be moved from one database to another. Unfortunately the migration code was written to process data <a href="http://tkyte.blogspot.com/2006/10/slow-by-slow.html">slow by slow</a>. Hence, to speed up the processing, parallelization was added to the picture.</p>
<p>The following chart (you can click on it to increase its size) shows the load generated by 20 parallel processes with default values for COMMIT_WAIT and COMMIT_LOGGING. With the default configutation the system was able to process about 1000 “objects” per second. As you can see there were a lot of waits related to the commit wait class.</p>
<p><a href="/images/20120404-img1.jpg" rel="lightbox[em]" title="COMMIT_WAIT and COMMIT_LOGGING"><img id="exeplan" src="/images/20120404-img1.jpg" alt="Database load (20 parallel processes; COMMIT_WAIT WAIT; COMMIT_LOGGING = IMMEDIATE)" width="550px" /></a></p>
<p>Since this was a controlled processing using COMMIT_WAIT was not considered a problem. So, we tested the very same load with COMMIT_WAIT set to NOWAIT and COMMIT_LOGGING set to BATCH. The throughput increased to about 1200 “object” per second. In other words, not dramatically. But, more importantly, as the following chart shows almost all waits in the commit wait class disappeared. This was very important because without that serialization taking place we were able to increase the number of parallel processes.</p>
<p><a href="/images/20120404-img2.jpg" rel="lightbox[em]" title="COMMIT_WAIT and COMMIT_LOGGING"><img id="exeplan" src="/images/20120404-img2.jpg" alt="Database load (20 parallel processes; COMMIT_WAIT NOWAIT; COMMIT_LOGGING = BATCH)" width="550px" /></a></p>
<p>The following chart shows the load generated by 50 parallel processes. Again, almost no wait related to commits. With 50 processes the system was able to process about 2300 “objects” per second.</p>
<p><a href="/images/20120404-img3.jpg" rel="lightbox[em]" title="COMMIT_WAIT and COMMIT_LOGGING"><img id="exeplan" src="/images/20120404-img3.jpg" alt="Database load (50 parallel processes; COMMIT_WAIT NOWAIT; COMMIT_LOGGING = BATCH)" width="550px" /></a></p>
<p>In summary, COMMIT_WAIT and COMMIT_LOGGING are not commonly used parameters but, in some specific situations, using them it might be beneficial to avoid wait events related to commits.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2012/04/commit_wait-and-commit_logging/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Analysing Row Lock Contention with LogMiner</title>
		<link>http://www.antognini.ch/2012/03/analysing-row-lock-contention-with-logminer/</link>
		<comments>http://www.antognini.ch/2012/03/analysing-row-lock-contention-with-logminer/#comments</comments>
		<pubDate>Mon, 12 Mar 2012 06:53:00 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[LogMiner]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1756</guid>
		<description><![CDATA[Recently I had to analyse a row lock contention problem that can be illustrated by the following test case: A session (let’s call it #1) creates a table and inserts a row into it (note that “n” is the primary key of the table): SQL> CREATE TABLE t (n NUMBER PRIMARY KEY); SQL> VARIABLE n [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I had to analyse a row lock contention problem that can be illustrated by the following test case:</p>
<ul>
<li>A session (let’s call it #1) creates a table and inserts a row into it (note that “n” is the primary key of the table):</li>
</ul>
<pre>SQL> CREATE TABLE t (n NUMBER PRIMARY KEY);

SQL> VARIABLE n NUMBER

SQL> execute :n := 1

SQL> INSERT INTO t VALUES (:n);</pre>
<ul>
<li>Another session (let’s call it #2) inserts the same data into the same table:</li>
</ul>
<pre>SQL> VARIABLE n NUMBER

SQL> execute :n := 1

SQL> INSERT INTO t VALUES (:n);</pre>
<ul>
<li>Since session #1 did not commit and that session #2 inserted a row with the same primary key, session #2 is blocked waiting for session #1 to either commit or rollback:</li>
</ul>
<pre>SQL> SELECT sid, blocking_session, event, sql_text
  2  FROM v$session LEFT OUTER JOIN v$sqlarea USING (sql_id)
  3  WHERE nvl(blocking_session,sid) IN (SELECT holding_session FROM dba_blockers);

       SID BLOCKING_SESSION EVENT                          SQL_TEXT
---------- ---------------- ------------------------------ ------------------------------
       130                  SQL*Net message from client
       197              130 enq: TX - row lock contention  INSERT INTO t VALUES (:n)</pre>
<p>It goes without saying that in such a case the problem is the application that tried to insert two rows with the same primary key. In the real case the primary key was a natural key, not a surrogate key generated through a sequence. To help the developers troubleshoot the problem it was therefore necessary to know the actual value of the bind variable used for the two inserts. Unfortunately this information is stored in the PGA of the server processes and, therefore, it is not directly accessible. In addition I started investigating the problem only a couple of hours after the contention began and, therefore, also a view like V$SQL_BIND_CAPTURE is of no use.</p>
<p>As a result I decided to mine the archived redo logs to find the information I was looking for. Here is what I did:</p>
<ul>
<li>Find out the XID of the transaction holding the row lock:</li>
</ul>
<pre>SQL> SELECT t.xidusn, t.xidslot, t.xidsqn, t.start_time, t.start_scn
  2  FROM v$transaction t JOIN v$session s ON t.addr = s.taddr
  3  WHERE s.sid = 130;

    XIDUSN    XIDSLOT     XIDSQN START_TIME            START_SCN
---------- ---------- ---------- -------------------- ----------
         7         12       1049 03/09/12 07:10:25       1462388</pre>
<ul>
<li>Find out which archived redo log contains the first redo records of that transaction:</li>
</ul>
<pre>SQL> SELECT name
  2  FROM v$archived_log
  3  WHERE 1462388 BETWEEN first_change# AND next_change# - 1;

NAME
-----------------------------------------------------------------------------------------------
/u00/app/oracle/fast_recovery_area/DBA112/archivelog/2012_03_09/o1_mf_1_94_7om7qzdx_.arc</pre>
<ul>
<li>Start LogMiner:</li>
</ul>
<pre>SQL> EXECUTE dbms_logmnr.add_logfile(logfilename=>'/u00/app/oracle/fast_recovery_area/DBA112/archivelog/2012_03_09/o1_mf_1_94_7om7qzdx_.arc')

SQL> EXECUTE dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog)</pre>
<ul>
<li>Extract the redo information of the operations performed by the transaction holding the row lock and, therefore, finding that the value of the bind variable was “1”:</li>
</ul>
<pre>SQL> SELECT sql_redo
  2  FROM v$logmnr_contents
  3  WHERE xidusn = 7
  4  AND xidslt = 12
  5  AND xidsqn = 1049;

SQL_REDO
--------------------------------------------------------------------------------
set transaction read write;
insert into "CHRIS"."T"("N") values ('1');</pre>
<ul>
<li>Stop LogMiner</li>
</ul>
<pre>SQL> EXECUTE dbms_logmnr.end_logmnr</pre>
<p>In conclusion, even though mining (archived) redo logs for extracting the value of bind variables is probably not the most typical use of LogMiner, it works well. Therefore, do not forget this possibility if you have to do it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2012/03/analysing-row-lock-contention-with-logminer/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Index Scan with Filter Predicate Based on a Subquery</title>
		<link>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/</link>
		<comments>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 21:36:58 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1705</guid>
		<description><![CDATA[Most execution plans can be interpreted by following few basic rules (in TOP, Chapter 6, I provide such a list of rules). Nevertheless, there are some special cases. One of them is when an index scan, in addition to the access predicate, has a filter predicate applying a subquery. The following execution plan, taken from [...]]]></description>
			<content:encoded><![CDATA[<p>Most execution plans can be interpreted by following few basic rules (in TOP, Chapter 6, I provide such a list of rules). Nevertheless, there are some special cases. One of them is when an index scan, in addition to the access predicate, has a filter predicate applying a subquery.</p>
<p>The following execution plan, taken from Enterprise Manager 11.2, is an example (click on the image to increase its size):<br />
<a href="/images/ep20120206.jpg" rel="lightbox" title="Index Scan with Filter Predicate Based on a Subquery"><img id="exeplan" src="/images/ep20120206.jpg" alt="Execution Plan" width="550px" /></a><br />
Notes:</p>
<ul>
<li>According to the order column the first operation being executed is the scan of the <code>I2</code> index. Unfortunately this is wrong. In fact the first operation being executed is the scan of the <code>I1</code> index. This is a bug in Enterprise Manager. I wanted to show you this example to demonstrate that not only for us it might be difficult to correctly interpret an execution plan <img src='http://www.antognini.ch/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </li>
<li>The filter predicate <code>IS NOT NULL</code> is also wrong. This is not a bug, however. It is a limitation in the current implementation. The problem is that in some cases the <code>V$SQL_PLAN</code> and <code>V$SQL_PLAN_STATISTICS_ALL</code> views are not able to show all the necessary details.</li>
</ul>
<p>Without seeing the query on which this execution plan is based, it is not obvious at all to know what’s going on. So, here is the query:</p>
<pre>SELECT *
FROM t1
WHERE n1 = 8
AND n2 IN (SELECT t2.n1 FROM t2, t3 WHERE t2.id = t3.id AND t3.n1 = <img src='http://www.antognini.ch/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> </pre>
<p>Based on the query it is essential to point out that the access predicate <code>"T2"."N1"=:B1</code> cannot be evaluated and, therefore, the scan of the <code>I2</code> index cannot be carried out, without having a value passed through the B1 bind variable. In other words, without knowing the value of <code>T1.N2</code>.</p>
<p>To describe how this execution plan is carried out, let’s have a look to the information provided by the <code>DBMS_XPLAN.DISPLAY</code> function (which does not expose the limitation related to the filter predicate).</p>
<pre>-----------------------------------------------
| Id  | Operation                      | Name |
-----------------------------------------------
|   0 | SELECT STATEMENT               |      |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T1   |
|*  2 |   INDEX RANGE SCAN             | I1   |
|   3 |    NESTED LOOPS                |      |
|   4 |     TABLE ACCESS BY INDEX ROWID| T2   |
|*  5 |      INDEX RANGE SCAN          | I2   |
|*  6 |     TABLE ACCESS BY INDEX ROWID| T3   |
|*  7 |      INDEX RANGE SCAN          | I3   |
-----------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("N1"=8)
    filter( EXISTS (SELECT /*+ LEADING ("T2" "T3") USE_NL ("T3") INDEX
           ("T3" "I3") INDEX_RS_ASC ("T2" "I2") */ 0 FROM "T3" "T3","T2" "T2" WHERE
           "T2"."N1"=:B1 AND "T3"."N1"=8 AND "T2"."ID"="T3"."ID"))
5 - access("T2"."N1"=:B1)
6 - filter("T2"."ID"="T3"."ID")
7 - access("T3"."N1"=8)</pre>
<p>The operations are carried out as follows:</p>
<ol>
<li>Operation 2 applies the access predicate <code>"N1"=8</code> by scanning the <code>I1</code> index.</li>
<li>For each key returned by the previous scan, the subquery is executed once. Note that the subquery carries out a nested loop. While the outer loop accesses the <code>T2</code> table, the inner loop accesses the <code>T3</code> table. </li>
<li>The first operation of the outer loop is operation 5. It applies the access predicate <code>"T2"."N1"=:B1</code> by scanning the <code>I2</code> index. Based on the rowid returned by the index access the <code>T2</code> table is accessed (operation 4).</li>
<li>For each row returned by the outer loop, the inner loop is executed once. The first operation of the inner loop is operation 7. It applies the access predicate <code>"T3"."N1"=8</code> by scanning the <code>I3</code> index. Based on the rowid returned by the index access the <code>T3</code> table is accessed (operation 6) and the filter predicate <code>"T2"."ID"="T3"."ID"</code> (the join condition) is applied. By the way, it is interesting to notice that, contrary to the join condition is <em>not</em> applied as an access predicate, as it usually happens.</li>
<li>If the subquery returns a row, the rowid returned by operation 2 can be used to access the <code>T1</code> table (operation 1). The row extracted from this operation is sent to the caller.</li>
</ol>
<p>All in all, this is a very special execution plan&#8230;</p>
<p>In summary, be careful when you see an index scan with a filter predicate applying a subquery. The execution plan might not be carried out as you expect at first sight. It is also essential to point out that in such a case the predicate information is essential to fully understand what’s going on.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>SQL Trace and Oracle Portal</title>
		<link>http://www.antognini.ch/2011/11/sql-trace-and-oracle-portal/</link>
		<comments>http://www.antognini.ch/2011/11/sql-trace-and-oracle-portal/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 10:32:34 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1637</guid>
		<description><![CDATA[Recently I was involved in a project where I had to trace the database calls of an application based on Oracle Portal 10.1.4. The basic requirements were the following: Tracing takes place in the production environment Tracing has to be enable for a single user only Instrumentation code cannot be added to the application Given [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I was involved in a project where I had to trace the database calls of an application based on Oracle Portal 10.1.4. The basic requirements were the following:</p>
<ul>
<li>Tracing takes place in the production environment</li>
<li>Tracing has to be enable for a single user only</li>
<li>Instrumentation code cannot be added to the application</li>
</ul>
<p>Given that Oracle Portal uses a pool of connections and that for each HTTP call it can use several database sessions, statically enable SQL trace for specific sessions was not an option.</p>
<p>Knowing nothing about Oracle Portal I started RTFM and, gladly, I discovered that there is a simple way to inject a piece of code before and after a requested procedure is called. This is done by setting, via the administration GUI, the parameters <a href="http://docs.oracle.com/cd/B14099_19/web.1012/b14008/confmods.htm#sthref632">PlsqlBeforeProcedure</a> and <a href="http://docs.oracle.com/cd/B14099_19/web.1012/b14008/confmods.htm#sthref625">PlsqlAfterProcedure</a>.</p>
<p>Since Oracle Portal provides a function (<a href="http://docs.oracle.com/cd/B14099_19/portal.1012/b14134/pdg_pdk_plsql.htm#sthref1442">WWCTX_API.GET_USER</a>) to get the current user, I decided to create the following procedures to set/clear the client identifier before/after every call. Note that I added the call to SUBSTR and the exception handler to make sure that the procedures do not raise exceptions (hey, it’s a production system and I do not want to impact everyone!).</p>
<pre>CREATE PROCEDURE tvd_set_client_identifier AS
BEGIN
  dbms_session.set_identifier(substr(portal.wwctx_api.get_user,1,64));
EXCEPTION
  WHEN others THEN NULL;
END;</pre>
<pre>CREATE PROCEDURE tvd_clear_client_identifier AS
BEGIN
  dbms_session.clear_identifier;
EXCEPTION
  WHEN others THEN NULL;
END;</pre>
<p>To &#8220;enable&#8221; these procedures we did the following:</p>
<ul>
<li>Set PlsqlBeforeProcedure=portal.tvd_set_client_identifier</li>
<li>Set PlsqlAfterProcedure=portal.tvd_clear_client_identifier</li>
<li>Restarted the application server</li>
</ul>
<p>With this configuration in place enabling SQL trace for a single user was easily done by calling the <a href="http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_monitor.htm#i1002256">DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE</a> procedure by specifying the user to be traced as value for the CLIENT_ID parameter.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/11/sql-trace-and-oracle-portal/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>optimizer_secure_view_merging and VPD</title>
		<link>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/</link>
		<comments>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/#comments</comments>
		<pubDate>Sun, 11 Sep 2011 09:21:49 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1552</guid>
		<description><![CDATA[At page 189 of TOP I wrote the following piece of text: In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging could lead to security issues. If this is the case, no view merging will be performed, and performance could be suboptimal as a result. For this [...]]]></description>
			<content:encoded><![CDATA[<p>At page 189 of <a href="/top">TOP</a> I wrote the following piece of text:</p>
<blockquote><p>In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging could lead to security issues. If this is the case, no view merging will be performed, and performance could be suboptimal as a result. For this reason, if you are not using views for security purposes, it is better to set this initialization parameter to FALSE.</p></blockquote>
<p>What I didn’t consider when I wrote it, it is the implication of predicate move-around related to Virtual Private Database (VPD). In fact, as described in the <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/initparams168.htm#I1010262">documentation</a>, that parameter controls <em>view merging</em> as well as <em>predicate move-around</em>.</p>
<p>To point out what the impact is, let’s have a look to an example based on the description provided in <a href="/top">TOP</a>:</p>
<ul>
<li>Say you have a very simple table with one primary key and two more columns.</li>
</ul>
<pre>CREATE TABLE t (
  id NUMBER(10) PRIMARY KEY,
  class NUMBER(10),
  pad VARCHAR2(10)
);</pre>
<ul>
<li>For security reasons, you define the following policy. Notice the filter that is applied with the function to partially show the content of the table. How this function is implemented and what it does exactly is not important.</li>
</ul>
<pre>CREATE OR REPLACE FUNCTION s (schema IN VARCHAR2, tab IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
  RETURN 'f(class) = 1';
END;
/

BEGIN
  dbms_rls.add_policy(object_schema   => 'U1',
                      object_name     => 'T',
                      policy_name     => 'T_SEC',
                      function_schema => 'U1',
                      policy_function => 'S');
END;
/</pre>
<ul>
<li>Now let’s say that a user who has access to the table creates the following PL/SQL function. As you can see, it will just display the value of the input parameters through a call to the package dbms_output.</li>
</ul>
<pre>CREATE OR REPLACE FUNCTION spy (id IN NUMBER, pad IN VARCHAR2) RETURN NUMBER AS
BEGIN
  dbms_output.put_line('id=' || id || ' pad=' || pad);
  RETURN 1;
END;
/</pre>
<ul>
<li>With the initialization parameter optimizer_secure_view_merging set to FALSE, you can run two test queries. Both return only the values that the user is allowed to see. In the second one, however, you are able to see data that you should not be able to access.</li>
</ul>
<pre>SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL

SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5
  4  AND spy(id, pad) = 1;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL
id=1 pad=DrMLTDXxxq
id=2 pad=XOZnqYRJwI
id=3 pad=nlGfGBTxNk
id=4 pad=AszBGEUGEL
id=5 pad=qTSRnFjRGb</pre>
<ul>
<li>With the initialization parameter optimizer_secure_view_merging set to TRUE, the second query returns the following output. As you can see, the function and the query display the same data.</li>
</ul>
<pre>SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5
  4  AND spy(id, pad) = 1;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL
id=1 pad=DrMLTDXxxq
id=4 pad=AszBGEUGEL</pre>
<p>The execution plans that are used in the two situations are the following. As you can see only the second one guarantee that the policy defined via VPD is applied before the predicate based on the SPY function. Interestingly enough the other predicate based on the ID column is applied before the one of the policy. Hence, the query optimizer can choose an access path that takes advantage of the primary key.</p>
<ul>
<li>optimizer_secure_view_merging = FALSE</li>
</ul>
<pre>---------------------------------------------------
| Id  | Operation                   | Name        |
---------------------------------------------------
|   0 | SELECT STATEMENT            |             |
|*  1 |  TABLE ACCESS BY INDEX ROWID| T           |
|*  2 |   INDEX RANGE SCAN          | SYS_C009970 |
---------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(("SPY"("ID","PAD")=1 AND "F"("CLASS")=1))
   2 - access("ID">=1 AND "ID"<=5)</pre>
<ul>
<li>optimizer_secure_view_merging = TRUE</li>
</ul>
<pre>----------------------------------------------------
| Id  | Operation                    | Name        |
----------------------------------------------------
|   0 | SELECT STATEMENT             |             |
|*  1 |  VIEW                        | T           |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T           |
|*  3 |    INDEX RANGE SCAN          | SYS_C009971 |
----------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("SPY"("ID","PAD")=1)
   2 - filter("F"("CLASS")=1)
   3 - access("ID">=1 AND "ID"<=5)</pre>
<p>Based on these observations, the summary that is provided by <a href="/top">TOP</a> at page 189 should be amended as follows:</p>
<blockquote><p>In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging or predicate move-around could lead to security issues. If this is the case, they will not be performed, and performance could be suboptimal as a result. For this reason, if you are not using views or VPD for security purposes, it is better to set this initialization parameter to FALSE.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>ITL Waits – Changes in Recent Releases (script)</title>
		<link>http://www.antognini.ch/2011/06/itl-waits-changes-in-recent-releases-script/</link>
		<comments>http://www.antognini.ch/2011/06/itl-waits-changes-in-recent-releases-script/#comments</comments>
		<pubDate>Mon, 20 Jun 2011 10:20:02 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[9iR2]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1533</guid>
		<description><![CDATA[A reader of this blog, Paresh, asked me how I was able to find out the logic behind ITL waits without having access to Oracle code. My reply was: I wrote a test case that reproduce ITL waits and a piece of code that monitors them. Since other readers might be interested, here is the [...]]]></description>
			<content:encoded><![CDATA[<p>A reader of this blog, Paresh, <a href="/2011/04/itl-waits-changes-in-recent-releases/#comment-27702">asked me</a>  how I was able to find out the logic behind <a href="/2011/04/itl-waits-changes-in-recent-releases">ITL waits</a> without having access to Oracle code. My reply was: I wrote a test case that reproduce ITL waits and a piece of code that monitors them.</p>
<p>Since other readers might be interested, here is the shell script I wrote. Notice that it takes four parameters as input: user name, password, SID, and how long it has to wait in the monitoring phase.</p>
<pre>#!/bin/sh

user=$1
password=$2
sid=$3
wait=$4

#
# Setup test environment
#

sqlplus -s $user/$password@$sid <<end
  SET ECHO OFF TERMOUT ON FEEDBACK OFF HEADING OFF
  BEGIN
    EXECUTE IMMEDIATE 'DROP TABLE t PURGE';
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;
  /
  CREATE TABLE t (n NUMBER, pad VARCHAR2(50)) PCTFREE 0 INITRANS 5 TABLESPACE users;
  INSERT INTO t SELECT rownum, rpad('*',50,'*') FROM dual CONNECT BY level <= 134 UNION ALL SELECT 135, rpad('*',48,'*') FROM dual;
  COMMIT;
  SELECT 'Setup correctly performed:', decode(count(DISTINCT dbms_rowid.rowid_block_number(rowid)),1,'YES','NO') FROM t;
END

#
# Produce ITL wait
#

for i in 1 2 3 4 5 6
do
  echo 'SET ECHO OFF TERMOUT OFF FEEDBACK OFF' > $i.$sid.sql
  if [[ $i = 6 ]]
  then
    # make sure that the other processes have locked one row
    echo 'execute dbms_lock.sleep(1)' >> $i.$sid.sql
  fi
  echo 'UPDATE t SET n = n WHERE n =' $i ';' >> $i.$sid.sql
  echo 'SET TERMOUT OFF' >> $i.$sid.sql
  if [[ $i < 6 ]]
  then
    echo 'execute dbms_lock.sleep(' $wait ')' >> $i.$sid.sql
  fi
  sqlplus -s $user/$password@$sid @$i.$sid.sql &#038;
done

#
# Monitor ITL wait
#

sqlplus -s $user/$password@$sid <<end
  SET SERVEROUTPUT ON ECHO OFF TERMOUT ON FEEDBACK OFF HEADING OFF
  SELECT * FROM v$version WHERE rownum = 1;
  DECLARE
    l_waiter_session v$session.sid%TYPE := NULL;
    l_blocking_session_curr v$session.blocking_session%TYPE := NULL;
    l_blocking_session_prev v$session.blocking_session%TYPE := NULL;
    l_seconds_in_wait_curr v$session.seconds_in_wait%TYPE := NULL;
    l_seconds_in_wait_prev v$session.seconds_in_wait%TYPE := NULL;
    c_sleep CONSTANT NUMBER := 0.1;
    c_iterations CONSTANT NUMBER := ceil(($wait-5)/c_sleep);
  BEGIN
    WHILE l_waiter_session IS NULL
    LOOP
      BEGIN
        SELECT sid INTO l_waiter_session
        FROM v$session
        WHERE event = 'enq: TX - allocate ITL entry';
      EXCEPTION
        WHEN no_data_found THEN NULL;
      END;
    END LOOP;
    FOR i IN 1..c_iterations
    LOOP
      BEGIN
        SELECT blocking_session, seconds_in_wait INTO l_blocking_session_curr, l_seconds_in_wait_curr
        FROM v$session
        WHERE sid = l_waiter_session;
      EXCEPTION
        WHEN no_data_found THEN NULL;
      END;
      IF l_blocking_session_curr <> l_blocking_session_prev
         OR l_blocking_session_prev IS NULL
         OR i = c_iterations
      THEN
        dbms_output.put_line(to_char((i-1)*c_sleep,'000000')||
                             ' blocking_session='||nvl(l_blocking_session_prev,l_blocking_session_curr)||
                             ' sleep='||nvl(l_seconds_in_wait_prev,l_seconds_in_wait_curr));
      END IF;
      l_blocking_session_prev := l_blocking_session_curr;
      l_seconds_in_wait_prev := l_seconds_in_wait_curr;
      dbms_lock.sleep(c_sleep);
    END LOOP;
  END;
  /
END

#
# Cleanup
#

for i in 1 2 3 4 5 6
do
  rm $i.$sid.sql
done

sleep 5

sqlplus -s $user/$password@$sid <<end
  SET ECHO OFF TERMOUT ON FEEDBACK OFF HEADING OFF
  DROP TABLE t PURGE;
END

exit 0
</pre>
<p>The outputs I got are the following:</p>
<ul>
<li>10.2.0.4</li>
</ul>
<pre>000000 blocking_session=136 sleep=0
000005 blocking_session=136 sleep=6
000010 blocking_session=140 sleep=3
000015 blocking_session=152 sleep=6
000020 blocking_session=159 sleep=6
029995 blocking_session=158 sleep=29979</pre>
<ul>
<li>10.2.0.5</li>
</ul>
<pre>000000 blocking_session=158 sleep=0
000001 blocking_session=158 sleep=0
000002 blocking_session=152 sleep=2
000003 blocking_session=141 sleep=0
000004 blocking_session=148 sleep=0
000005 blocking_session=140 sleep=3
000007 blocking_session=158 sleep=0
000009 blocking_session=152 sleep=3
000011 blocking_session=141 sleep=3
000013 blocking_session=148 sleep=0
000015 blocking_session=140 sleep=3
000019 blocking_session=158 sleep=3
000023 blocking_session=152 sleep=6
000027 blocking_session=141 sleep=3
000031 blocking_session=148 sleep=3
000035 blocking_session=140 sleep=6
000040 blocking_session=158 sleep=3
000045 blocking_session=152 sleep=6
000050 blocking_session=141 sleep=6
000054 blocking_session=148 sleep=3
000062 blocking_session=140 sleep=9
000067 blocking_session=158 sleep=6
000072 blocking_session=152 sleep=3
000077 blocking_session=141 sleep=6
000082 blocking_session=148 sleep=6
000098 blocking_session=140 sleep=15
000103 blocking_session=158 sleep=6
000108 blocking_session=152 sleep=3
000113 blocking_session=141 sleep=6
000118 blocking_session=148 sleep=6
000149 blocking_session=140 sleep=30
000154 blocking_session=158 sleep=6
000159 blocking_session=152 sleep=6
000164 blocking_session=141 sleep=3
000169 blocking_session=148 sleep=6
000232 blocking_session=140 sleep=63
000237 blocking_session=158 sleep=6
000242 blocking_session=152 sleep=6
000247 blocking_session=141 sleep=3
000252 blocking_session=148 sleep=6
000379 blocking_session=140 sleep=129
000383 blocking_session=158 sleep=3
000388 blocking_session=152 sleep=6
000393 blocking_session=141 sleep=6
000398 blocking_session=148 sleep=3
000651 blocking_session=140 sleep=258
000656 blocking_session=158 sleep=3
000661 blocking_session=152 sleep=6
000666 blocking_session=141 sleep=6
000671 blocking_session=148 sleep=3
001177 blocking_session=140 sleep=514
001182 blocking_session=158 sleep=6
001187 blocking_session=152 sleep=3
001192 blocking_session=141 sleep=6
001197 blocking_session=148 sleep=6
014218 blocking_session=140 sleep=13184
029995 blocking_session=140 sleep=28788</pre>
<ul>
<li>11.1.0.6</li>
</ul>
<pre>000000 blocking_session=146 sleep=0
000005 blocking_session=146 sleep=5
000010 blocking_session=129 sleep=5
000015 blocking_session=141 sleep=5
000020 blocking_session=126 sleep=5
029995 blocking_session=132 sleep=29978</pre>
<ul>
<li>11.1.0.7</li>
</ul>
<pre>000000 blocking_session=136 sleep=0
000005 blocking_session=136 sleep=5
000010 blocking_session=140 sleep=5
000015 blocking_session=132 sleep=5
000020 blocking_session=131 sleep=5
029995 blocking_session=134 sleep=29979</pre>
<ul>
<li>11.2.0.1</li>
</ul>
<pre>000000 blocking_session=131 sleep=0
000001 blocking_session=131 sleep=1
000002 blocking_session=133 sleep=1
000003 blocking_session=196 sleep=1
000004 blocking_session=67 sleep=1
000005 blocking_session=69 sleep=1
000007 blocking_session=131 sleep=2
000009 blocking_session=133 sleep=2
000011 blocking_session=196 sleep=2
000013 blocking_session=67 sleep=2
000015 blocking_session=69 sleep=2
000019 blocking_session=131 sleep=4
000023 blocking_session=133 sleep=4
000027 blocking_session=196 sleep=4
000031 blocking_session=67 sleep=4
000035 blocking_session=69 sleep=4
000040 blocking_session=131 sleep=5
000045 blocking_session=133 sleep=5
000050 blocking_session=196 sleep=5
000054 blocking_session=67 sleep=5
000062 blocking_session=69 sleep=8
000067 blocking_session=131 sleep=5
000072 blocking_session=133 sleep=5
000077 blocking_session=196 sleep=5
000082 blocking_session=67 sleep=5
000098 blocking_session=69 sleep=16
000103 blocking_session=131 sleep=5
000108 blocking_session=133 sleep=5
000113 blocking_session=196 sleep=5
000118 blocking_session=67 sleep=5
000149 blocking_session=69 sleep=32
000154 blocking_session=131 sleep=5
000159 blocking_session=133 sleep=5
000164 blocking_session=196 sleep=5
000169 blocking_session=67 sleep=5
000232 blocking_session=69 sleep=64
000237 blocking_session=131 sleep=5
000242 blocking_session=133 sleep=5
000247 blocking_session=196 sleep=5
000252 blocking_session=67 sleep=5
000379 blocking_session=69 sleep=128
000383 blocking_session=131 sleep=5
000388 blocking_session=133 sleep=5
000393 blocking_session=196 sleep=5
000398 blocking_session=67 sleep=5
000651 blocking_session=69 sleep=256
000656 blocking_session=131 sleep=5
000661 blocking_session=133 sleep=5
000666 blocking_session=196 sleep=5
000671 blocking_session=67 sleep=5
001177 blocking_session=69 sleep=512
001182 blocking_session=131 sleep=5
001187 blocking_session=133 sleep=5
001192 blocking_session=196 sleep=5
001196 blocking_session=67 sleep=5
029995 blocking_session=69 sleep=28787</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/06/itl-waits-changes-in-recent-releases-script/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ITL Waits &#8211; Changes in Recent Releases</title>
		<link>http://www.antognini.ch/2011/04/itl-waits-changes-in-recent-releases/</link>
		<comments>http://www.antognini.ch/2011/04/itl-waits-changes-in-recent-releases/#comments</comments>
		<pubDate>Wed, 13 Apr 2011 11:43:23 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[9iR2]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1512</guid>
		<description><![CDATA[In recent releases Oracle has silently changed the behavior of ITL waits. The aim of this post it to describe what has changed and why. But, first of all, let’s review some essential concepts about ITLs and ITL waits. Interested Transaction List The Oracle database engine locks the data modified by a transaction at the [...]]]></description>
			<content:encoded><![CDATA[<p>In recent releases Oracle has silently changed the behavior of ITL waits. The aim of this post it to describe what has changed and why. But, first of all, let’s review some essential concepts about ITLs and ITL waits.</p>
<p><strong>Interested Transaction List </strong></p>
<p>The Oracle database engine locks the data modified by a transaction at the row level. To implement this feature every data block contains a list of all transactions that are modifying it. This list is commonly called <em>interested transaction list</em> (ITL). Its purpose is twofold. First, it is used to store information to identify a transaction as well as a reference to access the undo data associated to it. Second, it is referenced by every modified or locked row to indicate which transaction it is involved.</p>
<p><strong>INITRANS</strong></p>
<p>The initial number of slots composing the ITL is set through the INITRANS parameter. Even though it can be set to 1, which is the default value as well, as of 9i at least 2 slots are always created. Note that the data dictionary lies to us on this matter. In fact, as shown in the following example, the data dictionary shows the value specified when the object was created and not the actual number of slots.</p>
<pre>SQL> CREATE TABLE t (n NUMBER) INITRANS 1;

SQL> SELECT ini_trans FROM user_tables WHERE table_name = 'T';

 INI_TRANS
----------
         1</pre>
<p><strong>MAXTRANS</strong></p>
<p>There is a maximum number of slots an ITL can contain. The actual maximum number depends on the blocks size. For example, an 8KB block can have up to 169 slots. Up to 9i the maximum is limited by the MAXTRANS parameter as well. As of 10g, however, this parameter is deprecated and, therefore, no longer honored. In the same way as for INITRANS, the data dictionary shows the value specified when the object was created and not the actual maximum number of slots.<br />
Also note that while creating an object the database engine checks whether the MAXTRANS value is not greater than 255. And, if it is greater, it raises an ORA-02209 (invalid MAXTRANS option value).</p>
<p><strong>ITL Waits</strong></p>
<p>When a session requires a slot but all the available ones are in use by other active transactions, the database engine tries to dynamically create a new slot. This is of course only possible when a) the maximum number of slots was not already allocated b) enough free space (one slot occupies 24 bytes) is available in the block itself. If a new slot cannot be created, the session requiring it hits a so-called <em>ITL wait</em>. Note that the name of the actual wait event is called “enq: TX &#8211; allocate ITL entry”.<br />
It is essential to point out that a session does not wait on the first slot becoming free. Instead, it probes, round-robin, the available slots to find out one that becomes free. And, while doing so, it waits few seconds on every one it probes. When during this short wait the slot becomes free, it uses it. Otherwise, it tries with another slot.<br />
The actual implementation for finding a free slot is what Oracle changed in recent releases. So, let’s describe what the behavior in recent releases is.</p>
<p><strong>ITL Waits in 11gR1</strong></p>
<p>In 11.1.0.6 and 11.1.0.7 a session waits at most one time on every slot. For all slots but one it waits up to 5 seconds. For the other one it might wait indefinitely. The following pseudo code illustrates this (you should consider the variable called &#8220;itl&#8221; as an array referencing/containing all ITL slots).</p>
<pre>FOR i IN itl.FIRST..itl.LAST
LOOP
  EXIT WHEN itl(i) IS FREE
  IF i <> itl.LAST
  THEN WAIT ON itl(i) FOR 5 SECONDS
  ELSE WAIT ON itl(i) FOREVER
  END IF
END LOOP</pre>
<p>The problem of this algorithm is that an “unlucky” session might wait much longer than necessary. In fact, once it enters the WAIT FOREVER status, it no longer considers the other slots.</p>
<p><strong>ITL Waits in 11gR2</strong></p>
<p>In 11.2.0.1 and 11.2.0.2 a session might wait several times for the same slot. Initially the wait is short. As the time passes, the wait time increases exponentially based on the formula “wait time = power(2,iteration-1)”. For all slots but one there is a maximum wait time of 5 seconds, though. For the other one, and for the first 10 iterations only, the wait time is computed with the very same formula. Then, during the 11th iteration, the session waits indefinitely. The following pseudo code illustrates this.</p>
<pre>iteration = 0
LOOP
  iteration++
  FOR i IN itl.FIRST..itl.LAST
  LOOP
    EXIT WHEN itl(i) IS FREE
    IF i <> itl.LAST
    THEN WAIT ON itl(i) FOR min(power(2,iteration-1),5) SECONDS
    ELSIF iteration <= 10
    THEN WAIT ON itl(i) FOR power(2,iteration-1) SECONDS
    ELSE WAIT ON itl(i) FOREVER
    END IF
  END LOOP
  EXIT WHEN free_itl_found
END LOOP</pre>
<p>The advantage of this algorithm is that a session might probe several time all the available slots and, as a result, enters the WAIT FOREVER status after about 20 minutes only.</p>
<p><strong>ITL Waits in 9i/10g</strong></p>
<p>Up to 10.2.0.4 the behavior is similar to 11gR1. The only noticeable difference is that the wait time is not always 5 seconds. Instead, it is either 3 or 6 seconds. I was not able to spot a rule behind the choice between the two durations. So, there might be some randomness involved.<br />
In 10.2.0.5 the behavior is similar to 11gR2. Also in this case the only noticeable difference is that the maximum wait time is not always 5 seconds. Instead, as in releases up to 10.2.0.4, it is either 3 or 6 seconds.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/04/itl-waits-changes-in-recent-releases/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Scripts to Download Documentation</title>
		<link>http://www.antognini.ch/2011/04/scripts-to-download-documentation/</link>
		<comments>http://www.antognini.ch/2011/04/scripts-to-download-documentation/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 01:40:02 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1502</guid>
		<description><![CDATA[In this post I pointed out that I like to have a copy of the documentation in PDF format on my notebook. In the same post, and its comments, I also described how I generate the scripts I use to download the files. Recently I updated the scripts and, as a result, I thought to [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="/2009/09/script-to-download-11gr2-documentation/">this post</a> I pointed out that I like to have a copy of the documentation in PDF format on my notebook. In the same post, and its comments, I also described how I generate the scripts I use to download the files. Recently I updated the scripts and, as a result, I thought to share them with you. So, below you find the CMD and SH scripts for the documentation related to 10.2, 11.1 and 11.2.</p>
<ul>
<li><a href="/downloads/download102.cmd">download102.cmd</a></li>
<li><a href="/downloads/download102.sh">download102.sh</a></li>
<li><a href="/downloads/download111.cmd">download111.cmd</a></li>
<li><a href="/downloads/download111.sh">download111.sh</a></li>
<li><a href="/downloads/download112.cmd">download112.cmd</a></li>
<li><a href="/downloads/download112.sh">download112.sh</a></li>
</ul>
<p>I hope you find them useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/04/scripts-to-download-documentation/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>IS NULL Conditions and B-tree Indexes</title>
		<link>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/</link>
		<comments>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 10:01:56 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[9iR2]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1479</guid>
		<description><![CDATA[At page 383 of my book I wrote the following sentence (BTW, the same information is also provided by Table 9-3 at page 381): With B-tree indexes, IS NULL conditions can be applied only through composite B-tree indexes when several SQL conditions are applied and at least one of them is not based on IS [...]]]></description>
			<content:encoded><![CDATA[<p>At page 383 of <a href="/top">my book</a> I wrote the following sentence (BTW, the same information is also provided by Table 9-3 at page 381):</p>
<blockquote><p>With B-tree indexes, IS NULL conditions can be applied only through composite B-tree indexes when several SQL conditions are applied and at least one of them is not based on IS NULL or an inequality.</p></blockquote>
<p>The text continues by showing the following examples (notice that in both cases the IS NULL predicate is applied through an access predicate):</p>
<pre>SELECT /*+ index(t) */ * FROM t WHERE n1 = 6 AND n2 IS NULL

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1"=6 AND "N2" IS NULL)

SELECT /*+ index(t) */ * FROM t WHERE n1 IS NULL AND n2 = 8

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1" IS NULL AND "N2"=8)
       filter("N2"=8)</pre>
<p>When I wrote that sentence I didn&#8217;t think about one case that, according to it, specifically the part &#8220;is not based on IS NULL or an inequality&#8221;, is not covered. In fact, as the following examples show, it is also possible to apply an IS NULL predicate when the other one is an IS NOT NULL. It is especially interesting to notice that the access predicate doesn&#8217;t reference at all the NOT NULL column!</p>
<pre>SELECT /*+ index(t) */ * FROM t WHERE n1 IS NULL AND n2 IS NOT NULL

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1" IS NULL)
       filter("N2" IS NOT NULL)

SELECT /*+ index(t) */ * FROM t WHERE n1 IS NOT NULL AND n2 IS NULL

Plan hash value: 3029444779

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX SKIP SCAN           | I_N123 |
----------------------------------------------

   2 - access("N2" IS NULL)
       filter(("N2" IS NULL AND "N1" IS NOT NULL))</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Parallel Full Table Scans Do Not Always Perform Direct Reads</title>
		<link>http://www.antognini.ch/2010/09/parallel-full-table-scans-do-not-always-perform-direct-reads/</link>
		<comments>http://www.antognini.ch/2010/09/parallel-full-table-scans-do-not-always-perform-direct-reads/#comments</comments>
		<pubDate>Sun, 12 Sep 2010 10:52:48 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Parallel Processing]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1287</guid>
		<description><![CDATA[Even though in general parallel full table scans performs direct reads, some exceptions exist. The aim of this post is to show such an exception. For test purposes I build in my own schema a copy of the SH.SALES table (the one distributed by Oracle with the demo schemas…). On that table I build an [...]]]></description>
			<content:encoded><![CDATA[<p>Even though in general parallel full table scans performs direct reads, some exceptions exist. The aim of this post is to show such an exception.</p>
<p>For test purposes I build in my own schema a copy of the SH.SALES table (the one distributed by Oracle with the demo schemas…). On that table I build an index on the TIME_ID column and, at the same time, I trace the execution. The statements I use are the following.</p>
<pre>execute dbms_monitor.session_trace_enable
CREATE INDEX sales_time_id ON sales (time_id) PARALLEL 2 ONLINE;
execute dbms_monitor.session_trace_disable</pre>
<p>Since the index is built in parallel (DOP=2), 5 processes are used to run the statement: the query coordinator, 2 slaves for reading the table, and 2 slaves for building the index. Based on the data provided by extended SQL trace let’s have a look to some information about the execution.</p>
<ul>
<li>Execution plan (without runtime statistics and query optimizer estimations): both the build of the index and the full table scan are performed in parallel.</li>
</ul>
<pre>Operation
----------------------------------------
PX COORDINATOR
  PX SEND QC (ORDER) :TQ10001
    INDEX BUILD NON UNIQUE SALES_TIME_ID
      SORT CREATE INDEX
        PX RECEIVE
          PX SEND RANGE :TQ10000
            PX BLOCK ITERATOR
              TABLE ACCESS FULL SALES</pre>
<ul>
<li>Resource usage profile of the query coordinator: no real work is performed, it&#8217;s just coordination work&#8230;</li>
</ul>
<pre>                                               Total            Number of     Duration per
Component                               Duration [s]       %       Events       Events [s]
----------------------------------- ---------------- ------- ------------ ----------------
PX Deq: Execute Reply                          1.928  79.044           44            0.044
recursive statements                           0.427  17.487          n/a              n/a
CPU                                            0.024   0.984          n/a              n/a
os thread startup                              0.017   0.690            1            0.017
log file sync                                  0.014   0.569            2            0.007
PX Deq: Parse Reply                            0.014   0.568            4            0.003
enq: CR - block range reuse ckpt               0.005   0.202            2            0.002
PX Deq: Join ACK                               0.004   0.145            5            0.001
SQL*Net message from client                    0.002   0.098            1            0.002
PX qref latch                                  0.002   0.075            1            0.002
PX Deq: Table Q qref                           0.001   0.058            1            0.001
enq: RO - fast object reuse                    0.001   0.028            1            0.001
PX Deq: Signal ACK EXT                         0.001   0.025            6            0.000
PX Deq: Slave Session Stats                    0.000   0.014            3            0.000
reliable message                               0.000   0.006            2            0.000
latch: call allocation                         0.000   0.004            1            0.000
db file sequential read                        0.000   0.004            6            0.000
rdbms ipc reply                                0.000   0.002            2            0.000
SQL*Net message to client                      0.000   0.000            1            0.000
----------------------------------- ---------------- -------
Total                                          2.439 100.000</pre>
<ul>
<li>Resource usage profile of one of the slaves building the index: direct writes are used to store the index.</li>
</ul>
<pre>                                               Total            Number of     Duration per
Component                               Duration [s]       %       Events       Events [s]
----------------------------------- ---------------- ------- ------------ ----------------
direct path write                              0.982  49.671          376            0.003
CPU                                            0.781  39.508          n/a              n/a
PX Deq: Table Q Normal                         0.177   8.957          130            0.001
PX Deq: Execution Msg                          0.012   0.595            4            0.003
cursor: pin S wait on X                        0.011   0.535            1            0.011
recursive statements                           0.008   0.420          n/a              n/a
log file sync                                  0.005   0.247            3            0.002
Disk file operations I/O                       0.001   0.030            1            0.001
PX Deq: Slave Session Stats                    0.000   0.021            1            0.000
reliable message                               0.000   0.011            1            0.000
rdbms ipc reply                                0.000   0.006            2            0.000
----------------------------------- ---------------- -------
Total                                          1.977 100.000</pre>
<ul>
<li>Resource usage profile of one of the slaves scanning the table: instead of performing direct reads, “regular” buffered reads are performed (notice the <em>db file scattered read</em> event).</li>
</ul>
<pre>                                               Total            Number of     Duration per
Component                               Duration [s]       %       Events       Events [s]
----------------------------------- ---------------- ------- ------------ ----------------
PX Deq: Execution Msg                          1.571  80.295           23            0.068
CPU                                            0.311  15.889          n/a              n/a
PX Deq Credit: need buffer                     0.055   2.794          432            0.000
db file scattered read                         0.016   0.835           55            0.000
PX Deq: Table Q Get Keys                       0.002   0.109            1            0.002
Disk file operations I/O                       0.001   0.032            1            0.001
PX Deq Credit: send blkd                       0.001   0.028            1            0.001
PX Deq: Slave Session Stats                    0.000   0.010            1            0.000
db file sequential read                        0.000   0.005            4            0.000
PX qref latch                                  0.000   0.002            1            0.000
latch: cache buffers chains                    0.000   0.000            1            0.000
----------------------------------- ---------------- -------
Total                                          1.957 100.000</pre>
<p>As pointed out by the last resource usage profile no direct reads are performed. Why? In this case it is because the ONLINE option was specified. By the way, I do not know why there is such a limitation&#8230; Anway, without this option, for one of the slaves reading the table the following resource usage profile is used (notice the <em>direct path read</em> event). I do not show the other resource usage profiles and the execution plan because they do not change.</p>
<pre>                                               Total            Number of     Duration per
Component                               Duration [s]       %       Events       Events [s]
----------------------------------- ---------------- ------- ------------ ----------------
PX Deq: Execution Msg                          1.499  80.358           22            0.068
CPU                                            0.278  14.897          n/a              n/a
PX Deq Credit: need buffer                     0.071   3.790          504            0.000
direct path read                               0.012   0.630           52            0.000
PX Deq Credit: send blkd                       0.003   0.176            2            0.002
PX Deq: Table Q Get Keys                       0.001   0.047            2            0.000
PX Deq: Slave Session Stats                    0.001   0.041            1            0.001
Disk file operations I/O                       0.001   0.033            1            0.001
library cache: mutex X                         0.000   0.025            1            0.000
db file sequential read                        0.000   0.002            2            0.000
asynch descriptor resize                       0.000   0.001           18            0.000
----------------------------------- ---------------- -------
Total                                          1.866 100.000</pre>
<p>In summary, do not expect to always see direct reads when a parallel full table scan is performed.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2010/09/parallel-full-table-scans-do-not-always-perform-direct-reads/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

