In August 2010, I sent an email to pghackers reporting a strange deadlock problem.
I couldn’t understand how it was possible you could get a deadlock by updating
the very same row again later in the same transaction.

http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg157869.html

I also made a screencast demonstrating the problem:

http://www.screencast.com/users/joeljacobson/folders/Jing/media/42c31028-80fa-45fe-b21f-9039110c3555

Simon Riggs came up with a very clever solution to this problem.
By introducing a new type of lock, it would be possible to avoid a lot of deadlocks.
Álvaro Herrera has since then been working on the implementation.
It turned out to be a lot of work, unfortunately the feature didn’t make it into 9.2.
I bet this will be one of the most important performance features in 9.3.
It will greatly improve the performance by increasing the concurrency possible if you have a high transaction load.

I thought it would be fun to test my old example from 2010 using the latest version of the patch.

Let’s checkout Álvaro’s latest version of the fklocks patch:

git remote add fklocks git://github.com/alvherre/postgres.git
git fetch fklocks
git checkout fklocks

CREATE TABLE A (
AID integer not null,
Col1 integer,
PRIMARY KEY (AID)
);

CREATE TABLE B (
BID integer not null,
AID integer not null,
Col2 integer,
PRIMARY KEY (BID),
FOREIGN KEY (AID) REFERENCES A(AID)
);

INSERT INTO A (AID) VALUES (1);
INSERT INTO B (BID,AID) VALUES (2,1);

-- Process 1:
BEGIN;
    -- Process 2:
    BEGIN;

UPDATE A SET Col1 = 1 WHERE AID = 1;

    UPDATE B SET Col2 = 2 WHERE BID = 2;

UPDATE B SET Col2 = 3 WHERE BID = 2;
-- Process 1 is waiting

    UPDATE B SET Col2 = 4 WHERE BID = 2;
    -- Process 2 is allowed to update the same row
    COMMIT;

COMMIT;

SELECT * FROM A;
 aid | col1 
-----+------
   1 |    1
(1 row)

SELECT * FROM B;
 bid | aid | col2 
-----+-----+------
   2 |   1 |    3
(1 row)

Both transactions were able to COMMIT successfully, hurray!

Without this patch, you would get a “deadlock detected” on row 30,
when Process 2 tries to update the same row again, BID = 2.

However, I was a bit surprised the final value of Col2 is 3 and not 4,
as Process 2 updated the row BID = 2 before Process 1,
and Process 2 updated the row again after Process 1.
This probably has a logical explanation.

I hope to have time to do some concurrency stress testing using
example code and data extracted from our production database.

Would be interesting to see if my old read-life deadlock scenarios
are fixed by this patch and how it affects concurrency and performance.