Oracle: Select immediately after insert in serializable transaction
Asked Answered
H

2

7

I have run into a strange problem lately, programming in an Oracle database: inside a serializable transaction, i do a mass insert (INSERT ... SELECT), and immediately after, I open a cursor with a SELECT on the altered table. I assumed that this cursor would include the newly inserted rows, but, to my surprise, its contents are erratic, sometimes including all the newly inserted rows, and sometimes only a subset.

I have solved this problem by commiting before opening the cursor, but the behaviour has puzzled me. Can a select after an insert inside the same transaction, without an intervining commit, actually be trusted? Or is this behaviour somehow related to the transaction being serializable?

Followup: When trying to create a reproducible test case, I was only able to obtain this behaviour once I added an index (in this case a primary key index, on the actual code it was a regular index). Perhaps the problem lies in the time spent building the index, so that the SELECT actually uses an incomplete index to retrieve the results? Anyway, here goes a reproducible test case:

-- Create empty source table
CREATE TABLE TEST_CASE_1 AS 
  (SELECT 'CONTENT' AS CONTENT
   FROM DUAL
   WHERE 1 = 2)

-- Add primary key
ALTER TABLE TEST_CASE_1
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT);

-- Create empty destination table
CREATE TABLE TEST_CASE_2 AS 
  (SELECT 'CONTENT' AS CONTENT
   FROM DUAL
   WHERE 1 = 2)

-- Example of faulty code
BEGIN

  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

  -- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good)
  INSERT INTO TEST_CASE_1
    (SELECT ROWNUM 
     FROM ALL_OBJECTS
     WHERE ROWNUM <= 100000);

  INSERT INTO TEST_CASE_2
    (SELECT *
     FROM TEST_CASE_1
     WHERE CONTENT > 0);

  COMMIT;

END;

In this example, I would expect TEST_CASE_2 to also have 100.000 rows. Reproducing this test case (in a load-free database), I obtained about 400-500 rows inserted. Removing the statement setting the transaction as serializable, I obtained the correct 100.000 row count.

Heidiheidie answered 6/8, 2012 at 10:19 Comment(4)
Are you opening the cursor in the same database session as you're doing the insert? Where are you doing this work - actually in the database or from a client application - and if the latter do you have connection pooling, and you're (sometimes) getting a different connection for the two actions?Ealing
The cursor is being opened in the same session. Originally the cursor was opened in the database and then iterated in a .NET executable, but to isolate the problem I made a version of the procedure that iterated the cursor inside the database, and the problem persisted - in fact before doing this I couldn't convince myself that the problem was in the select following the insert.Heidiheidie
That shouldn't happen; from the docs 'In the serialization isolation level, a transaction sees only changes committed at the time the transaction—not the query—began and changes made by the transaction itself', so you should see your own changes. Could be a bug in your specific version I guess. Committing makes changing the isolation level a bit pointless. Can you post a reproducible test case?Ealing
I followed up the response with a reproducible test case. By building the test case, I now suspect that the problem is in the interaction between serializable transactions and index updating. The Oracle version I tested this in is the Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit.Heidiheidie
E
8

This seems to be a bug; if you've got access to Oracle's support website look at note 1455175.1, which dates back to 8i. There are a couple of bug numbers listed (7592038 - 'SILENTLY INVISIBLE DATA FROM SELECT/UPDATE OF NEWLY INSERTED ROW IN SERIALIZABLE', 6363019) but they're closed as duplicates of 440317 ('ISOLATION LEVEL SERIALIZABLE CAUSES NO DATA FOUND ON ROWS SELECTED AFTER INSERT'), which is shown as still open and being investigated by development - even though it was originally raised against version 7(!).

You seem to be right that's it's related to the PK. The workarounds listed are:

  • Commit the work executed to that point.
  • Execute additional (but different) statements (perhaps after rolling back to a savepoint established earlier in the transaction).
  • Roll back the entire transaction and restart the transaction from beginning.
  • Perform a full table scan and avoid using the indexes.

You know the first workaround is effective already, and I don't think the second or third will help you? You could try the fourth, adding a /*+ FULL(TEST_CASE_1) */ hint to the select for the second insert.

I don't get the error in 11.2.0.2 (Linux), though I can't find anything suggesting the bug has been fixed; and I don't have an 11.1 environment to try it on - so I can't check that last work-around applies to this test case.

There's a note that you can get ORA-08177 instead in 11G. I had that problem if I ran the anonymous block too soon after creating the tables, or if I had too many rows inserted, which also seems to be related to the PK. This previous question may be relevant.

Seems like this will continue to be a problem, so if the workarounds don't help you may need to reconsider if you really do need to change the isolation level; and if you do you may have to raise a service request with Oracle to get a better answer.

Ealing answered 7/8, 2012 at 12:40 Comment(2)
Thank you a lot for your thorough answer! The bug I'm experiencing does seem to correspond to the issue in Oracle support. I'm glad it is a bug as we are dependent on serializable transactions for a very small but mission-critical section of our system, and if these were standard semantics for serializable transactions, we would probably have to rewrite the whole section. The first workaround (which I used) is fine in my particular scenario, but not in other parts of the program. I will keep the whole arsenal of workarounds in my toolbox.Heidiheidie
It seems that this bug still isn't fixed; I just reproduced it with Oracle 12.1.0.1.0 on Windows.Woolfolk
W
2

This is a confirmed bug, and Oracle stated that they do not plan on fixing it. Here's an excerpt from their response to my service request (January 2015):

These symptoms are due to the Serializable transaction been found with the known issues and you conclusion with the Bug 440317 is correct.

Bug 440317 - ISOLATION LEVEL SERIALIZABLE CAUSES NO DATA FOUND ON ROWS SELECTED AFTER INSERT
Bug 16803610 - ROWS INSERTED USING INSERT INTO ARE LOST IN SERIALIZABLE ISOLATION LEVEL TRANSAC

Both these Bugs are published, so you can see the details in the MOS bug search.

As per development, there was multiple bugs for the same issue with very long history. The design isn't easy to change, hence there's no fix till the moment forking out this feature a not very useful one.

Development has closed the bug saying code fix is not feasible.

The workarounds suggested are
Application Code modifiction:
change the logic to have the commit before the select
or dont use serializable
Without application code modification:
Do not use the primary key or the indexes on the table

Woolfolk answered 10/2, 2015 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.