mirror of
https://github.com/postgres/postgres.git
synced 2026-04-09 19:16:17 -04:00
Add isolation tests for UPDATE/DELETE FOR PORTION OF
Add documentation about concurrency issues related to UPDATE/DELETE FOR PORTION OF as well as supporting isolation tests. Author: Paul A. Jungwirth <pj@illuminatedcomputing.com> Reviewed-by: Peter Eisentraut <peter@eisentraut.org> Discussion: https://www.postgresql.org/message-id/flat/ec498c3d-5f2b-48ec-b989-5561c8aa2024%40illuminatedcomputing.com
This commit is contained in:
parent
5bcc3fbd19
commit
b6ccd30d8f
9 changed files with 4823 additions and 1 deletions
|
|
@ -394,6 +394,44 @@ DELETE FROM products
|
|||
triggers are fired, but permission checks for inserting rows are
|
||||
skipped.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In <literal>READ COMMITTED</literal> mode, temporal updates and deletes can
|
||||
yield unexpected results when they concurrently touch the same row. It is
|
||||
possible to lose all or part of the second update or delete. The scenario
|
||||
is illustrated in <xref linkend="temporal-isolation-figure"/>. Session 2
|
||||
searches for rows to change, and it finds one that Session 1 has already
|
||||
modified. It waits for Session 1 to commit. Then it re-checks whether the
|
||||
row still matches its search criteria (including the start/end times
|
||||
targeted by <literal>FOR PORTION OF</literal>). Session 1 may have changed
|
||||
those times so that they no longer qualify.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In addition, the temporal leftovers inserted by Session 1 are not visible
|
||||
within Session 2's transaction, because they are not yet committed.
|
||||
Therefore there is nothing for Session 2 to update/delete: neither the
|
||||
modified row nor the leftovers. The portion of history that Session 2
|
||||
intended to change is not affected.
|
||||
</para>
|
||||
|
||||
<figure id="temporal-isolation-figure">
|
||||
<title>Temporal Isolation Example</title>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="images/temporal-isolation.svg" format="SVG" width="35%"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</figure>
|
||||
|
||||
<para>
|
||||
To solve these problems, precede every temporal update/delete with a
|
||||
<literal>SELECT FOR UPDATE</literal> matching the same criteria (including
|
||||
the targeted portion of application time). That way the actual
|
||||
update/delete doesn't begin until the lock is held, and all concurrent
|
||||
leftovers will be visible. In higher transaction isolation levels, this
|
||||
lock is not required.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="dml-returning">
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ ALL_IMAGES = \
|
|||
temporal-entities.svg \
|
||||
temporal-references.svg \
|
||||
temporal-update.svg \
|
||||
temporal-delete.svg
|
||||
temporal-delete.svg \
|
||||
temporal-isolation.svg
|
||||
|
||||
DITAA = ditaa
|
||||
DOT = dot
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ all_files = [
|
|||
'temporal-references.txt',
|
||||
'temporal-update.txt',
|
||||
'temporal-delete.txt',
|
||||
'temporal-isolation.txt',
|
||||
]
|
||||
|
||||
foreach file : all_files
|
||||
|
|
|
|||
47
doc/src/sgml/images/temporal-isolation.svg
Normal file
47
doc/src/sgml/images/temporal-isolation.svg
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 672" width="350" height="672" shape-rendering="geometricPrecision" version="1.0">
|
||||
<defs>
|
||||
<filter id="f2" x="0" y="0" width="200%" height="200%">
|
||||
<feOffset result="offOut" in="SourceGraphic" dx="5" dy="5"/>
|
||||
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="3"/>
|
||||
<feBlend in="SourceGraphic" in2="blurOut" mode="normal"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g stroke-width="1" stroke-linecap="square" stroke-linejoin="round">
|
||||
<rect x="0" y="0" width="350" height="672" style="fill: #ffffff"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 301.0 L205.0 371.0 L325.0 371.0 L325.0 301.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M325.0 469.0 L325.0 539.0 L205.0 539.0 L205.0 469.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 35.0 L25.0 91.0 L145.0 91.0 L145.0 35.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 133.0 L25.0 189.0 L145.0 189.0 L145.0 133.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 287.0 L145.0 287.0 L145.0 231.0 L25.0 231.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 133.0 L205.0 189.0 L325.0 189.0 L325.0 133.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M325.0 581.0 L205.0 581.0 L205.0 637.0 L325.0 637.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 91.0 L325.0 91.0 L325.0 35.0 L205.0 35.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M145.0 399.0 L145.0 455.0 L25.0 455.0 L25.0 399.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 112.0 L85.0 126.0 L90.0 112.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 112.0 L265.0 126.0 L270.0 112.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 210.0 L85.0 224.0 L90.0 210.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 280.0 L265.0 294.0 L270.0 280.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 378.0 L85.0 392.0 L90.0 378.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 448.0 L265.0 462.0 L270.0 448.0 z"/>
|
||||
<path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 560.0 L265.0 574.0 L270.0 560.0 z"/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 91.0 L85.0 119.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 189.0 L85.0 217.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 539.0 L265.0 567.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 91.0 L265.0 119.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 371.0 L265.0 455.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 385.0 L85.0 287.0 "/>
|
||||
<path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 189.0 L265.0 287.0 "/>
|
||||
<text x="46" y="68" font-family="Courier" font-size="13" stroke="none" fill="#000000">Session 1</text>
|
||||
<text x="220" y="334" font-family="Courier" font-size="13" stroke="none" fill="#000000">UPDATE;</text>
|
||||
<text x="220" y="348" font-family="Courier" font-size="13" stroke="none" fill="#000000">waits...</text>
|
||||
<text x="48" y="166" font-family="Courier" font-size="13" stroke="none" fill="#000000">BEGIN;</text>
|
||||
<text x="228" y="166" font-family="Courier" font-size="13" stroke="none" fill="#000000">BEGIN;</text>
|
||||
<text x="226" y="68" font-family="Courier" font-size="13" stroke="none" fill="#000000">Session 2</text>
|
||||
<text x="47" y="264" font-family="Courier" font-size="13" stroke="none" fill="#000000">UPDATE;</text>
|
||||
<text x="46" y="432" font-family="Courier" font-size="13" stroke="none" fill="#000000">COMMIT;</text>
|
||||
<text x="226" y="614" font-family="Courier" font-size="13" stroke="none" fill="#000000">COMMIT;</text>
|
||||
<text x="220" y="502" font-family="Courier" font-size="13" stroke="none" fill="#000000">...resumes</text>
|
||||
<text x="220" y="516" font-family="Courier" font-size="13" stroke="none" fill="#000000">rechecks</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
44
doc/src/sgml/images/temporal-isolation.txt
Normal file
44
doc/src/sgml/images/temporal-isolation.txt
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
+-----------+ +-----------+
|
||||
| cGRE | | cYEL |
|
||||
|Session 1 | |Session 2 |
|
||||
| | | |
|
||||
+-----+-----+ +-----+-----+
|
||||
| |
|
||||
v v
|
||||
+-----+-----+ +-----+-----+
|
||||
| cGRE | | cYEL |
|
||||
| BEGIN; | | BEGIN; |
|
||||
| | | |
|
||||
+-----+-----+ +-----+-----+
|
||||
| |
|
||||
v |
|
||||
+-----+-----+ |
|
||||
| cGRE | |
|
||||
| UPDATE; | |
|
||||
| | |
|
||||
+-----+-----+ v
|
||||
| +-----+-----+
|
||||
| | cYEL |
|
||||
| | UPDATE; |
|
||||
| | waits... |
|
||||
| | |
|
||||
| +-----+-----+
|
||||
v |
|
||||
+-----+-----+ |
|
||||
| cGRE | |
|
||||
| COMMIT; | |
|
||||
| | |
|
||||
+-----------+ v
|
||||
+-----+-----+
|
||||
| cYEL |
|
||||
| ...resumes|
|
||||
| rechecks |
|
||||
| |
|
||||
+-----+-----+
|
||||
|
|
||||
v
|
||||
+-----+-----+
|
||||
| cYEL |
|
||||
| COMMIT; |
|
||||
| |
|
||||
+-----------+
|
||||
|
|
@ -1465,6 +1465,10 @@ ExecForPortionOfLeftovers(ModifyTableContext *context,
|
|||
* We have already locked the tuple in ExecUpdate/ExecDelete, and it has
|
||||
* passed EvalPlanQual. This ensures that concurrent updates in READ
|
||||
* COMMITTED can't insert conflicting temporal leftovers.
|
||||
*
|
||||
* It does *not* protect against concurrent update/deletes overlooking
|
||||
* each others' leftovers though. See our isolation tests for details
|
||||
* about that and a viable workaround.
|
||||
*/
|
||||
if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc, tupleid, SnapshotAny, oldtupleSlot))
|
||||
elog(ERROR, "failed to fetch tuple for FOR PORTION OF");
|
||||
|
|
|
|||
4089
src/test/isolation/expected/for-portion-of.out
Normal file
4089
src/test/isolation/expected/for-portion-of.out
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -125,3 +125,4 @@ test: serializable-parallel-2
|
|||
test: serializable-parallel-3
|
||||
test: matview-write-skew
|
||||
test: lock-nowait
|
||||
test: for-portion-of
|
||||
|
|
|
|||
597
src/test/isolation/specs/for-portion-of.spec
Normal file
597
src/test/isolation/specs/for-portion-of.spec
Normal file
|
|
@ -0,0 +1,597 @@
|
|||
# UPDATE/DELETE FOR PORTION OF test
|
||||
#
|
||||
# Test inserting temporal leftovers from a FOR PORTION OF update/delete.
|
||||
#
|
||||
# In READ COMMITTED mode, concurrent updates/deletes to the same records cause
|
||||
# weird results. Portions of history that should have been updated/deleted don't
|
||||
# get changed. That's because the leftovers from one operation are added too
|
||||
# late to be seen by the other. EvalPlanQual will reload the changed-in-common
|
||||
# row, but it won't re-scan to find new leftovers.
|
||||
#
|
||||
# MariaDB similarly gives undesirable results in READ COMMITTED mode (although
|
||||
# not the same results). DB2 doesn't have READ COMMITTED, but it gives correct
|
||||
# results at all levels, in particular READ STABILITY (which seems closest).
|
||||
#
|
||||
# A workaround is to lock the part of history you want before changing it (using
|
||||
# SELECT FOR UPDATE). That way the search for rows is late enough to see
|
||||
# leftovers from the other session(s). This shouldn't impose any new deadlock
|
||||
# risks, since the locks are the same as before. Adding a third/fourth/etc.
|
||||
# connection also doesn't change the semantics. The READ COMMITTED tests here
|
||||
# demonstrate the problem and also show that solving it with manual locks is
|
||||
# viable and not vitiated by any bugs. Incidentally, this approach also works in
|
||||
# MariaDB.
|
||||
#
|
||||
# We run the same tests under REPEATABLE READ to show the problem goes away.
|
||||
# In general they do what you'd want with no explicit locking required, but some
|
||||
# orderings raise a concurrent update/delete failure (as expected). If there is
|
||||
# a prior read by s1, concurrent update/delete failures are more common.
|
||||
#
|
||||
# To save on test time, we only run a couple SERIALIZABLE tests (for the more
|
||||
# problematic permutations).
|
||||
#
|
||||
# We test updates where s2 updates history that is:
|
||||
#
|
||||
# - non-overlapping with s1,
|
||||
# - contained entirely in s1,
|
||||
# - partly contained in s1.
|
||||
#
|
||||
# We don't need to test where s2 entirely contains s1 because of symmetry:
|
||||
# we test both when s1 precedes s2 and when s2 precedes s1, so that scenario is
|
||||
# covered.
|
||||
#
|
||||
# We test various orderings of the update/delete/commit from s1 and s2.
|
||||
# Note that `s1lock s2lock s1change` is boring because it's the same as
|
||||
# `s1lock s1change s2lock`. In other words it doesn't matter if something
|
||||
# interposes between the lock and its change (as long as everyone is following
|
||||
# the same policy).
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE products (
|
||||
id int4range NOT NULL,
|
||||
valid_at daterange NOT NULL,
|
||||
price decimal NOT NULL,
|
||||
PRIMARY KEY (id, valid_at WITHOUT OVERLAPS));
|
||||
INSERT INTO products VALUES
|
||||
('[1,2)', '[2020-01-01,2030-01-01)', 5.00);
|
||||
}
|
||||
|
||||
teardown { DROP TABLE products; }
|
||||
|
||||
session s1
|
||||
setup { SET datestyle TO ISO, YMD; }
|
||||
step s1rc { BEGIN ISOLATION LEVEL READ COMMITTED; }
|
||||
step s1rr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
|
||||
step s1ser { BEGIN ISOLATION LEVEL SERIALIZABLE; }
|
||||
step s1lock2025 {
|
||||
SELECT * FROM products
|
||||
WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)'
|
||||
ORDER BY valid_at FOR UPDATE;
|
||||
}
|
||||
step s1upd2025 {
|
||||
UPDATE products
|
||||
FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01'
|
||||
SET price = 8.00
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s1del2025 {
|
||||
DELETE FROM products
|
||||
FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01'
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s1q { SELECT * FROM products ORDER BY id, valid_at; }
|
||||
step s1c { COMMIT; }
|
||||
|
||||
session s2
|
||||
setup { SET datestyle TO ISO, YMD; }
|
||||
step s2rc { BEGIN ISOLATION LEVEL READ COMMITTED; }
|
||||
step s2rr { BEGIN ISOLATION LEVEL REPEATABLE READ; }
|
||||
step s2ser { BEGIN ISOLATION LEVEL SERIALIZABLE; }
|
||||
step s2lock202503 {
|
||||
SELECT * FROM products
|
||||
WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)'
|
||||
ORDER BY valid_at FOR UPDATE;
|
||||
}
|
||||
step s2lock20252026 {
|
||||
SELECT * FROM products
|
||||
WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)'
|
||||
ORDER BY valid_at FOR UPDATE;
|
||||
}
|
||||
step s2lock2027 {
|
||||
SELECT * FROM products
|
||||
WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)'
|
||||
ORDER BY valid_at FOR UPDATE;
|
||||
}
|
||||
step s2upd202503 {
|
||||
UPDATE products
|
||||
FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01'
|
||||
SET price = 10.00
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2upd20252026 {
|
||||
UPDATE products
|
||||
FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01'
|
||||
SET price = 10.00
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2upd2027 {
|
||||
UPDATE products
|
||||
FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01'
|
||||
SET price = 10.00
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2del202503 {
|
||||
DELETE FROM products
|
||||
FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01'
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2del20252026 {
|
||||
DELETE FROM products
|
||||
FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01'
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2del2027 {
|
||||
DELETE FROM products
|
||||
FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01'
|
||||
WHERE id = '[1,2)';
|
||||
}
|
||||
step s2c { COMMIT; }
|
||||
|
||||
# ########################################
|
||||
# READ COMMITTED tests, UPDATE+UPDATE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2upd2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2upd202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2upd20252026 s2c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be updated either.
|
||||
permutation s1rc s2rc s2upd2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 updates the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# but EvalPlanQual still matches the row to be updated.
|
||||
permutation s1rc s2rc s2upd202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 overwrites the row from s2 and sees its leftovers
|
||||
permutation s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# but EvalPlanQual still matches the row to be updated,
|
||||
# and s1's leftovers don't conflict with s2's.
|
||||
permutation s1rc s2rc s2upd20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 overwrites the row from s2 and sees its leftovers
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# ########################################
|
||||
# READ COMMITTED tests, UPDATE+DELETE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2del2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2del202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2del20252026 s2c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be updated either.
|
||||
permutation s1rc s2rc s2del2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 updates the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be updated either.
|
||||
permutation s1rc s2rc s2del202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 sees the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be updated either.
|
||||
permutation s1rc s2rc s2del20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 sees the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1upd2025 s1c s1q
|
||||
|
||||
# ########################################
|
||||
# READ COMMITTED tests, DELETE+UPDATE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2upd2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2upd202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2upd20252026 s2c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be deleted either.
|
||||
permutation s1rc s2rc s2upd2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# but EvalPlanQual still matches the row to be deleted.
|
||||
permutation s1rc s2rc s2upd202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the new row from s2 and its leftovers
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# but EvalPlanQual still matches the row to be deleted,
|
||||
# and s1 leaves leftovers from the row created by s2.
|
||||
permutation s1rc s2rc s2upd20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the new row from s2 and its leftovers
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# ########################################
|
||||
# READ COMMITTED tests, DELETE+DELETE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2del2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2del202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2del20252026 s2c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be deleted either.
|
||||
permutation s1rc s2rc s2del2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be deleted either.
|
||||
permutation s1rc s2rc s2del202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# Problem:
|
||||
# s1 (without locking) overlooks the leftovers from s2
|
||||
# and EvalPlanQual no longer matches the row to be deleted either.
|
||||
permutation s1rc s2rc s2del20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
# Workaround:
|
||||
# s1 deletes the leftovers from s2
|
||||
# Locking is required or s1 won't see the leftovers.
|
||||
permutation s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1del2025 s1c s1q
|
||||
|
||||
# ########################################
|
||||
# REPEATABLE READ tests, UPDATE+UPDATE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rr s2rr s2upd2027 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2upd202503 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2upd20252026 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1upd2025 s1c s2upd2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s1upd2025 s1c s2upd202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s1upd2025 s1c s2upd20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
## with prior read by s1:
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd2027 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd202503 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd20252026 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2upd2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2upd202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2upd20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
# ########################################
|
||||
# REPEATABLE READ tests, UPDATE+DELETE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rr s2rr s2del2027 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s2del202503 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s2del20252026 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1upd2025 s1c s2del2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row
|
||||
permutation s1rr s2rr s1upd2025 s1c s2del202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s1upd2025 s1c s2del20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
## with prior read by s1:
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del2027 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del202503 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del20252026 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2del2027 s2c s1q
|
||||
|
||||
# s2 loads the updated row
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2del202503 s2c s1q
|
||||
|
||||
# s2 loads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s1q s1upd2025 s1c s2del20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del2027 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del202503 s1upd2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del20252026 s1upd2025 s2c s1c s1q
|
||||
|
||||
# ########################################
|
||||
# REPEATABLE READ tests, DELETE+UPDATE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rr s2rr s2upd2027 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2upd202503 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2upd20252026 s2c s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1del2025 s1c s2upd2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rr s2rr s1del2025 s1c s2upd202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s1del2025 s1c s2upd20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s2upd20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
## with prior read by s1:
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd2027 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd202503 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd20252026 s2c s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2upd2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2upd202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2upd20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent update
|
||||
permutation s1rr s2rr s1q s2upd20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
# ########################################
|
||||
# REPEATABLE READ tests, DELETE+DELETE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1rr s2rr s2del2027 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2del202503 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1rr s2rr s2del20252026 s2c s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1del2025 s1c s2del2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rr s2rr s1del2025 s1c s2del202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s1del2025 s1c s2del20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s2del20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
## with prior read by s1:
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del2027 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del202503 s2c s1del2025 s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del20252026 s2c s1del2025 s1c s1q
|
||||
|
||||
# s2 sees the leftovers
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2del2027 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2del202503 s2c s1q
|
||||
|
||||
# s2 ignores the deleted row and sees its leftovers
|
||||
permutation s1rr s2rr s1q s1del2025 s1c s2del20252026 s2c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del2027 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del202503 s1del2025 s2c s1c s1q
|
||||
|
||||
# s1 fails from concurrent delete
|
||||
permutation s1rr s2rr s1q s2del20252026 s1del2025 s2c s1c s1q
|
||||
|
||||
# ########################################
|
||||
# SERIALIZABLE tests, UPDATE+UPDATE:
|
||||
# ########################################
|
||||
|
||||
# s1 sees the leftovers
|
||||
permutation s1ser s2ser s2upd2027 s2c s1upd2025 s1c s1q
|
||||
|
||||
# s1 reloads the updated row and sees its leftovers
|
||||
permutation s1ser s2ser s2upd20252026 s2c s1upd2025 s1c s1q
|
||||
Loading…
Reference in a new issue