Skip to content

Commit 81da8b3

Browse files
ashutosh-bapatCommitfest Bot
authored andcommitted
Add a view to read contents of shared buffer lookup table
The view exposes the contents of the shared buffer lookup table for debugging, testing and investigation. This helped me in debugging issues where the buffer descriptor array and buffer lookup table were out of sync; either the buffer lookup table had a mapping page->buffer which wasn't present in the buffer descriptor array or a page in the buffer descriptor array didn't have corresponding entry in the buffer lookup table. pg_buffercache doesn't help with those kind of issues. Also doing that under the debugger in very painful. I intend to keep this patch while the rest of the code matures. If it is found useful as a debugging tool, we may consider make it committable and commit it. Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
1 parent c0bc9af commit 81da8b3

File tree

7 files changed

+250
-0
lines changed

7 files changed

+250
-0
lines changed

contrib/pg_buffercache/expected/pg_buffercache.out

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0;
2323
t
2424
(1 row)
2525

26+
-- Test the buffer lookup table function and count is <= shared_buffers
27+
select count(*) <= (select setting::bigint
28+
from pg_settings
29+
where name = 'shared_buffers')
30+
from pg_buffercache_lookup_table_entries();
31+
?column?
32+
----------
33+
t
34+
(1 row)
35+
36+
-- Check that pg_buffercache_lookup_table view works and count is <= shared_buffers
37+
select count(*) <= (select setting::bigint
38+
from pg_settings
39+
where name = 'shared_buffers')
40+
from pg_buffercache_lookup_table;
41+
?column?
42+
----------
43+
t
44+
(1 row)
45+
2646
-- Check that the functions / views can't be accessed by default. To avoid
2747
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
2848
SET ROLE pg_database_owner;
@@ -34,6 +54,10 @@ SELECT * FROM pg_buffercache_summary();
3454
ERROR: permission denied for function pg_buffercache_summary
3555
SELECT * FROM pg_buffercache_usage_counts();
3656
ERROR: permission denied for function pg_buffercache_usage_counts
57+
SELECT * FROM pg_buffercache_lookup_table_entries();
58+
ERROR: permission denied for function pg_buffercache_lookup_table_entries
59+
SELECT * FROM pg_buffercache_lookup_table;
60+
ERROR: permission denied for view pg_buffercache_lookup_table
3761
RESET role;
3862
-- Check that pg_monitor is allowed to query view / function
3963
SET ROLE pg_monitor;
@@ -55,6 +79,21 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
5579
t
5680
(1 row)
5781

82+
RESET role;
83+
-- Check that pg_read_all_stats is allowed to query buffer lookup table
84+
SET ROLE pg_read_all_stats;
85+
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table_entries();
86+
?column?
87+
----------
88+
t
89+
(1 row)
90+
91+
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table;
92+
?column?
93+
----------
94+
t
95+
(1 row)
96+
5897
RESET role;
5998
------
6099
---- Test pg_buffercache_evict* functions

contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,27 @@ CREATE FUNCTION pg_buffercache_evict_all(
4444
OUT buffers_skipped int4)
4545
AS 'MODULE_PATHNAME', 'pg_buffercache_evict_all'
4646
LANGUAGE C PARALLEL SAFE VOLATILE;
47+
48+
-- Add the buffer lookup table function
49+
CREATE FUNCTION pg_buffercache_lookup_table_entries(
50+
OUT tablespace oid,
51+
OUT database oid,
52+
OUT relfilenode oid,
53+
OUT forknum int2,
54+
OUT blocknum int8,
55+
OUT bufferid int4)
56+
RETURNS SETOF record
57+
AS 'MODULE_PATHNAME', 'pg_buffercache_lookup_table_entries'
58+
LANGUAGE C PARALLEL SAFE VOLATILE;
59+
60+
-- Create a view for convenient access.
61+
CREATE VIEW pg_buffercache_lookup_table AS
62+
SELECT * FROM pg_buffercache_lookup_table_entries();
63+
64+
-- Don't want these to be available to public.
65+
REVOKE ALL ON FUNCTION pg_buffercache_lookup_table_entries() FROM PUBLIC;
66+
REVOKE ALL ON pg_buffercache_lookup_table FROM PUBLIC;
67+
68+
-- Grant access to monitoring role.
69+
GRANT EXECUTE ON FUNCTION pg_buffercache_lookup_table_entries() TO pg_read_all_stats;
70+
GRANT SELECT ON pg_buffercache_lookup_table TO pg_read_all_stats;

contrib/pg_buffercache/pg_buffercache_pages.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "storage/buf_internals.h"
1717
#include "storage/bufmgr.h"
1818
#include "utils/rel.h"
19+
#include "utils/tuplestore.h"
1920

2021

2122
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
@@ -100,6 +101,7 @@ PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
100101
PG_FUNCTION_INFO_V1(pg_buffercache_evict);
101102
PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation);
102103
PG_FUNCTION_INFO_V1(pg_buffercache_evict_all);
104+
PG_FUNCTION_INFO_V1(pg_buffercache_lookup_table_entries);
103105

104106

105107
/* Only need to touch memory once per backend process lifetime */
@@ -776,3 +778,19 @@ pg_buffercache_evict_all(PG_FUNCTION_ARGS)
776778

777779
PG_RETURN_DATUM(result);
778780
}
781+
782+
/*
783+
* Return lookup table content as a set of records.
784+
*/
785+
Datum
786+
pg_buffercache_lookup_table_entries(PG_FUNCTION_ARGS)
787+
{
788+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
789+
790+
InitMaterializedSRF(fcinfo, 0);
791+
792+
/* Fill the tuplestore */
793+
BufTableGetContents(rsinfo->setResult, rsinfo->setDesc);
794+
795+
return (Datum) 0;
796+
}

contrib/pg_buffercache/sql/pg_buffercache.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,27 @@ from pg_buffercache_summary();
1212

1313
SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0;
1414

15+
-- Test the buffer lookup table function and count is <= shared_buffers
16+
select count(*) <= (select setting::bigint
17+
from pg_settings
18+
where name = 'shared_buffers')
19+
from pg_buffercache_lookup_table_entries();
20+
21+
-- Check that pg_buffercache_lookup_table view works and count is <= shared_buffers
22+
select count(*) <= (select setting::bigint
23+
from pg_settings
24+
where name = 'shared_buffers')
25+
from pg_buffercache_lookup_table;
26+
1527
-- Check that the functions / views can't be accessed by default. To avoid
1628
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
1729
SET ROLE pg_database_owner;
1830
SELECT * FROM pg_buffercache;
1931
SELECT * FROM pg_buffercache_pages() AS p (wrong int);
2032
SELECT * FROM pg_buffercache_summary();
2133
SELECT * FROM pg_buffercache_usage_counts();
34+
SELECT * FROM pg_buffercache_lookup_table_entries();
35+
SELECT * FROM pg_buffercache_lookup_table;
2236
RESET role;
2337

2438
-- Check that pg_monitor is allowed to query view / function
@@ -28,6 +42,12 @@ SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
2842
SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
2943
RESET role;
3044

45+
-- Check that pg_read_all_stats is allowed to query buffer lookup table
46+
SET ROLE pg_read_all_stats;
47+
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table_entries();
48+
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table;
49+
RESET role;
50+
3151

3252
------
3353
---- Test pg_buffercache_evict* functions

doc/src/sgml/system-views.sgml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
<entry>backend memory contexts</entry>
7272
</row>
7373

74+
<row>
75+
<entry><link linkend="view-pg-buffer-lookup-table"><structname>pg_buffer_lookup_table</structname></link></entry>
76+
<entry>shared buffer lookup table</entry>
77+
</row>
78+
7479
<row>
7580
<entry><link linkend="view-pg-config"><structname>pg_config</structname></link></entry>
7681
<entry>compile-time configuration parameters</entry>
@@ -901,6 +906,90 @@ AND c1.path[c2.level] = c2.path[c2.level];
901906
</para>
902907
</sect1>
903908

909+
<sect1 id="view-pg-buffer-lookup-table">
910+
<title><structname>pg_buffer_lookup_table</structname></title>
911+
<indexterm>
912+
<primary>pg_buffer_lookup_table</primary>
913+
</indexterm>
914+
<para>
915+
The <structname>pg_buffer_lookup_table</structname> view exposes the current
916+
contents of the shared buffer lookup table. Each row represents an entry in
917+
the lookup table mapping a relation page to the ID of buffer in which it is
918+
cached. The shared buffer lookup table is locked for a short duration while
919+
reading so as to ensure consistency. This may affect performance if this view
920+
is queried very frequently.
921+
</para>
922+
<table id="pg-buffer-lookup-table-view" xreflabel="pg_buffer_lookup_table">
923+
<title><structname>pg_buffer_lookup_table</structname> View</title>
924+
<tgroup cols="1">
925+
<thead>
926+
<row>
927+
<entry role="catalog_table_entry"><para role="column_definition">
928+
Column Type
929+
</para>
930+
<para>
931+
Description
932+
</para></entry>
933+
</row>
934+
</thead>
935+
<tbody>
936+
<row>
937+
<entry role="catalog_table_entry"><para role="column_definition">
938+
<structfield>tablespace</structfield> <type>oid</type>
939+
</para>
940+
<para>
941+
OID of the tablespace containing the relation
942+
</para></entry>
943+
</row>
944+
<row>
945+
<entry role="catalog_table_entry"><para role="column_definition">
946+
<structfield>database</structfield> <type>oid</type>
947+
</para>
948+
<para>
949+
OID of the database containing the relation (zero for shared relations)
950+
</para></entry>
951+
</row>
952+
<row>
953+
<entry role="catalog_table_entry"><para role="column_definition">
954+
<structfield>relfilenode</structfield> <type>oid</type>
955+
</para>
956+
<para>
957+
relfilenode identifying the relation
958+
</para></entry>
959+
</row>
960+
<row>
961+
<entry role="catalog_table_entry"><para role="column_definition">
962+
<structfield>forknum</structfield> <type>int2</type>
963+
</para>
964+
<para>
965+
Fork number within the relation (see <xref linkend="storage-file-layout"/>)
966+
</para></entry>
967+
</row>
968+
<row>
969+
<entry role="catalog_table_entry"><para role="column_definition">
970+
<structfield>blocknum</structfield> <type>int8</type>
971+
</para>
972+
<para>
973+
Block number within the relation
974+
</para></entry>
975+
</row>
976+
<row>
977+
<entry role="catalog_table_entry"><para role="column_definition">
978+
<structfield>bufferid</structfield> <type>int4</type>
979+
</para>
980+
<para>
981+
ID of the buffer caching the page
982+
</para></entry>
983+
</row>
984+
</tbody>
985+
</tgroup>
986+
</table>
987+
<para>
988+
Access to this view is restricted to members of the
989+
<literal>pg_read_all_stats</literal> role by default.
990+
</para>
991+
</sect1>
992+
904993
<sect1 id="view-pg-config">
905994
<title><structname>pg_config</structname></title>
906995

src/backend/storage/buffer/buf_table.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
*/
2222
#include "postgres.h"
2323

24+
#include "fmgr.h"
25+
#include "funcapi.h"
2426
#include "storage/buf_internals.h"
27+
#include "storage/lwlock.h"
28+
#include "utils/rel.h"
29+
#include "utils/builtins.h"
2530

2631
/* entry for buffer lookup hashtable */
2732
typedef struct
@@ -159,3 +164,56 @@ BufTableDelete(BufferTag *tagPtr, uint32 hashcode)
159164
if (!result) /* shouldn't happen */
160165
elog(ERROR, "shared buffer hash table corrupted");
161166
}
167+
168+
/*
169+
* BufTableGetContents
170+
* Fill the given tuplestore with contents of the shared buffer lookup table
171+
*
172+
* This function is used by pg_buffercache extension to expose buffer lookup
173+
* table contents via SQL. The caller is responsible for setting up the
174+
* tuplestore and result set info.
175+
*/
176+
void
177+
BufTableGetContents(Tuplestorestate *tupstore, TupleDesc tupdesc)
178+
{
179+
/* Expected number of attributes of the buffer lookup table entry. */
180+
#define BUFTABLE_CONTENTS_COLS 6
181+
182+
HASH_SEQ_STATUS hstat;
183+
BufferLookupEnt *ent;
184+
Datum values[BUFTABLE_CONTENTS_COLS];
185+
bool nulls[BUFTABLE_CONTENTS_COLS];
186+
int i;
187+
188+
memset(nulls, 0, sizeof(nulls));
189+
190+
Assert(tupdesc->natts == BUFTABLE_CONTENTS_COLS);
191+
192+
/*
193+
* Lock all buffer mapping partitions to ensure a consistent view of the
194+
* hash table during the scan. Must grab LWLocks in partition-number order
195+
* to avoid LWLock deadlock.
196+
*/
197+
for (i = 0; i < NUM_BUFFER_PARTITIONS; i++)
198+
LWLockAcquire(BufMappingPartitionLockByIndex(i), LW_SHARED);
199+
200+
hash_seq_init(&hstat, SharedBufHash);
201+
while ((ent = (BufferLookupEnt *) hash_seq_search(&hstat)) != NULL)
202+
{
203+
values[0] = ObjectIdGetDatum(ent->key.spcOid);
204+
values[1] = ObjectIdGetDatum(ent->key.dbOid);
205+
values[2] = ObjectIdGetDatum(ent->key.relNumber);
206+
values[3] = ObjectIdGetDatum(ent->key.forkNum);
207+
values[4] = Int64GetDatum(ent->key.blockNum);
208+
values[5] = Int32GetDatum(ent->id);
209+
210+
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
211+
}
212+
213+
/*
214+
* Release all buffer mapping partition locks in the reverse order so as
215+
* to avoid LWLock deadlock.
216+
*/
217+
for (i = NUM_BUFFER_PARTITIONS - 1; i >= 0; i--)
218+
LWLockRelease(BufMappingPartitionLockByIndex(i));
219+
}

src/include/storage/buf_internals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "storage/spin.h"
2929
#include "utils/relcache.h"
3030
#include "utils/resowner.h"
31+
#include "utils/tuplestore.h"
3132

3233
/*
3334
* Buffer state is a single 32-bit variable where following data is combined.
@@ -520,6 +521,7 @@ extern uint32 BufTableHashCode(BufferTag *tagPtr);
520521
extern int BufTableLookup(BufferTag *tagPtr, uint32 hashcode);
521522
extern int BufTableInsert(BufferTag *tagPtr, uint32 hashcode, int buf_id);
522523
extern void BufTableDelete(BufferTag *tagPtr, uint32 hashcode);
524+
extern void BufTableGetContents(Tuplestorestate *tupstore, TupleDesc tupdesc);
523525

524526
/* localbuf.c */
525527
extern bool PinLocalBuffer(BufferDesc *buf_hdr, bool adjust_usagecount);

0 commit comments

Comments
 (0)