On 1/2/24 00:07, Marko Mäkelä wrote:
  Consider a
simple example like this, where two transactions T2 and T3 update the same
row in parallel:

   T1: BEGIN
   T1: INSERT INTO t1 VALUES (1,1), (2,2), (3,3)
   T1: COMMIT

   T2:   BEGIN
   T2:   SELECT * FROM t1                 # S1: (2,2) ?

   T3:     BEGIN
   T3:     UPDATE t1 SET b=12 where a=2
   T3:     COMMIT

   T2:   SELECT * FROM t1 WHERE a=2       # S2: (2,2) ? (2,12) ?
   T2:   UPDATE t1 SET b=-1 WHERE b>=10   # U1: update the row?
   T2:   SELECT * FROM t1 WHERE a=2       # S3: (2,2) ? (2,-1) ?
   T2:   COMMIT
>
If the WHERE clause in T2 had been b=2 instead of b>=10, what is the
expected outcome? Does MySQL or MariaDB even have an error code for
that? I think that this would be a perfect use of https://dbfiddle.uk.

Under SI, T2 always operates on the snapshot it took before T3 began, and observes none of T3's effects. T2 updates row 2 to (2, -1), and aborts. Why? First-committer-wins identifies that a new version of row 2 was committed in the interval between T2's snapshot and commit timestamp. T2 must abort to prevent lost update.

Under RR, I thiiiink there's the same freedom of choice--T2 is executing a predicate write, and those are (broadly speaking) unconstrained in RR. There might be something here about predicate wr/ww dependencies vs predicate rw anti-dependencies; I'd have to stare at this one for a while. Predicates are *weird*.

Just to take predicate confusion out of the mix, here's the same thing with primary-key access (assuming a is the primary key).

  T1: BEGIN
  T1: INSERT INTO t1 VALUES (1,1), (2,2), (3,3)
  T1: COMMIT

  T2:   BEGIN
  T2:   SELECT * FROM t1 where a=2;      # S1: (2,2)

  T3:     BEGIN
  T3:     UPDATE t1 SET b=12 where a=2
  T3:     COMMIT

  T2:   SELECT * FROM t1 WHERE a=2       # S2: (2,2)
  T2:   UPDATE t1 SET b=-1 WHERE a=2     # U1: Either update or abort
  T2:   SELECT * FROM t1 WHERE a=2       # S3: (2,-1)
  T2:   COMMIT # Must abort, if it hasn't already

This is what would happen under both snapshot isolation and repeatable read. Under RR, you have a choice of aborting as soon as T2 updates, or at commit time, but ultimately T2 *must not commit*. Doing so would cause lost update.

Here's what MySQL does:

  T2:   SELECT * FROM t1 WHERE a=2       # S2: (2,2)
  T2:   UPDATE t1 SET b=-1 WHERE a=2     # U1: Updates row to (2, -1)
  T2:   SELECT * FROM t1 WHERE a=2       # S3: (2,-1)
  T2:   COMMIT # Commits

This is the canonical definition of lost update--it's forbidden both by RR and SI, but RR lets it happen!

--Kyle
_______________________________________________
discuss mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to