Dynamic testing of weak isolation levels #64
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
1. Changes to 'version_list':
In the previous implementation of 'version_list', an unexpected exception could occur: "Value exists, but did not successfully read." This happens when a read operation returns no results, but previous write operations updated values that should have been part of the read results. The problem arises because these updated values haven't been committed yet, making them invisible in isolation levels above Read Committed.
This is especially concerning under snapshot isolation because it only shows updates from transactions that were committed before the snapshot was created.
Example:
Preparation Steps:
Execution Steps:
Q1-T1: BEGIN;Q2-T1: INSERT INTO t1 VALUES (1, 1);Q3-T2: BEGIN;Q4-T2: SELECT * FROM t1 WHERE k=1;(This returns null)Q5-T2: SELECT * FROM t1 WHERE k > 0 AND k < 2;(This also returns null)Q6-T2: COMMIT;Q7-T1: UPDATE t1 SET v=2 WHERE k=1;Q8-T1: COMMIT;Test Result:
Q4-T2, but it should have returned an empty result instead.Solution:
I added visible timestamps and snapshot timestamps. Now, a snapshot can only include a version if its timestamp is more recent than the visible timestamp. For read uncommitted, the visible timestamp is the time the write operation finished. For higher isolation levels, it reflects when the transaction commits.
For snapshot reads, the snapshot time is different in different DBMS. For MySQL, the snapshot is taken before the first normal read operation; for postgreSQL, the snapshot is taken before the first non-control statement.
I also allow transactions to see their own uncommitted updates.
2. Handling Versions:
To see the latest version, I traversed the 'version_list' in reverse. If the first valid version is not the one that was read, it indicates that the read operation is using an older version. This approach helps prevent Intermediate Read anomalies.
3. Checking Loops for Weak Isolation Levels:
Different isolation levels handle loops in dependency graphs differently:
For snapshot isolation, if a loop exists, it must have at least two consecutive anti-dependency edges.
Implementation Note: Adya’s definitions are the minimum requirements for isolation levels, but the actual implementation might be stricter. For instance, the serializability implemented by SSI does not allow two consecutive anti-dependency edges.
MySQL's repeatable read is lower than PL-2.99, which allows loops with item anti-dependencies. And MySQL's repeatable read is not snapshot isolation, because it allows concurrent updates, which can cause lost updates. I use snapshot read visibility plus PL-1 rules to detect MySQL's repeatable read isolation level.
Verification Process: