Commit f5779198 authored by Andres Freund's avatar Andres Freund

Add pinning_backends column to the pg_buffercache extension.

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.
parent ce486056
...@@ -4,7 +4,7 @@ MODULE_big = pg_buffercache ...@@ -4,7 +4,7 @@ MODULE_big = pg_buffercache
OBJS = pg_buffercache_pages.o $(WIN32RES) OBJS = pg_buffercache_pages.o $(WIN32RES)
EXTENSION = pg_buffercache 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" PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
ifdef USE_PGXS ifdef USE_PGXS
......
/* 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);
/* 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 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit \echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit
...@@ -13,7 +13,8 @@ LANGUAGE C; ...@@ -13,7 +13,8 @@ LANGUAGE C;
CREATE VIEW pg_buffercache AS CREATE VIEW pg_buffercache AS
SELECT P.* FROM pg_buffercache_pages() AS P SELECT P.* FROM pg_buffercache_pages() AS P
(bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid, (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. -- Don't want these to be available to public.
REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC; REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC;
......
# pg_buffercache extension # pg_buffercache extension
comment = 'examine the shared buffer cache' comment = 'examine the shared buffer cache'
default_version = '1.0' default_version = '1.1'
module_pathname = '$libdir/pg_buffercache' module_pathname = '$libdir/pg_buffercache'
relocatable = true relocatable = true
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
#include "storage/bufmgr.h" #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; PG_MODULE_MAGIC;
...@@ -33,6 +34,12 @@ typedef struct ...@@ -33,6 +34,12 @@ typedef struct
bool isvalid; bool isvalid;
bool isdirty; bool isdirty;
uint16 usagecount; 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; } BufferCachePagesRec;
...@@ -60,6 +67,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -60,6 +67,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
MemoryContext oldcontext; MemoryContext oldcontext;
BufferCachePagesContext *fctx; /* User function context. */ BufferCachePagesContext *fctx; /* User function context. */
TupleDesc tupledesc; TupleDesc tupledesc;
TupleDesc expected_tupledesc;
HeapTuple tuple; HeapTuple tuple;
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
...@@ -75,8 +83,23 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -75,8 +83,23 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
/* Create a user function context for cross-call persistence */ /* Create a user function context for cross-call persistence */
fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext)); 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. */ /* 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", TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
INT4OID, -1, 0); INT4OID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode", TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
...@@ -94,6 +117,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -94,6 +117,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count", TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
INT2OID, -1, 0); INT2OID, -1, 0);
if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
INT4OID, -1, 0);
fctx->tupdesc = BlessTupleDesc(tupledesc); fctx->tupdesc = BlessTupleDesc(tupledesc);
/* Allocate NBuffers worth of BufferCachePagesRec records. */ /* Allocate NBuffers worth of BufferCachePagesRec records. */
...@@ -131,6 +158,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -131,6 +158,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
fctx->record[i].forknum = bufHdr->tag.forkNum; fctx->record[i].forknum = bufHdr->tag.forkNum;
fctx->record[i].blocknum = bufHdr->tag.blockNum; fctx->record[i].blocknum = bufHdr->tag.blockNum;
fctx->record[i].usagecount = bufHdr->usage_count; fctx->record[i].usagecount = bufHdr->usage_count;
fctx->record[i].pinning_backends = bufHdr->refcount;
if (bufHdr->flags & BM_DIRTY) if (bufHdr->flags & BM_DIRTY)
fctx->record[i].isdirty = true; fctx->record[i].isdirty = true;
...@@ -185,6 +213,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -185,6 +213,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
nulls[5] = true; nulls[5] = true;
nulls[6] = true; nulls[6] = true;
nulls[7] = true; nulls[7] = true;
/* unused for v1.0 callers, but the array is always long enough */
nulls[8] = true;
} }
else else
{ {
...@@ -202,6 +232,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) ...@@ -202,6 +232,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
nulls[6] = false; nulls[6] = false;
values[7] = Int16GetDatum(fctx->record[i].usagecount); values[7] = Int16GetDatum(fctx->record[i].usagecount);
nulls[7] = false; 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. */ /* Build and return the tuple. */
......
...@@ -106,6 +106,13 @@ ...@@ -106,6 +106,13 @@
<entry>Clock-sweep access count</entry> <entry>Clock-sweep access count</entry>
</row> </row>
<row>
<entry><structfield>pinning_backends</structfield></entry>
<entry><type>integer</type></entry>
<entry></entry>
<entry>Number of backends pinning this buffer</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment