mirror of
https://github.com/postgres/postgres.git
synced 2025-04-20 00:42:27 +03:00
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.
This commit is contained in:
parent
ce486056ec
commit
f57791985a
@ -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
|
||||||
|
11
contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql
Normal file
11
contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql
Normal file
@ -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);
|
@ -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
|
-- 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;
|
|||||||
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;
|
@ -1,5 +1,5 @@
|
|||||||
# 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 @@
|
|||||||
#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
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
<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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user