Yesterday I thought I’d spend half an hour before breakfast creating a little demonstration of a feature; some time about midnight I felt it was time to stop because I’d spent enough time chasing around a couple of bugs that produced wrong results in a variety of ways. Today’s short post is just little warning: be VERY careful what you do with the PL/SQL result cache – if you use the results of database queries in the cache you may end up with inconsistent results in your application. Here’s one very simple example of what can go wrong, starting with a little script:
alter system flush shared_pool; truncate table gtt1; drop table gtt1; create global temporary table gtt1 (n1 number) on commit preserve rows ; insert into gtt1 values(1); commit; execute dbms_stats.gather_table_stats(user,'gtt1'); create or replace function f_cache return number result_cache -- relies_on (gtt1) is m_ret number; begin select max(n1) into f_cache.m_ret from gtt1 ; return f_cache.m_ret; end; / execute dbms_output.put_line(f_cache)
Here’s the output from a session that’s just connected and run this script (the table already existed from an earlier run):
SQL> @temp System altered. Table truncated. Table dropped. Table created. 1 row created. Commit complete. PL/SQL procedure successfully completed. Function created. 1 PL/SQL procedure successfully completed. SQL>
Let’s call this session A, and I’m going to connect with another session which I’ll call session B. The following extracts are cut-and-pastes as I alternate between sessions:
Session B:
SQL> execute dbms_output.put_line(f_cache); 1 PL/SQL procedure successfully completed. SQL> insert into gtt1 values(0); 1 row created. SQL> execute dbms_output.put_line(f_cache); 0 PL/SQL procedure successfully completed.
Session B has just seen the data inserted into a global temporary table by session A; but after inserting a row into the table it now sees what it perceives to be the correct answer.
Session A:
SQL> truncate table gtt1; Table truncated. SQL> execute dbms_output.put_line(f_cache); 1 PL/SQL procedure successfully completed.
Despite truncating the table, session A still sees the data which has been eliminated unrecoverably.
Session B (where I hadn’t yet committed):
SQL> commit; Commit complete. SQL>
Session A (where I’ve done nothing new):
SQL> execute dbms_output.put_line(f_cache); PL/SQL procedure successfully completed. SQL>
The row has finally “disappeared” because session B committed.
Session B (where I haven’t done anything since committing):
SQL> execute dbms_output.put_line(f_cache); PL/SQL procedure successfully completed. SQL>
Session B no longer sees the data because it’s now seeing what session A has just seen.
Warning.
This is just one of several ways I managed to get surprising inconsistencies when using the (cross-session) PL/SQL Result Cache. Oracle (in 12c) is supposed to know what the cache relies on and react accordingly, but it doesn’t manage to do it properly (even if you include the relies_on clause) – if you query the database in your PL/SQL you may find strange things happen.
The most important point to note in this example is that a session that wasn’t necessarily doing anything wrong got the wrong results because of the actions of another session that Oracle should have protected it from.
The good thing about this is example that it’s documented (sort of) – as it says in the manual: “to be result cached … does not reference … temporary tables ..”. So that should warn people off copying my example; on the other hand the problem I’m seeing arises because Oracle seems to be trying to use the result cache when the manuals suggests it shouldn’t be.
Conclusion
Do not mix the pl/sql result cache with database queries. The cache is public but (unlike the buffer cache) it is not guaranteed to give you read-consistency.
If you think this was an unrealistic example and you don’t need to worry about it – I’ll post a couple more examples in the next couple of weeks. They’ll be slightly special cases again, but I find the Oracle world is full of special cases.
Update
This behaviour is now visible on MoS as “Bug 21907155 : PL/SQL RC FN SHOWS ONE SESSION THE CONTENTS OF ANOTHER SESSION’S GTT”
