agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Bertrand Drouvot <[email protected]>
Subject: [PATCH v7 2/3] Add the pg_stat_lock view
Date: Thu, 31 Jul 2025 09:35:31 +0000
This new view reports lock statistics.
Note that it does not omit combinations which do not make sense (as pg_locks
does).
Also wait_time is reported as bigint as the deadlock_timeout default value is
1s.
This commit also adds documentation and a few tests.
XXX: Bump catversion
---
doc/src/sgml/monitoring.sgml | 125 +++++++++++++++
src/backend/catalog/system_views.sql | 10 ++
src/backend/utils/activity/pgstat_lock.c | 8 +
src/backend/utils/adt/pgstatfuncs.c | 40 +++++
src/include/catalog/pg_proc.dat | 9 ++
src/include/pgstat.h | 1 +
src/test/isolation/expected/stats.out | 189 +++++++++++++++++++++++
src/test/isolation/expected/stats_1.out | 189 +++++++++++++++++++++++
src/test/isolation/specs/stats.spec | 95 ++++++++++++
src/test/regress/expected/rules.out | 7 +
src/test/regress/expected/stats.out | 48 ++++++
src/test/regress/sql/stats.sql | 36 +++++
12 files changed, 757 insertions(+)
16.7% doc/src/sgml/
5.0% src/backend/utils/adt/
53.6% src/test/isolation/expected/
11.1% src/test/isolation/specs/
5.6% src/test/regress/expected/
3.9% src/test/regress/sql/
3.7% src/
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b77d189a500..cfcb8189418 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -493,6 +493,15 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</entry>
</row>
+ <row>
+ <entry><structname>pg_stat_lock</structname><indexterm><primary>pg_stat_lock</primary></indexterm></entry>
+ <entry>
+ One row for each lock type, containing cluster-wide locks statistics.
+ See <link linkend="monitoring-pg-stat-lock-view">
+ <structname>pg_stat_lock</structname></link> for details.
+ </entry>
+ </row>
+
<row>
<entry><structname>pg_stat_replication_slots</structname><indexterm><primary>pg_stat_replication_slots</primary></indexterm></entry>
<entry>One row per replication slot, showing statistics about the
@@ -3124,6 +3133,116 @@ description | Waiting for a newly initialized WAL file to reach durable storage
</sect2>
+
+ <sect2 id="monitoring-pg-stat-lock-view">
+ <title><structname>pg_stat_lock</structname></title>
+
+ <indexterm>
+ <primary>pg_stat_lock</primary>
+ </indexterm>
+
+ <para>
+ The <structname>pg_stat_lock</structname> view will contain one row for each
+ lock type, showing cluster-wide locks statistics.
+ </para>
+
+ <table id="pg-stat-lock-view" xreflabel="pg_stat_lock">
+ <title><structname>pg_stat_lock</structname> View</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para>
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>locktype</structfield> <type>text</type>
+ </para>
+ <para>
+ Type of the lockable object. See <link linkend="view-pg-locks">
+ <structname>pg_locks</structname></link> for details.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>waits</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times a lock of this type had to wait because of a
+ conflicting lock.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>timed_waits</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times a lock of this type had to wait because of a
+ conflicting lock. Only incremented when <xref linkend="guc-log-lock-waits"/>
+ is enabled and the lock was successfully acquired after waiting longer
+ than <xref linkend="guc-deadlock-timeout"/>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>wait_time</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Total time spent waiting for locks of this type, in milliseconds.
+ Only incremented when <xref linkend="guc-log-lock-waits"/> is enabled and
+ the lock was successfully acquired after waiting longer than
+ <xref linkend="guc-deadlock-timeout"/>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>fastpath_exceeded</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Number of times a lock of this type could not be acquired via fast path
+ because the fast path slot limit was exceeded. You may want to increase
+ <xref linkend="guc-max-locks-per-transaction"/> if you feel this counter
+ is too high.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry">
+ <para role="column_definition">
+ <structfield>stats_reset</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Time at which these statistics were last reset.
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+
<sect2 id="monitoring-pg-stat-bgwriter-view">
<title><structname>pg_stat_bgwriter</structname></title>
@@ -5195,6 +5314,12 @@ description | Waiting for a newly initialized WAL file to reach durable storage
<structname>pg_stat_io</structname> view.
</para>
</listitem>
+ <listitem>
+ <para>
+ <literal>lock</literal>: Reset all the counters shown in the
+ <structname>pg_stat_lock</structname> view.
+ </para>
+ </listitem>
<listitem>
<para>
<literal>recovery_prefetch</literal>: Reset all the counters shown in
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 1ea8f1faa9e..dcf12a202e0 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -985,6 +985,16 @@ CREATE VIEW pg_stat_slru AS
s.stats_reset
FROM pg_stat_get_slru() s;
+CREATE VIEW pg_stat_lock AS
+ SELECT
+ l.locktype,
+ l.waits,
+ l.timed_waits,
+ l.wait_time,
+ l.fastpath_exceeded,
+ l.stats_reset
+ FROM pg_stat_get_lock() l;
+
CREATE VIEW pg_stat_wal_receiver AS
SELECT
s.pid,
diff --git a/src/backend/utils/activity/pgstat_lock.c b/src/backend/utils/activity/pgstat_lock.c
index b410f376d49..451fba4f8d9 100644
--- a/src/backend/utils/activity/pgstat_lock.c
+++ b/src/backend/utils/activity/pgstat_lock.c
@@ -21,6 +21,14 @@
static PgStat_PendingLock PendingLockStats;
static bool have_lockstats = false;
+PgStat_Lock *
+pgstat_fetch_stat_lock(void)
+{
+ pgstat_snapshot_fixed(PGSTAT_KIND_LOCK);
+
+ return &pgStatLocal.snapshot.lock;
+}
+
/*
* Simpler wrapper of pgstat_lock_flush_cb()
*/
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index b1df96e7b0b..3b7cbdb82be 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1737,6 +1737,43 @@ pg_stat_get_wal(PG_FUNCTION_ARGS)
wal_stats->stat_reset_timestamp));
}
+Datum
+pg_stat_get_lock(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_LOCK_COLS 6
+ ReturnSetInfo *rsinfo;
+ PgStat_Lock *lock_stats;
+
+ InitMaterializedSRF(fcinfo, 0);
+ rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ lock_stats = pgstat_fetch_stat_lock();
+
+ for (int lcktype = 0; lcktype <= LOCKTAG_LAST_TYPE; lcktype++)
+ {
+ const char *locktypename;
+ Datum values[PG_STAT_LOCK_COLS] = {0};
+ bool nulls[PG_STAT_LOCK_COLS] = {0};
+ PgStat_LockEntry *lck_stats = &lock_stats->stats[lcktype];
+ int i = 0;
+
+ locktypename = LockTagTypeNames[lcktype];
+
+ values[i++] = CStringGetTextDatum(locktypename);
+ values[i++] = Int64GetDatum(lck_stats->waits);
+ values[i++] = Int64GetDatum(lck_stats->timed_waits);
+ values[i++] = Int64GetDatum(lck_stats->wait_time);
+ values[i++] = Int64GetDatum(lck_stats->fastpath_exceeded);
+ values[i] = TimestampTzGetDatum(lock_stats->stat_reset_timestamp);
+
+ Assert(i + 1 == PG_STAT_LOCK_COLS);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
+
/*
* Returns statistics of SLRU caches.
*/
@@ -1921,6 +1958,7 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS)
pgstat_reset_of_kind(PGSTAT_KIND_BGWRITER);
pgstat_reset_of_kind(PGSTAT_KIND_CHECKPOINTER);
pgstat_reset_of_kind(PGSTAT_KIND_IO);
+ pgstat_reset_of_kind(PGSTAT_KIND_LOCK);
XLogPrefetchResetStats();
pgstat_reset_of_kind(PGSTAT_KIND_SLRU);
pgstat_reset_of_kind(PGSTAT_KIND_WAL);
@@ -1938,6 +1976,8 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS)
pgstat_reset_of_kind(PGSTAT_KIND_CHECKPOINTER);
else if (strcmp(target, "io") == 0)
pgstat_reset_of_kind(PGSTAT_KIND_IO);
+ else if (strcmp(target, "lock") == 0)
+ pgstat_reset_of_kind(PGSTAT_KIND_LOCK);
else if (strcmp(target, "recovery_prefetch") == 0)
XLogPrefetchResetStats();
else if (strcmp(target, "slru") == 0)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index dac40992cbc..3cd497a8e1f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6027,6 +6027,15 @@
proargnames => '{backend_type,object,context,reads,read_bytes,read_time,writes,write_bytes,write_time,writebacks,writeback_time,extends,extend_bytes,extend_time,hits,evictions,reuses,fsyncs,fsync_time,stats_reset}',
prosrc => 'pg_stat_get_io' },
+{ oid => '9375', descr => 'statistics: per lock type statistics',
+ proname => 'pg_stat_get_lock', prorows => '10', proretset => 't',
+ provolatile => 'v', proparallel => 'r', prorettype => 'record',
+ proargtypes => '',
+ proallargtypes => '{text,int8,int8,int8,int8,timestamptz}',
+ proargmodes => '{o,o,o,o,o,o}',
+ proargnames => '{locktype,waits,timed_waits,wait_time,fastpath_exceeded,stats_reset}',
+ prosrc => 'pg_stat_get_lock' },
+
{ oid => '6386', descr => 'statistics: backend IO statistics',
proname => 'pg_stat_get_backend_io', prorows => '5', proretset => 't',
provolatile => 'v', proparallel => 'r', prorettype => 'record',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 9c4508d5544..a4cc006794d 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -641,6 +641,7 @@ extern void pgstat_lock_flush(bool nowait);
extern void pgstat_count_lock_waits(uint8 locktag_type);
extern void pgstat_count_lock_fastpath_exceeded(uint8 locktag_type);
extern void pgstat_count_lock_timed_wait(uint8 locktag_type, long msecs);
+extern PgStat_Lock *pgstat_fetch_stat_lock(void);
/*
* Functions in pgstat_database.c
diff --git a/src/test/isolation/expected/stats.out b/src/test/isolation/expected/stats.out
index cfad309ccf3..86d70a0705c 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -3751,3 +3751,192 @@ test_stat_func| 1|t |t
(1 row)
step s1_commit: COMMIT;
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_begin s1_lock_relation s2_begin s2_ff s2_lock_relation s1_sleep s1_commit s2_commit s2_report_stat_lock_relation
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_begin: BEGIN;
+step s1_lock_relation: LOCK TABLE test_stat_tab;
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_relation: LOCK TABLE test_stat_tab; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_lock_relation: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_relation: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'relation';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_table_insert s1_begin s1_table_update_k1 s2_begin s2_ff s2_table_update_k1 s1_sleep s1_commit s2_commit s2_report_stat_lock_transactionid
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_table_insert: INSERT INTO test_stat_tab(key, value) VALUES('k1', 1), ('k2', 1), ('k3', 1);
+step s1_begin: BEGIN;
+step s1_table_update_k1: UPDATE test_stat_tab SET value = value + 1 WHERE key = 'k1';
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_table_update_k1: UPDATE test_stat_tab SET value = value + 1 WHERE key = 'k1'; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_table_update_k1: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_transactionid: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'transactionid';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_lock_advisory_lock s2_begin s2_ff s2_lock_advisory_lock s1_sleep s1_lock_advisory_unlock s2_lock_advisory_unlock s2_commit s2_report_stat_lock_advisory
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_lock_advisory_lock: SELECT pg_advisory_lock(1);
+pg_advisory_lock
+----------------
+
+(1 row)
+
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_advisory_lock: SELECT pg_advisory_lock(1); <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_lock_advisory_unlock: SELECT pg_advisory_unlock(1);
+pg_advisory_unlock
+------------------
+t
+(1 row)
+
+step s2_lock_advisory_lock: <... completed>
+pg_advisory_lock
+----------------
+
+(1 row)
+
+step s2_lock_advisory_unlock: SELECT pg_advisory_unlock(1);
+pg_advisory_unlock
+------------------
+t
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_report_stat_lock_advisory: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'advisory';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_unset_log_lock_waits s1_begin s1_lock_relation s2_begin s2_ff s2_lock_relation s1_sleep s1_commit s2_commit s2_report_stat_lock_relation
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_unset_log_lock_waits: SET log_lock_waits = off;
+step s1_begin: BEGIN;
+step s1_lock_relation: LOCK TABLE test_stat_tab;
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_relation: LOCK TABLE test_stat_tab; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_lock_relation: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_relation: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'relation';
+?column?|?column?|?column?
+--------+--------+--------
+t |f |f
+(1 row)
+
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index e1d937784cb..4e4c68b4525 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -3775,3 +3775,192 @@ test_stat_func| 1|t |t
(1 row)
step s1_commit: COMMIT;
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_begin s1_lock_relation s2_begin s2_ff s2_lock_relation s1_sleep s1_commit s2_commit s2_report_stat_lock_relation
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_begin: BEGIN;
+step s1_lock_relation: LOCK TABLE test_stat_tab;
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_relation: LOCK TABLE test_stat_tab; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_lock_relation: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_relation: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'relation';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_table_insert s1_begin s1_table_update_k1 s2_begin s2_ff s2_table_update_k1 s1_sleep s1_commit s2_commit s2_report_stat_lock_transactionid
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_table_insert: INSERT INTO test_stat_tab(key, value) VALUES('k1', 1), ('k2', 1), ('k3', 1);
+step s1_begin: BEGIN;
+step s1_table_update_k1: UPDATE test_stat_tab SET value = value + 1 WHERE key = 'k1';
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_table_update_k1: UPDATE test_stat_tab SET value = value + 1 WHERE key = 'k1'; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_table_update_k1: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_transactionid: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'transactionid';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_set_log_lock_waits s1_lock_advisory_lock s2_begin s2_ff s2_lock_advisory_lock s1_sleep s1_lock_advisory_unlock s2_lock_advisory_unlock s2_commit s2_report_stat_lock_advisory
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_set_log_lock_waits: SET log_lock_waits = on;
+step s1_lock_advisory_lock: SELECT pg_advisory_lock(1);
+pg_advisory_lock
+----------------
+
+(1 row)
+
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_advisory_lock: SELECT pg_advisory_lock(1); <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_lock_advisory_unlock: SELECT pg_advisory_unlock(1);
+pg_advisory_unlock
+------------------
+t
+(1 row)
+
+step s2_lock_advisory_lock: <... completed>
+pg_advisory_lock
+----------------
+
+(1 row)
+
+step s2_lock_advisory_unlock: SELECT pg_advisory_unlock(1);
+pg_advisory_unlock
+------------------
+t
+(1 row)
+
+step s2_commit: COMMIT;
+step s2_report_stat_lock_advisory: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'advisory';
+?column?|?column?|?column?
+--------+--------+--------
+t |t |t
+(1 row)
+
+
+starting permutation: s1_set_deadlock_timeout s1_reset_stat_lock s1_set_log_lock_waits s2_set_deadlock_timeout s2_unset_log_lock_waits s1_begin s1_lock_relation s2_begin s2_ff s2_lock_relation s1_sleep s1_commit s2_commit s2_report_stat_lock_relation
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s1_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s1_reset_stat_lock: SELECT pg_stat_reset_shared('lock');
+pg_stat_reset_shared
+--------------------
+
+(1 row)
+
+step s1_set_log_lock_waits: SET log_lock_waits = on;
+step s2_set_deadlock_timeout: SET deadlock_timeout = '10ms';
+step s2_unset_log_lock_waits: SET log_lock_waits = off;
+step s1_begin: BEGIN;
+step s1_lock_relation: LOCK TABLE test_stat_tab;
+step s2_begin: BEGIN;
+step s2_ff: SELECT pg_stat_force_next_flush();
+pg_stat_force_next_flush
+------------------------
+
+(1 row)
+
+step s2_lock_relation: LOCK TABLE test_stat_tab; <waiting ...>
+step s1_sleep: SELECT pg_sleep(0.5);
+pg_sleep
+--------
+
+(1 row)
+
+step s1_commit: COMMIT;
+step s2_lock_relation: <... completed>
+step s2_commit: COMMIT;
+step s2_report_stat_lock_relation: SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'relation';
+?column?|?column?|?column?
+--------+--------+--------
+t |f |f
+(1 row)
+
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index da16710da0f..d4b53545198 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -130,6 +130,14 @@ step s1_slru_check_stats {
WHERE before.stat = 'blks_zeroed';
}
+# Lock stats steps
+step s1_set_deadlock_timeout { SET deadlock_timeout = '10ms'; }
+step s1_set_log_lock_waits { SET log_lock_waits = on; }
+step s1_reset_stat_lock { SELECT pg_stat_reset_shared('lock'); }
+step s1_sleep { SELECT pg_sleep(0.5); }
+step s1_lock_relation { LOCK TABLE test_stat_tab; }
+step s1_lock_advisory_lock { SELECT pg_advisory_lock(1); }
+step s1_lock_advisory_unlock { SELECT pg_advisory_unlock(1); }
session s2
setup { SET stats_fetch_consistency = 'none'; }
@@ -164,6 +172,16 @@ step s2_big_notify { SELECT pg_notify('stats_test_use',
repeat(i::text, current_setting('block_size')::int / 2)) FROM generate_series(1, 3) g(i);
}
+# Lock stats steps
+step s2_set_deadlock_timeout { SET deadlock_timeout = '10ms'; }
+step s2_set_log_lock_waits { SET log_lock_waits = on; }
+step s2_unset_log_lock_waits { SET log_lock_waits = off; }
+step s2_report_stat_lock_relation { SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'relation'; }
+step s2_report_stat_lock_transactionid { SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'transactionid'; }
+step s2_report_stat_lock_advisory { SELECT waits > 0, timed_waits = waits, wait_time > 500 FROM pg_stat_lock WHERE locktype = 'advisory'; }
+step s2_lock_relation { LOCK TABLE test_stat_tab; }
+step s2_lock_advisory_lock { SELECT pg_advisory_lock(1); }
+step s2_lock_advisory_unlock { SELECT pg_advisory_unlock(1); }
######################
# Function stats tests
@@ -765,3 +783,80 @@ permutation
s1_clear_snapshot
s1_func_stats
s1_commit
+
+######################
+# Lock stats tests
+######################
+
+# relation lock
+
+permutation
+ s1_set_deadlock_timeout
+ s1_reset_stat_lock
+ s1_set_log_lock_waits
+ s2_set_deadlock_timeout
+ s2_set_log_lock_waits
+ s1_begin
+ s1_lock_relation
+ s2_begin
+ s2_ff
+ s2_lock_relation
+ s1_sleep
+ s1_commit
+ s2_commit
+ s2_report_stat_lock_relation
+
+# transaction lock
+
+permutation
+ s1_set_deadlock_timeout
+ s1_reset_stat_lock
+ s1_set_log_lock_waits
+ s2_set_deadlock_timeout
+ s2_set_log_lock_waits
+ s1_table_insert
+ s1_begin
+ s1_table_update_k1
+ s2_begin
+ s2_ff
+ s2_table_update_k1
+ s1_sleep
+ s1_commit
+ s2_commit
+ s2_report_stat_lock_transactionid
+
+# advisory lock
+
+permutation
+ s1_set_deadlock_timeout
+ s1_reset_stat_lock
+ s1_set_log_lock_waits
+ s2_set_deadlock_timeout
+ s2_set_log_lock_waits
+ s1_lock_advisory_lock
+ s2_begin
+ s2_ff
+ s2_lock_advisory_lock
+ s1_sleep
+ s1_lock_advisory_unlock
+ s2_lock_advisory_unlock
+ s2_commit
+ s2_report_stat_lock_advisory
+
+# Ensure log_lock_waits behaves correctly
+
+permutation
+ s1_set_deadlock_timeout
+ s1_reset_stat_lock
+ s1_set_log_lock_waits
+ s2_set_deadlock_timeout
+ s2_unset_log_lock_waits
+ s1_begin
+ s1_lock_relation
+ s2_begin
+ s2_ff
+ s2_lock_relation
+ s1_sleep
+ s1_commit
+ s2_commit
+ s2_report_stat_lock_relation
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 78a37d9fc8f..2fcb48df6c9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1951,6 +1951,13 @@ pg_stat_io| SELECT backend_type,
fsync_time,
stats_reset
FROM pg_stat_get_io() b(backend_type, object, context, reads, read_bytes, read_time, writes, write_bytes, write_time, writebacks, writeback_time, extends, extend_bytes, extend_time, hits, evictions, reuses, fsyncs, fsync_time, stats_reset);
+pg_stat_lock| SELECT locktype,
+ waits,
+ timed_waits,
+ wait_time,
+ fastpath_exceeded,
+ stats_reset
+ FROM pg_stat_get_lock() l(locktype, waits, timed_waits, wait_time, fastpath_exceeded, stats_reset);
pg_stat_progress_analyze| SELECT s.pid,
s.datid,
d.datname,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index cd00f35bf7a..93de87747d4 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -1910,4 +1910,52 @@ SELECT * FROM check_estimated_rows('SELECT * FROM table_fillfactor');
(1 row)
DROP TABLE table_fillfactor;
+-- Test fastpath_exceeded stat
+CREATE TABLE part_test (id int) PARTITION BY RANGE (id);
+SELECT pg_stat_reset_shared('lock');
+ pg_stat_reset_shared
+----------------------
+
+(1 row)
+
+-- Create partitions (exceeds number of slots)
+DO $$
+DECLARE
+ max_locks int;
+BEGIN
+ SELECT setting::int INTO max_locks
+ FROM pg_settings
+ WHERE name = 'max_locks_per_transaction';
+
+ FOR i IN 1..(max_locks + 10) LOOP
+ EXECUTE format(
+ 'CREATE TABLE part_test_%s PARTITION OF part_test
+ FOR VALUES FROM (%s) TO (%s)',
+ i, (i-1)*1000, i*1000
+ );
+ END LOOP;
+END;
+$$;
+SELECT fastpath_exceeded AS fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation' \gset
+-- Needs a lock on each partition
+SELECT count(*) FROM part_test;
+ count
+-------
+ 0
+(1 row)
+
+-- Ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+SELECT fastpath_exceeded > :fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation';
+ ?column?
+----------
+ t
+(1 row)
+
+DROP TABLE part_test;
-- End of Stats Test
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 8768e0f27fd..db9948eedb8 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -944,4 +944,40 @@ SELECT * FROM check_estimated_rows('SELECT * FROM table_fillfactor');
DROP TABLE table_fillfactor;
+-- Test fastpath_exceeded stat
+CREATE TABLE part_test (id int) PARTITION BY RANGE (id);
+
+SELECT pg_stat_reset_shared('lock');
+
+-- Create partitions (exceeds number of slots)
+DO $$
+DECLARE
+ max_locks int;
+BEGIN
+ SELECT setting::int INTO max_locks
+ FROM pg_settings
+ WHERE name = 'max_locks_per_transaction';
+
+ FOR i IN 1..(max_locks + 10) LOOP
+ EXECUTE format(
+ 'CREATE TABLE part_test_%s PARTITION OF part_test
+ FOR VALUES FROM (%s) TO (%s)',
+ i, (i-1)*1000, i*1000
+ );
+ END LOOP;
+END;
+$$;
+
+SELECT fastpath_exceeded AS fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation' \gset
+
+-- Needs a lock on each partition
+SELECT count(*) FROM part_test;
+
+-- Ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+
+SELECT fastpath_exceeded > :fastpath_exceeded_before FROM pg_stat_lock WHERE locktype = 'relation';
+
+DROP TABLE part_test;
+
-- End of Stats Test
--
2.34.1
--gJ14Jcshhoq1cFBD
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v7-0003-Introduce-a-new-track_lock_timing-GUC.patch"
view thread (14+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v7 2/3] Add the pg_stat_lock view
In-Reply-To: <no-message-id-3899@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox