Hi all,

I've modified the pg_stat_lwlocks patch to be able to work with
the latest PostgreSQL Git code.

This patch provides:
  pg_stat_lwlocks           New system view to show lwlock statistics.
  pg_stat_get_lwlocks()     New function to retrieve lwlock statistics.
  pg_stat_reset_lwlocks()   New function to reset lwlock statistics.

Please try it out.

Regards,

2012/06/26 5:29, Satoshi Nagayasu wrote:
> Hi all,
> 
> I've been working on a new system view, pg_stat_lwlocks, to observe
> LWLock, and just completed my 'proof-of-concept' code that can work
> with version 9.1.
> 
> Now, I'd like to know the possibility of this feature for future
> release.
> 
> With this patch, DBA can easily determine a bottleneck around lwlocks.
> --------------------------------------------------
> postgres=# SELECT * FROM pg_stat_lwlocks ORDER BY time_ms DESC LIMIT 10;
>   lwlockid | calls  | waits | time_ms
> ----------+--------+-------+---------
>         49 | 193326 | 32096 |   23688
>          8 |   3305 |   133 |    1335
>          2 |     21 |     0 |       0
>          4 | 135188 |     0 |       0
>          5 |  57935 |     0 |       0
>          6 |    141 |     0 |       0
>          7 |  24580 |     1 |       0
>          3 |   3282 |     0 |       0
>          1 |     41 |     0 |       0
>          9 |      3 |     0 |       0
> (10 rows)
> 
> postgres=#
> --------------------------------------------------
> 
> In this view,
>    'lwlockid' column represents LWLockId used in the backends.
>    'calls' represents how many times LWLockAcquire() was called.
>    'waits' represents how many times LWLockAcquire() needed to wait
>    within it before lock acquisition.
>    'time_ms' represents how long LWLockAcquire() totally waited on
>    a lwlock.
> 
> And lwlocks that use a LWLockId range, such as BufMappingLock or
> LockMgrLock, would be grouped and summed up in a single record.
> For example, lwlockid 49 in the above view represents LockMgrLock
> statistics.
> 
> Now, I know there are some considerations.
> 
> (1) Performance
> 
>    I've measured LWLock performance both with and without the patch,
>    and confirmed that this patch does not affect the LWLock perfomance
>    at all.
> 
>    pgbench scores with the patch:
>      tps = 900.906658 (excluding connections establishing)
>      tps = 908.528422 (excluding connections establishing)
>      tps = 903.900977 (excluding connections establishing)
>      tps = 910.470595 (excluding connections establishing)
>      tps = 909.685396 (excluding connections establishing)
> 
>    pgbench scores without the patch:
>      tps = 909.096785 (excluding connections establishing)
>      tps = 894.868712 (excluding connections establishing)
>      tps = 910.074669 (excluding connections establishing)
>      tps = 904.022770 (excluding connections establishing)
>      tps = 895.673830 (excluding connections establishing)
> 
>    Of course, this experiment was not I/O bound, and the cache hit ratio
>    was>99.9%.
> 
> (2) Memory space
> 
>    In this patch, I added three new members to LWLock structure
>    as uint64 to collect statistics.
> 
>    It means that those members must be held in the shared memory,
>    but I'm not sure whether it's appropriate.
> 
>    I think another possible option is holding those statistics
>    values in local (backend) process memory, and send them through
>    the stat collector process (like other statistics values).
> 
> (3) LWLock names (or labels)
> 
>    Now, pg_stat_lwlocks view shows LWLockId itself. But LWLockId is
>    not easy for DBA to determine actual lock type.
> 
>    So, I want to show LWLock names (or labels), like 'WALWriteLock'
>    or 'LockMgrLock', but how should I implement it?
> 
> Any comments?
> 
> Regards,


-- 
Satoshi Nagayasu <sn...@uptime.jp>
Uptime Technologies, LLC. http://www.uptime.jp
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index 7cc1d41..f832b45 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -658,6 +658,14 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_lwlocks AS
+    SELECT
+            S.lwlockid,
+            S.calls,
+            S.waits,
+            S.time_ms
+    FROM pg_stat_get_lwlocks() AS S;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/storage/lmgr/lwlock.c 
b/src/backend/storage/lmgr/lwlock.c
index 95d4b37..2a2c197 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -32,6 +32,7 @@
 #include "storage/proc.h"
 #include "storage/spin.h"
 
+#include <sys/time.h>
 
 /* We use the ShmemLock spinlock to protect LWLockAssign */
 extern slock_t *ShmemLock;
@@ -46,6 +47,11 @@ typedef struct LWLock
        PGPROC     *head;                       /* head of list of waiting 
PGPROCs */
        PGPROC     *tail;                       /* tail of list of waiting 
PGPROCs */
        /* tail is undefined when head is NULL */
+
+       /* statistics stuff */
+       uint64          calls;
+       uint64          waits;
+       uint64          time_ms;
 } LWLock;
 
 /*
@@ -287,6 +293,9 @@ CreateLWLocks(void)
                lock->lock.shared = 0;
                lock->lock.head = NULL;
                lock->lock.tail = NULL;
+               lock->lock.calls = 0;
+               lock->lock.waits = 0;
+               lock->lock.time_ms = 0;
        }
 
        /*
@@ -342,8 +351,10 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
        PGPROC     *proc = MyProc;
        bool            retry = false;
        int                     extraWaits = 0;
+       struct timeval          wait_start,wait_done;
 
        PRINT_LWDEBUG("LWLockAcquire", lockid, lock);
+       lock->calls++;
 
 #ifdef LWLOCK_STATS
        /* Set up local count state first time through in a given process */
@@ -467,6 +478,8 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
 #endif
 
                TRACE_POSTGRESQL_LWLOCK_WAIT_START(lockid, mode);
+               lock->waits++;
+               gettimeofday(&wait_start, NULL);
 
                for (;;)
                {
@@ -478,6 +491,8 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
                }
 
                TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(lockid, mode);
+               gettimeofday(&wait_done, NULL);
+               lock->time_ms += (wait_done.tv_sec-wait_start.tv_sec)*1000 + 
(wait_done.tv_usec-wait_start.tv_usec)/1000;
 
                LOG_LWDEBUG("LWLockAcquire", lockid, "awakened");
 
@@ -879,3 +894,48 @@ LWLockHeldByMe(LWLockId lockid)
        }
        return false;
 }
+
+void
+lwlock_get_stat(LWLockId lockid, uint64 *calls, uint64 *waits, uint64 *time_ms)
+{
+       volatile LWLock *lock = &(LWLockArray[lockid].lock);
+
+       SpinLockAcquire(&lock->mutex);
+       *calls   = lock->calls;
+       *waits   = lock->waits;
+       *time_ms = lock->time_ms;
+       SpinLockRelease(&lock->mutex);
+}
+
+void
+lwlock_get_stat_sum(LWLockId start, LWLockId end, uint64 *calls, uint64 
*waits, uint64 *time_ms)
+{
+       LWLockId lockid;
+
+       *calls   = 0;
+       *waits   = 0;
+       *time_ms = 0;
+
+       for (lockid=start ; lockid<=end ; lockid++)
+       {
+               uint64 calls2, waits2, time_ms2;
+
+               lwlock_get_stat(lockid, &calls2, &waits2, &time_ms2);
+
+               *calls   += calls2;
+               *waits   += waits2;
+               *time_ms += time_ms2;
+       }
+}
+
+void
+lwlock_reset_stat(LWLockId lockid)
+{
+       volatile LWLock *lock = &(LWLockArray[lockid].lock);
+
+       SpinLockAcquire(&lock->mutex);
+       lock->calls   = 0;
+       lock->waits   = 0;
+       lock->time_ms = 0;
+       SpinLockRelease(&lock->mutex);
+}
diff --git a/src/backend/utils/adt/pgstatfuncs.c 
b/src/backend/utils/adt/pgstatfuncs.c
index 7c0705a..b4e453f 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -117,6 +117,8 @@ extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS);
 extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS);
 extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS);
 
+extern Datum pg_stat_get_lwlocks(PG_FUNCTION_ARGS);
+
 /* Global bgwriter statistics, from bgwriter.c */
 extern PgStat_MsgBgWriter bgwriterStats;
 
@@ -1700,3 +1702,109 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS)
 
        PG_RETURN_VOID();
 }
+
+Datum
+pg_stat_get_lwlocks(PG_FUNCTION_ARGS)
+{
+       FuncCallContext *funcctx;
+
+       /* stuff done only on the first call of the function */
+       if (SRF_IS_FIRSTCALL())
+       {
+               MemoryContext oldcontext;
+               TupleDesc       tupdesc;
+
+               /* create a function context for cross-call persistence */
+               funcctx = SRF_FIRSTCALL_INIT();
+
+               oldcontext = 
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+               tupdesc = CreateTemplateTupleDesc(4, false);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 1, "lockid",
+                                                  INT8OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 2, "calls",
+                                                  INT8OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 3, "waits",
+                                                  INT8OID, -1, 0);
+               TupleDescInitEntry(tupdesc, (AttrNumber) 4, "time_ms",
+                                                  INT8OID, -1, 0);
+
+               funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+               funcctx->max_calls = NumLWLocks();
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /* stuff done on every call of the function */
+       funcctx = SRF_PERCALL_SETUP();
+
+       if (funcctx->call_cntr < funcctx->max_calls)
+       {
+               Datum           values[4];
+               bool            nulls[4];
+               HeapTuple       tuple;
+               LWLockId        lockid;
+               uint64          calls,waits,time_ms;
+
+               MemSet(values, 0, sizeof(values));
+               MemSet(nulls, 0, sizeof(nulls));
+
+               lockid = funcctx->call_cntr;
+
+               if ( lockid<FirstBufMappingLock )
+               {
+                       lwlock_get_stat(lockid, &calls, &waits, &time_ms);
+               }
+               else if ( FirstBufMappingLock<=lockid && 
lockid<FirstLockMgrLock )
+               {
+                       lwlock_get_stat_sum(FirstBufMappingLock, 
FirstLockMgrLock-1,
+                                           &calls, &waits, &time_ms);
+                       funcctx->call_cntr = FirstLockMgrLock - 1;
+               }
+               else if ( FirstLockMgrLock<=lockid && 
lockid<FirstPredicateLockMgrLock )
+               {
+                       lwlock_get_stat_sum(FirstLockMgrLock, 
FirstPredicateLockMgrLock-1,
+                                           &calls, &waits, &time_ms);
+                       funcctx->call_cntr = FirstPredicateLockMgrLock - 1;
+               }
+               else if ( FirstPredicateLockMgrLock<=lockid && 
lockid<NumFixedLWLocks )
+               {
+                       lwlock_get_stat_sum(FirstPredicateLockMgrLock, 
NumFixedLWLocks-1,
+                                           &calls, &waits, &time_ms);
+                       funcctx->call_cntr = NumFixedLWLocks - 1;
+               }
+               else if ( NumFixedLWLocks<=lockid && lockid<NumLWLocks() )
+               {
+                       lwlock_get_stat_sum(NumFixedLWLocks, NumLWLocks()-1,
+                                           &calls, &waits, &time_ms);
+                       funcctx->call_cntr = NumLWLocks() - 1;
+               }
+
+               values[0] = Int64GetDatum(lockid);
+               values[1] = Int64GetDatum(calls);
+               values[2] = Int64GetDatum(waits);
+               values[3] = Int64GetDatum(time_ms);
+
+               tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+
+               SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+       }
+       else
+       {
+               SRF_RETURN_DONE(funcctx);
+       }
+}
+
+Datum
+pg_stat_reset_lwlocks(PG_FUNCTION_ARGS)
+{
+       LWLockId        lockid;
+
+       for (lockid=0 ; lockid<NumLWLocks() ; lockid++)
+       {
+               lwlock_reset_stat(lockid);
+       }
+
+       PG_RETURN_VOID();
+}
+
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bee7154..85d2a7f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2602,6 +2602,10 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset      
        PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity                 PGNSP PGUID 12 
1 100 0 0 f f f f f t s 1 0 2249 "23" 
"{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23}" 
"{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" 
"{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port}"
 _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3764 (  pg_stat_get_lwlocks  PGNSP PGUID 12 1 100 0 0 f f f 
f f t s 0 0 2249 "" "{20,20,20,20}" "{o,o,o,o}" 
"{lwlockid,calls,waits,time_ms}" _null_ pg_stat_get_lwlocks _null_ _null_ 
_null_ ));
+DESCR("statistics: light-weight lock statistics");
+DATA(insert OID = 3765 (  pg_stat_reset_lwlocks  PGNSP PGUID 12 1 0 0 0 f f f 
f f f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_stat_reset_lwlocks _null_ 
_null_ _null_ ));
+DESCR("statistics: reset light-weight lock statistics");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders      PGNSP PGUID 12 1 10 0 0 
f f f f f t s 0 0 2249 "" "{23,25,25,25,25,25,23,25}" "{o,o,o,o,o,o,o,o}" 
"{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}"
 _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
 DATA(insert OID = 2026 (  pg_backend_pid                               PGNSP 
PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ 
pg_backend_pid _null_ _null_ _null_ ));
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 82d8ec4..41c4259 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -119,4 +119,8 @@ extern void CreateLWLocks(void);
 
 extern void RequestAddinLWLocks(int n);
 
+extern void lwlock_get_stat(LWLockId, uint64 *, uint64 *, uint64 *);
+extern void lwlock_get_stat_sum(LWLockId, LWLockId, uint64 *, uint64 *, uint64 
*);
+extern void lwlock_reset_stat(LWLockId);
+
 #endif   /* LWLOCK_H */
-- 
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