ssi-readme-tuple-lock.patch

text/plain

Filename: ssi-readme-tuple-lock.patch
Type: text/plain
Part: 0
Message: Re: SSI predicate locking on heap -- tuple or row?

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/storage/lmgr/README-SSI 48 0
*** a/src/backend/storage/lmgr/README-SSI
--- b/src/backend/storage/lmgr/README-SSI
***************
*** 402,407 **** is based on the top level xid.  When looking at an xid that comes
--- 402,455 ----
  from a tuple's xmin or xmax, for example, we always call
  SubTransGetTopmostTransaction() before doing much else with it.
  
+     * PostgreSQL does not use "update in place" with a rollback log
+ for its MVCC implementation.  Where possible it uses "HOT" updates on
+ the same page (if there is room and no indexed value is changed).
+ For non-HOT updates the old tuple is expired in place and a new tuple
+ is inserted at a new location.  Because of this difference, a tuple
+ lock in PostgreSQL doesn't automatically lock any other versions of a
+ row.  We don't try to copy or expand a tuple lock to any other
+ versions of the row, based on the following proof that any additional
+ serialization failures we would get from that would be false
+ positives:
+ 
+           o If transaction T1 reads a row (thus acquiring a predicate
+ lock on it) and a second transaction T2 updates that row, must a
+ third transaction T3 which updates the new version of the row have a
+ rw-conflict in from T1 to prevent anomalies?  In other words, does it
+ matter whether this edge T1 -> T3 is there?
+ 
+           o If T1 has a conflict in, it certainly doesn't. Adding the
+ edge T1 -> T3 would create a dangerous structure, but we already had
+ one from the edge T1 -> T2, so we would have aborted something
+ anyway.
+ 
+           o Now let's consider the case where T1 doesn't have a
+ conflict in. If that's the case, for this edge T1 -> T3 to make a
+ difference, T3 must have a rw-conflict out that induces a cycle in
+ the dependency graph, i.e. a conflict out to some transaction
+ preceding T1 in the serial order. (A conflict out to T1 would work
+ too, but that would mean T1 has a conflict in and we would have
+ rolled back.)
+ 
+           o So now we're trying to figure out if there can be an
+ rw-conflict edge T3 -> T0, where T0 is some transaction that precedes
+ T1. For T0 to precede T1, there has to be has to be some edge, or
+ sequence of edges, from T0 to T1. At least the last edge has to be a
+ wr-dependency or ww-dependency rather than a rw-conflict, because T1
+ doesn't have a rw-conflict in. And that gives us enough information
+ about the order of transactions to see that T3 can't have a
+ rw-dependency to T0:
+  - T0 committed before T1 started (the wr/ww-dependency implies this)
+  - T1 started before T2 committed (the T1->T2 rw-conflict implies this)
+  - T2 committed before T3 started (otherwise, T3 would be aborted
+                                    because of an update conflict)
+ 
+           o That means T0 committed before T3 started, and therefore
+ there can't be a rw-conflict from T3 to T0.
+ 
+           o In both cases, we didn't need the T1 -> T3 edge.
+ 
      * Predicate locking in PostgreSQL will start at the tuple level
  when possible, with automatic conversion of multiple fine-grained
  locks to coarser granularity as need to avoid resource exhaustion.