I noticed the following event in both Oracle and PostgreSQL.
Given that we have the following database schema:
create table post ( id int8 not null, title varchar(255), version int4 not null, primary key (id)); create table post_comment ( id int8 not null, review varchar(255), version int4 not null, post_id int8, primary key (id)); alter table post_comment add constraint FKna4y825fdc5hw8aow65ijexm0 foreign key (post_id) references post;
With the following data:
insert into post (title, version, id) values ('Transactions', 0, 1); insert into post_comment (post_id, review, version, id) values (1, 'Post comment 1', 459, 0); insert into post_comment (post_id, review, version, id) values (1, 'Post comment 2', 537, 1); insert into post_comment (post_id, review, version, id) values (1, 'Post comment 3', 689, 2);
If I open two separate SQL consoles and follow these instructions:
TX1: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; TX2: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; TX1: SELECT COUNT(*) FROM post_comment where post_id = 1; TX1: > 3 TX1: UPDATE post_comment SET version = 100 WHERE post_id = 1; TX2: INSERT INTO post_comment (post_id, review, version, id) VALUES (1, 'Phantom', 0, 1000); TX2: COMMIT; TX1: SELECT COUNT(*) FROM post_comment where post_id = 1; TX1: > 3 TX1: COMMIT; TX3: SELECT * from post_comment; > 0;"Post comment 0";100;1 1;"Post comment 1";100;1 2;"Post comment 2";100;1 1000;"Phantom";0;1
As expected, the isolation level SERIALIZABLE saved the snapshot data from the start of transaction TX1, and TX1 sees only 3 post_comment .
Due to the MVCC model, Oracle and PostgreSQL TX2 are allowed to insert a new record and commit.
Why is TX1 allowed to commit? Since this is an anomaly in reading phantom, I expected to see that TX1 would roll back with a “serialization failure exception” or something similar.
Does the MVCC Serializable model in PostgreSQL and Oracle only guarantee isolation of snapshots, but not phantom read anomalous detection?
UPDATE
I even changed Tx1 to issue an UPDATE statement that changes the version column for all post_comment entries belonging to the same post .
Thus, Tx2 creates a new record, and Tx1 is about to commit, not knowing that a new record has been added that meets the UPDATE filtering criteria.
In fact, the only way to do this unsuccessfully on PostgreSQL is to execute the following COUNT query in Tx2 before inserting a phantom entry:
Tx2: SELECT COUNT(*) FROM post_comment where post_id = 1 and version = 0 TX2: INSERT INTO post_comment (post_id, review, version, id) VALUES (1, 'Phantom', 0, 1000); TX2: COMMIT;
Then Tx1 will rollback using:
org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions Detail: Reason code: Canceled on identification as a pivot, during conflict out checking. Hint: The transaction might succeed if retried.
Most likely, the mechanism for preventing record-distortion anomalies detected this change and transaction rollback.
Interestingly, Oracle does not seem to be bothered by this anomaly, and therefore Tx1 just succeeds. Since Oracle does not prevent a skewed recording, Tx1 compiles comments nicely.
By the way, you can run all of these examples yourself, as they are on GitHub .