{
  "date": "2016-09-25T16:51:17",
  "slug": "oracle-12cr2-is_rolling_invalid-in-vsql",
  "link": "https://www.dbi-services.com/blog/oracle-12cr2-is_rolling_invalid-in-vsql/",
  "title": {
    "rendered": "Oracle 12cR2: IS_ROLLING_INVALID in V$SQL"
  },
  "content": {
    "rendered": "<h2>By Franck Pachot</h2>\n<p>.<br />\nIn a <a href=\"http://dbi-services.com/blog/rolling-invalidate-window-exceeded/\" target=\"_blank\" rel=\"noopener noreferrer\">previous post</a> I published a test case to show when a cursor is not shared anymore after a rolling invalidation. Basically the dbms_stats marks the cursor as &#8216;rolling invalid&#8217; and the next execution marks it as &#8216;rolling invalid executed&#8217;. Looking at 12<em>c</em>R2 there is a little enhancement in V$SQL with an additional column displays those states.<br />\n<!--more--><br />\nNote that 12<em>c</em>R2 full documentation is not yet available, but you can test this on the Exadata Express Cloud Service.</p>\n<p>I set the invalidation period to 5 seconds instead of 5 hours to show the behavior without waiting</p>\n<pre><code>\n17:43:52 SQL&gt; alter system set \"_optimizer_invalidation_period\"=5;\nSystem altered.\n</code></pre>\n<p>I&#8217;ll run a statement with dbms_sql in order to separate parse and execute phases</p>\n<pre><code>\n17:43:53 SQL&gt; variable c number\n17:43:53 SQL&gt; exec :c := dbms_sql.open_cursor;\nPL/SQL procedure successfully completed.\n17:43:53 SQL&gt; exec dbms_sql.parse(:c, 'select (cast(sys_extract_utc(current_timestamp) as date)-date''1970-01-01'')*24*3600 from DEMO' , dbms_sql.native );\nPL/SQL procedure successfully completed.\n</code></pre>\n<p>Here is the cursor from V$SQL including the new IS_ROLLING_INVALID column:</p>\n<pre><code>\n17:43:53 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          0 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 N\n</code></pre>\n<p>Statement is parsed (one parse call + load) but IS_ROLLING_INVALID is N</p>\n<p>Now I execute it:</p>\n<pre><code>\n17:43:53 SQL&gt; exec dbms_output.put_line( dbms_sql.execute(:c) );\n0\nPL/SQL procedure successfully completed.\n&nbsp;\n17:43:53 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          1 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 N\n</code></pre>\n<p>Statement has one execution.</p>\n<p>I&#8217;m now gathering statistics with default rolling invalidation:</p>\n<pre><code>\n17:43:53 SQL&gt; exec dbms_stats.gather_table_stats(user,'DEMO');\nPL/SQL procedure successfully completed.\n&nbsp;\n17:43:53 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          1 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 Y\n</code></pre>\n<p>The cursor is now marked as rolling invalid (IS_ROLLING_INVALID=&#8221;Y&#8221;) but wait, this is not a &#8220;Y&#8221;/&#8221;N&#8221; boolean, there&#8217;s another possible value.</p>\n<p>I execute the statement again (no parse call, only execution):</p>\n<pre><code>\n17:43:53 SQL&gt; exec dbms_output.put_line( dbms_sql.execute(:c) );\n0\nPL/SQL procedure successfully completed.\n&nbsp;\n17:43:53 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 X\n</code></pre>\n<p>Cursor is now marked as rolling invalid executed (&#8220;X&#8221;) and this is where the rolling window starts (which I&#8217;ve set to 5 seconds instead of 5 hours)</p>\n<p>I wait 5 seconds and the cursor has not changed:</p>\n<pre><code>\n17:43:58 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 X\n&nbsp;\n</code></pre>\n<p>I execute it again (no parse call, only re-execute the cursor):</p>\n<pre><code>\n17:43:58 SQL&gt; exec dbms_output.put_line( dbms_sql.execute(:c) );\n0\nPL/SQL procedure successfully completed.\n</code></pre>\n<p>For this execution, a new child has been created:</p>\n<pre><code>\n17:43:58 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 X\n            0          1           0          1 2016-09-25/17:43:53 2016-09-25/17:43:57 25-SEP-16 17:43:57 N\n</code></pre>\n<p>So rolling invalidation do not require a parse call. Execution can start the rolling window and set the invalidation timestamp, and first execution after this timestamp creates a new child cursor.</p>\n<p>I&#8217;ll now test what happens with parse calls only.</p>\n<p>I set a longer rolling window (2 minutes) here:</p>\n<pre><code>\n17:43:58 SQL&gt; exec dbms_stats.gather_table_stats(user,'DEMO');\nPL/SQL procedure successfully completed.\n&nbsp;\n17:43:58 SQL&gt; alter system set \"_optimizer_invalidation_period\"=120;\nSystem altered.\n</code></pre>\n<p>The last child has been marked as rolling invalid but not yet executed in this state:</p>\n<pre><code>\n17:43:58 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTIVE_TIME   IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- ------------------ ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 17:43:52 X\n            0          1           0          1 2016-09-25/17:43:53 2016-09-25/17:43:57 25-SEP-16 17:43:57 Y\n</code></pre>\n<p>From a new session I open another cursor:</p>\n<pre><code>\n17:43:58 SQL&gt; connect &amp;_user./demo@&amp;_connect_identifier\nConnected.\n17:43:58 SQL&gt; exec :c := dbms_sql.open_cursor;\nPL/SQL procedure successfully completed.\n</code></pre>\n<p>And run several parse calls without execute, one every 10 seconds:</p>\n<pre><code>\n17:43:58 SQL&gt; exec for i in 1..12 loop dbms_sql.parse(:c, 'select (cast(sys_extract_utc(current_timestamp) as date)-date''1970-01-01'')*24*3600 from DEMO' , dbms_sql.native ); dbms_lock.sleep(10); end loop;\nPL/SQL procedure successfully completed.\n</code></pre>\n<p>So two minutes later I see that I have a new child created during the rolling window:</p>\n<pre><code>\n17:45:58 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTI IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- --------- ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 X\n            0          1           3          1 2016-09-25/17:43:53 2016-09-25/17:43:57 25-SEP-16 Y\n            0          1           9          0 2016-09-25/17:43:53 2016-09-25/17:44:27 25-SEP-16 N\n</code></pre>\n<p>Here, at the third parse call (17:44:27) during the invalidation window, a new child cursor has been created. The old one is still marked as rolling invalid (&#8220;Y&#8221;), but not &#8216;rolling invalid executed&#8217; (&#8220;X&#8221;) because it has not been executed.</p>\n<p>So it seems that both parse or execute are triggering the rolling invalidation, and the IS_ROLLING_INVALID displays which one.</p>\n<p>An execute will now execute the new cursor:</p>\n<pre><code>\n17:45:58 SQL&gt; exec dbms_output.put_line( dbms_sql.execute(:c) );\n&nbsp;\nPL/SQL procedure successfully completed.\n&nbsp;\n17:45:58 SQL&gt; select invalidations,loads,parse_calls,executions,first_load_time,last_load_time,last_active_time,is_rolling_invalid from v$sql where sql_id='61x2h0y9zv0r6';\n&nbsp;\nINVALIDATIONS      LOADS PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME     LAST_LOAD_TIME      LAST_ACTI IS_ROLLING_INVALID\n------------- ---------- ----------- ---------- ------------------- ------------------- --------- ------------------\n            0          1           1          2 2016-09-25/17:43:53 2016-09-25/17:43:53 25-SEP-16 X\n            0          1           3          1 2016-09-25/17:43:53 2016-09-25/17:43:57 25-SEP-16 Y\n            0          1           9          1 2016-09-25/17:43:53 2016-09-25/17:44:27 25-SEP-16 N\n</code></pre>\n<p>Of course, when new cursors have been created we can see the reason in V$SQL_SHARED_CURSOR:</p>\n<pre><code>\n17:45:58 SQL&gt; select child_number,reason from v$sql_shared_cursor where sql_id='61x2h0y9zv0r6';\n&nbsp;\nCHILD_NUMBER REASON\n------------ --------------------------------------------------------------------------------\n           0 &lt;ChildNode&gt;&lt;ChildNumber&gt;0&lt;/ChildNumber&gt;&lt;ID&gt;33&lt;/ID&gt;&lt;reason&gt;Rolling Invalidate Win\n             dow Exceeded(2)&lt;/reason&gt;&lt;size&gt;0x0&lt;/size&gt;&lt;details&gt;already_processed&lt;/details&gt;&lt;/Ch\n             ildNode&gt;&lt;ChildNode&gt;&lt;ChildNumber&gt;0&lt;/ChildNumber&gt;&lt;ID&gt;33&lt;/ID&gt;&lt;reason&gt;Rolling Invali\n             date Window Exceeded(3)&lt;/reason&gt;&lt;size&gt;2x4&lt;/size&gt;&lt;invalidation_window&gt;1472658232&lt;\n             /invalidation_window&gt;&lt;ksugctm&gt;1472658237&lt;/ksugctm&gt;&lt;/ChildNode&gt;\n&nbsp;\n           1 &lt;ChildNode&gt;&lt;ChildNumber&gt;1&lt;/ChildNumber&gt;&lt;ID&gt;33&lt;/ID&gt;&lt;reason&gt;Rolling Invalidate Win\n             dow Exceeded(2)&lt;/reason&gt;&lt;size&gt;0x0&lt;/size&gt;&lt;details&gt;already_processed&lt;/details&gt;&lt;/Ch\n             ildNode&gt;&lt;ChildNode&gt;&lt;ChildNumber&gt;1&lt;/ChildNumber&gt;&lt;ID&gt;33&lt;/ID&gt;&lt;reason&gt;Rolling Invali\n             date Window Exceeded(3)&lt;/reason&gt;&lt;size&gt;2x4&lt;/size&gt;&lt;invalidation_window&gt;1472658266&lt;\n             /invalidation_window&gt;&lt;ksugctm&gt;1472658268&lt;/ksugctm&gt;&lt;/ChildNode&gt;\n&nbsp;\n           2\n</code></pre>\n<p>The last child cursor has been created at 5:44:28 (invalidation_window=1472658268) because invalidation timestamp (ksugctm=1472658266)</p>\n<h3>So what?</h3>\n<p>We love Oracle because it&#8217;s not a black box. And it&#8217;s good to see that they continue in this way by exposing in V$ views information that can be helpful for troubleshooting. </p>\n<p>Rolling invalidation has been introduced for dbms_stats because we have to gather statistics and we don&#8217;t want hard parse storms after that.<br />\nBut remember that invalidation can also occur with DDL such as create, alter, drop, comment, grant, revoke.</p>\n<p>You should avoid running DDL when application is running. However, we may have to do some of those operations online. It would be nice to have the same rolling invalidation mechanisms and it seems that it will be possible: </p>\n<pre><code>\nSQL&gt; show parameter invalid\n&nbsp;\nNAME                                 TYPE        VALUE\n------------------------------------ ----------- ------------------------------\ncursor_invalidation                  string      IMMEDIATE\n&nbsp;\nSQL&gt; alter session set cursor_invalidation=XXX;\nERROR:\nORA-00096: invalid value XXX for parameter cursor_invalidation, must be from among IMMEDIATE, DEFERRED\n</code></pre>\n<p>That&#8217;s interesting. I&#8217;ll explain which DDL can use that in a future blog post.</p>\n",
    "protected": false
  }
}
