I am writing a talk about the lock manager for PG Open and I would like
suggestions on how to improve a query in my talk.  The query creates a
lockinfo_hierarchy view of a recursive query on other views.  The output
shows the locks held and the locks being waited for:

        \! psql -e -c 'SELECT * FROM lockinfo_hierarchy;' | sed 's/^/\t/g'
        SELECT * FROM lockinfo_hierarchy;
         ?column? |  pid  |  vxid  | granted | xid_lock |   lock_type   | 
relname  | page | tuple
        
----------+-------+--------+---------+----------+---------------+----------+------+-------
         1        | 24860 | 2/3106 | t       | 828      | transactionid |       
   |      |
         1        | 24864 | 3/42   | t       | 829      | transactionid |       
   |      |
         1        | 24868 | 4/78   | t       | 830      | transactionid |       
   |      |
         1        | 24872 | 5/22   | t       | 831      | transactionid |       
   |      |
         2        | 24864 | 3/42   | f       | 828      | transactionid |       
   |      |
         3        | 24864 | 3/42   | t       |          | tuple         | 
lockdemo |    0 |     1
         4        | 24868 | 4/78   | f       |          | tuple         | 
lockdemo |    0 |     1
         4        | 24872 | 5/22   | f       |          | tuple         | 
lockdemo |    0 |     1
        (8 rows)

The SQL needed to reproduce this output is attached, and must be run
in your personal database, e.g. postgres.

What this output shows are four transactions holding locks on their own
xids, transaction 3/42 waiting for 828 to complete, and 3/42 holding a
row lock that 4/78 and 5/22 are waiting on.

When there are multiple waiters, one transaction waits on the real xid
and the others sleep waiting to be woken up later.

Is there any better way to show this?  (The first column is just there
for debugging so you can see what part of the query generated the row.)

-- 
  Bruce Momjian  <br...@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + It's impossible for everything to be true. +
-- cannot be a temporary view because other sessions must see it
DROP VIEW IF EXISTS lockview CASCADE;

CREATE VIEW lockview AS
SELECT  pid, virtualtransaction AS vxid, locktype AS lock_type, 
        mode AS lock_mode, granted,
        CASE
                WHEN virtualxid IS NOT NULL AND transactionid IS NOT NULL
                THEN    virtualxid || ' ' || transactionid
                WHEN virtualxid::text IS NOT NULL
                THEN    virtualxid
                ELSE    transactionid::text
        END AS xid_lock, relname,
        page, tuple, classid, objid, objsubid
FROM    pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)
WHERE   -- do not show our view's locks
        pid != pg_backend_pid() AND
        -- no need to show self-vxid locks
        virtualtransaction IS DISTINCT FROM virtualxid
-- granted is ordered earlier
ORDER BY 1, 2, 5 DESC, 6, 3, 4, 7;

DROP VIEW IF EXISTS lockview1 CASCADE;

CREATE VIEW lockview1 AS
SELECT  pid, vxid, lock_type, lock_mode, granted, xid_lock, relname
FROM    lockview
-- granted is ordered earlier
ORDER BY 1, 2, 5 DESC, 6, 3, 4, 7;

DROP VIEW IF EXISTS lockview2 CASCADE;

CREATE VIEW lockview2 AS
SELECT  pid, vxid, lock_type, page, tuple, classid, objid, objsubid
FROM    lockview
-- granted is first
-- add non-display columns to match ordering of lockview
ORDER BY 1, 2, granted DESC, vxid, xid_lock::text, 3, 4, 5, 6, 7, 8;

DROP TABLE IF EXISTS lockdemo;
CREATE TABLE lockdemo (col int);
INSERT INTO lockdemo VALUES (1);
-- do two UPDATEs to cause a wait

CREATE VIEW lockinfo_hierarchy AS
        WITH RECURSIVE lockinfo1 AS (
                SELECT '1', pid, vxid, granted, xid_lock, lock_type, relname, 
page, tuple
                FROM lockview
                WHERE xid_lock IS NOT NULL AND
                      relname IS NULL AND
                      granted
                UNION ALL
                SELECT '2', lockview.pid, lockview.vxid, lockview.granted, 
lockview.xid_lock, 
                        lockview.lock_type, lockview.relname, lockview.page, 
lockview.tuple
                FROM lockinfo1 JOIN lockview ON (lockinfo1.xid_lock = 
lockview.xid_lock)
                WHERE lockview.xid_lock IS NOT NULL AND
                      lockview.relname IS NULL AND
                      NOT lockview.granted AND
                      lockinfo1.granted),
        lockinfo2 AS (
                SELECT '3', pid, vxid, granted, xid_lock, lock_type, relname, 
page, tuple
                FROM lockview
                WHERE lock_type = 'tuple' AND
                      granted
                UNION ALL
                SELECT '4', lockview.pid, lockview.vxid, lockview.granted, 
lockview.xid_lock,
                        lockview.lock_type, lockview.relname, lockview.page, 
lockview.tuple
                FROM lockinfo2 JOIN lockview ON (
                        lockinfo2.lock_type = lockview.lock_type AND
                        lockinfo2.relname = lockview.relname AND
                        lockinfo2.page = lockview.page AND
                        lockinfo2.tuple = lockview.tuple)
                WHERE lockview.lock_type = 'tuple' AND
                      NOT lockview.granted AND
                      lockinfo2.granted
        )
        SELECT * FROM lockinfo1
        UNION ALL
        SELECT * FROM lockinfo2;

-- try several updates
BEGIN WORK;
SELECT ctid, xmin, * FROM lockdemo;
UPDATE lockdemo SET col = 4;
SELECT ctid, xmin, * FROM lockdemo;
SELECT pg_backend_pid();
SELECT txid_current();
\! psql -e -c 'BEGIN WORK; UPDATE lockdemo SET col = 5; SELECT pg_sleep(0.300); 
COMMIT;' | sed 's/^/\t/g' &
\! psql -e -c 'BEGIN WORK; UPDATE lockdemo SET col = 6; SELECT pg_sleep(0.300); 
COMMIT;' | sed 's/^/\t/g' &
\! psql -e -c 'BEGIN WORK; UPDATE lockdemo SET col = 7; SELECT pg_sleep(0.300); 
COMMIT;' | sed 's/^/\t/g' &
SELECT pg_sleep(0.100);
\! psql -e -c 'SELECT * FROM lockview1;' | sed 's/^/\t/g'
\! psql -e -c 'SELECT * FROM lockview2;' | sed 's/^/\t/g'
\! psql -e -c 'SELECT * FROM lockinfo_hierarchy;' | sed 's/^/\t/g'
\! psql -e -c 'SELECT * FROM pg_locks;' | sed 's/^/\t/g'
COMMIT;
\! psql -e -c 'SELECT * FROM lockview1;' | sed 's/^/\t/g'
\! psql -e -c 'SELECT * FROM lockview2;' | sed 's/^/\t/g'
SELECT pg_sleep(0.300);

DELETE FROM lockdemo;
INSERT INTO lockdemo VALUES (1);

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to