Add pinning_backends column to the pg_buffercache extension.
authorAndres Freund
Thu, 21 Aug 2014 22:28:37 +0000 (00:28 +0200)
committerAndres Freund
Thu, 21 Aug 2014 22:28:37 +0000 (00:28 +0200)
The new column shows how many backends have a buffer pinned. That can
be useful during development or to diagnose production issues
e.g. caused by vacuum waiting for cleanup locks.

To handle upgrades transparently - the extension might be used in
views - deal with callers expecting the old number of columns.

Reviewed by Fujii Masao and Rajeev rastogi.

contrib/pg_buffercache/Makefile
contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql [new file with mode: 0644]
contrib/pg_buffercache/pg_buffercache--1.1.sql [moved from contrib/pg_buffercache/pg_buffercache--1.0.sql with 88% similarity]
contrib/pg_buffercache/pg_buffercache.control
contrib/pg_buffercache/pg_buffercache_pages.c
doc/src/sgml/pgbuffercache.sgml

index c5297d98d481101220ea5449a818ff7837ce7a03..065d3d690a97e5124ea36619c462b3727d24efe5 100644 (file)
@@ -4,7 +4,7 @@ MODULE_big = pg_buffercache
 OBJS = pg_buffercache_pages.o $(WIN32RES)
 
 EXTENSION = pg_buffercache
-DATA = pg_buffercache--1.0.sql pg_buffercache--unpackaged--1.0.sql
+DATA = pg_buffercache--1.1.sql pg_buffercache--1.0--1.1.sql pg_buffercache--unpackaged--1.0.sql
 PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
 
 ifdef USE_PGXS
diff --git a/contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql b/contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql
new file mode 100644 (file)
index 0000000..54d02f5
--- /dev/null
@@ -0,0 +1,11 @@
+/* contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.1'" to load this file. \quit
+
+-- Upgrade view to 1.1. format
+CREATE OR REPLACE VIEW pg_buffercache AS
+   SELECT P.* FROM pg_buffercache_pages() AS P
+   (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
+    relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,
+    pinning_backends int4);
similarity index 88%
rename from contrib/pg_buffercache/pg_buffercache--1.0.sql
rename to contrib/pg_buffercache/pg_buffercache--1.1.sql
index 4ca4c44256c8a0e5e44eb0eadca3345ede63ca6f..f3b6482fa6258912e0d7642e5ca4eb2193708082 100644 (file)
@@ -1,4 +1,4 @@
-/* contrib/pg_buffercache/pg_buffercache--1.0.sql */
+/* contrib/pg_buffercache/pg_buffercache--1.1.sql */
 
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
@@ -13,7 +13,8 @@ LANGUAGE C;
 CREATE VIEW pg_buffercache AS
    SELECT P.* FROM pg_buffercache_pages() AS P
    (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
-    relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2);
+    relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2,
+    pinning_backends int4);
 
 -- Don't want these to be available to public.
 REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
index 709513c334ed4d04c4047358e9b23dffd6c5bd5f..5494e2fae521a122507e3cb7a179050ded1fcdd7 100644 (file)
@@ -1,5 +1,5 @@
 # pg_buffercache extension
 comment = 'examine the shared buffer cache'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/pg_buffercache'
 relocatable = true
index b205683bffcce95d0f735b42bc1c0947a02480cc..d3b1ba3245d6853e42c10d421c71b44bb4b4903e 100644 (file)
@@ -15,7 +15,8 @@
 #include "storage/bufmgr.h"
 
 
-#define NUM_BUFFERCACHE_PAGES_ELEM 8
+#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
+#define NUM_BUFFERCACHE_PAGES_ELEM 9
 
 PG_MODULE_MAGIC;
 
@@ -33,6 +34,12 @@ typedef struct
    bool        isvalid;
    bool        isdirty;
    uint16      usagecount;
+   /*
+    * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
+    * being pinned by too many backends and each backend will only pin once
+    * because of bufmgr.c's PrivateRefCount array.
+    */
+   int32       pinning_backends;
 } BufferCachePagesRec;
 
 
@@ -60,6 +67,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
    MemoryContext oldcontext;
    BufferCachePagesContext *fctx;      /* User function context. */
    TupleDesc   tupledesc;
+   TupleDesc   expected_tupledesc;
    HeapTuple   tuple;
 
    if (SRF_IS_FIRSTCALL())
@@ -75,8 +83,23 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
        /* Create a user function context for cross-call persistence */
        fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext));
 
+       /*
+        * To smoothly support upgrades from version 1.0 of this extension
+        * transparently handle the (non-)existance of the pinning_backends
+        * column. We unfortunately have to get the result type for that... -
+        * we can't use the result type determined by the function definition
+        * without potentially crashing when somebody uses the old (or even
+        * wrong) function definition though.
+        */
+       if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
+           elog(ERROR, "return type must be a row type");
+
+       if (expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
+           expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
+           elog(ERROR, "incorrect number of output arguments");
+
        /* Construct a tuple descriptor for the result rows. */
-       tupledesc = CreateTemplateTupleDesc(NUM_BUFFERCACHE_PAGES_ELEM, false);
+       tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts, false);
        TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
                           INT4OID, -1, 0);
        TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
@@ -94,6 +117,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
        TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
                           INT2OID, -1, 0);
 
+       if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
+           TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
+                              INT4OID, -1, 0);
+
        fctx->tupdesc = BlessTupleDesc(tupledesc);
 
        /* Allocate NBuffers worth of BufferCachePagesRec records. */
@@ -131,6 +158,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
            fctx->record[i].forknum = bufHdr->tag.forkNum;
            fctx->record[i].blocknum = bufHdr->tag.blockNum;
            fctx->record[i].usagecount = bufHdr->usage_count;
+           fctx->record[i].pinning_backends = bufHdr->refcount;
 
            if (bufHdr->flags & BM_DIRTY)
                fctx->record[i].isdirty = true;
@@ -185,6 +213,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
            nulls[5] = true;
            nulls[6] = true;
            nulls[7] = true;
+           /* unused for v1.0 callers, but the array is always long enough */
+           nulls[8] = true;
        }
        else
        {
@@ -202,6 +232,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
            nulls[6] = false;
            values[7] = Int16GetDatum(fctx->record[i].usagecount);
            nulls[7] = false;
+           /* unused for v1.0 callers, but the array is always long enough */
+           values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
+           nulls[8] = false;
        }
 
        /* Build and return the tuple. */
index 4eb02c06239a0d21489ee5f092ea29a49b552433..f379be225f0c20268520d7f850e4418271cd9dff 100644 (file)
       Clock-sweep access count
      
 
+     
+      pinning_backends
+      integer
+      
+      Number of backends pinning this buffer
+     
+