Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions contrib/pg_buffercache/expected/pg_buffercache.out
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
CREATE EXTENSION pg_buffercache;
select count(*) = (select setting::bigint
from pg_settings
where name = 'shared_buffers')
from pg_buffercache;
select pg_size_bytes(setting)/(select setting::bigint from pg_settings where name = 'block_size') AS nbuffers
from pg_settings
where name = 'shared_buffers'
\gset
select count(*) = :nbuffers from pg_buffercache;
?column?
----------
t
Expand All @@ -23,6 +24,20 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0;
t
(1 row)

-- Test the buffer lookup table function and count is <= shared_buffers
select count(*) <= :nbuffers from pg_buffercache_lookup_table_entries();
?column?
----------
t
(1 row)

-- Check that pg_buffercache_lookup_table view works and count is <= shared_buffers
select count(*) <= :nbuffers from pg_buffercache_lookup_table;
?column?
----------
t
(1 row)

-- Check that the functions / views can't be accessed by default. To avoid
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
SET ROLE pg_database_owner;
Expand All @@ -34,6 +49,10 @@ SELECT * FROM pg_buffercache_summary();
ERROR: permission denied for function pg_buffercache_summary
SELECT * FROM pg_buffercache_usage_counts();
ERROR: permission denied for function pg_buffercache_usage_counts
SELECT * FROM pg_buffercache_lookup_table_entries();
ERROR: permission denied for function pg_buffercache_lookup_table_entries
SELECT * FROM pg_buffercache_lookup_table;
ERROR: permission denied for view pg_buffercache_lookup_table
RESET role;
-- Check that pg_monitor is allowed to query view / function
SET ROLE pg_monitor;
Expand All @@ -55,6 +74,21 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
t
(1 row)

RESET role;
-- Check that pg_read_all_stats is allowed to query buffer lookup table
SET ROLE pg_read_all_stats;
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table_entries();
?column?
----------
t
(1 row)

SELECT count(*) >= 0 FROM pg_buffercache_lookup_table;
?column?
----------
t
(1 row)

RESET role;
------
---- Test pg_buffercache_evict* functions
Expand Down
24 changes: 24 additions & 0 deletions contrib/pg_buffercache/pg_buffercache--1.5--1.6.sql
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ CREATE FUNCTION pg_buffercache_evict_all(
OUT buffers_skipped int4)
AS 'MODULE_PATHNAME', 'pg_buffercache_evict_all'
LANGUAGE C PARALLEL SAFE VOLATILE;

-- Add the buffer lookup table function
CREATE FUNCTION pg_buffercache_lookup_table_entries(
OUT tablespace oid,
OUT database oid,
OUT relfilenode oid,
OUT forknum int2,
OUT blocknum int8,
OUT bufferid int4)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_buffercache_lookup_table_entries'
LANGUAGE C PARALLEL SAFE VOLATILE;

-- Create a view for convenient access.
CREATE VIEW pg_buffercache_lookup_table AS
SELECT * FROM pg_buffercache_lookup_table_entries();

-- Don't want these to be available to public.
REVOKE ALL ON FUNCTION pg_buffercache_lookup_table_entries() FROM PUBLIC;
REVOKE ALL ON pg_buffercache_lookup_table FROM PUBLIC;

-- Grant access to monitoring role.
GRANT EXECUTE ON FUNCTION pg_buffercache_lookup_table_entries() TO pg_read_all_stats;
GRANT SELECT ON pg_buffercache_lookup_table TO pg_read_all_stats;
36 changes: 33 additions & 3 deletions contrib/pg_buffercache/pg_buffercache_pages.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"
#include "utils/tuplestore.h"


#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
Expand Down Expand Up @@ -100,6 +101,7 @@ PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
PG_FUNCTION_INFO_V1(pg_buffercache_evict);
PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation);
PG_FUNCTION_INFO_V1(pg_buffercache_evict_all);
PG_FUNCTION_INFO_V1(pg_buffercache_lookup_table_entries);


/* Only need to touch memory once per backend process lifetime */
Expand All @@ -116,6 +118,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
TupleDesc tupledesc;
TupleDesc expected_tupledesc;
HeapTuple tuple;
int currentNBuffers = pg_atomic_read_u32(&ShmemCtrl->currentNBuffers);

if (SRF_IS_FIRSTCALL())
{
Expand Down Expand Up @@ -172,10 +175,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
/* Allocate NBuffers worth of BufferCachePagesRec records. */
fctx->record = (BufferCachePagesRec *)
MemoryContextAllocHuge(CurrentMemoryContext,
sizeof(BufferCachePagesRec) * NBuffers);
sizeof(BufferCachePagesRec) * currentNBuffers);

/* Set max calls and remember the user function context. */
funcctx->max_calls = NBuffers;
funcctx->max_calls = currentNBuffers;
funcctx->user_fctx = fctx;

/* Return to original context when allocating transient memory */
Expand All @@ -189,13 +192,24 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
* snapshot across all buffers, but we do grab the buffer header
* locks, so the information of each buffer is self-consistent.
*/
for (i = 0; i < NBuffers; i++)
for (i = 0; i < currentNBuffers; i++)
{
BufferDesc *bufHdr;
uint32 buf_state;

CHECK_FOR_INTERRUPTS();

/*
* TODO: We should just scan the entire buffer descriptor
* array instead of relying on curent buffer pool size. But that can
* happen if only we setup the descriptor array large enough at the
* server startup time.
*/
if (currentNBuffers != pg_atomic_read_u32(&ShmemCtrl->currentNBuffers))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("number of shared buffers changed during scan of buffer cache")));

bufHdr = GetBufferDescriptor(i);
/* Lock each buffer header before inspecting. */
buf_state = LockBufHdr(bufHdr);
Expand Down Expand Up @@ -777,3 +791,19 @@ pg_buffercache_evict_all(PG_FUNCTION_ARGS)

PG_RETURN_DATUM(result);
}

/*
* Return lookup table content as a set of records.
*/
Datum
pg_buffercache_lookup_table_entries(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;

InitMaterializedSRF(fcinfo, 0);

/* Fill the tuplestore */
BufTableGetContents(rsinfo->setResult, rsinfo->setDesc);

return (Datum) 0;
}
23 changes: 19 additions & 4 deletions contrib/pg_buffercache/sql/pg_buffercache.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
CREATE EXTENSION pg_buffercache;

select count(*) = (select setting::bigint
from pg_settings
where name = 'shared_buffers')
from pg_buffercache;
select pg_size_bytes(setting)/(select setting::bigint from pg_settings where name = 'block_size') AS nbuffers
from pg_settings
where name = 'shared_buffers'
\gset
select count(*) = :nbuffers from pg_buffercache;

select buffers_used + buffers_unused > 0,
buffers_dirty <= buffers_used,
Expand All @@ -12,13 +13,21 @@ from pg_buffercache_summary();

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

-- Test the buffer lookup table function and count is <= shared_buffers
select count(*) <= :nbuffers from pg_buffercache_lookup_table_entries();

-- Check that pg_buffercache_lookup_table view works and count is <= shared_buffers
select count(*) <= :nbuffers from pg_buffercache_lookup_table;

-- Check that the functions / views can't be accessed by default. To avoid
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
SET ROLE pg_database_owner;
SELECT * FROM pg_buffercache;
SELECT * FROM pg_buffercache_pages() AS p (wrong int);
SELECT * FROM pg_buffercache_summary();
SELECT * FROM pg_buffercache_usage_counts();
SELECT * FROM pg_buffercache_lookup_table_entries();
SELECT * FROM pg_buffercache_lookup_table;
RESET role;

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

-- Check that pg_read_all_stats is allowed to query buffer lookup table
SET ROLE pg_read_all_stats;
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table_entries();
SELECT count(*) >= 0 FROM pg_buffercache_lookup_table;
RESET role;


------
---- Test pg_buffercache_evict* functions
Expand Down
44 changes: 43 additions & 1 deletion doc/src/sgml/config.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -1724,7 +1724,6 @@ include_dir 'conf.d'
that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
(Non-default values of <symbol>BLCKSZ</symbol> change the minimum
value.)
This parameter can only be set at server start.
</para>

<para>
Expand All @@ -1747,6 +1746,49 @@ include_dir 'conf.d'
appropriate, so as to leave adequate space for the operating system.
</para>

<para>
The shared memory consumed by the buffer pool is allocated and
initialized according to the value of the GUC at the time of starting
the server. A desired new value of GUC can be loaded while the server is
running using <systemitem>SIGHUP</systemitem>. But the buffer pool will
not be resized immediately. Use
<function>pg_resize_shared_buffers()</function> to dynamically resize
the shared buffer pool (see <xref linkend="functions-admin"/> for details).
<command>SHOW shared_buffers</command> shows the current number of
shared buffers and pending number, if any. Please note that when the GUC
is changed, the other GUCS which use this GUCs value to set their
defaults will not be changed. They may still require a server restart to
consider new value.
</para>
</listitem>
</varlistentry>

<varlistentry id="guc-max-shared-buffers" xreflabel="max_shared_buffers">
<term><varname>max_shared_buffers</varname> (<type>integer</type>)
<indexterm>
<primary><varname>max_shared_buffers</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the upper limit for the <varname>shared_buffers</varname> value.
The default value is <literal>0</literal>,
which means no explicit limit is set and <varname>max_shared_buffers</varname>
will be automatically set to the value of <varname>shared_buffers</varname>
at server startup.
If this value is specified without units, it is taken as blocks,
that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
This parameter can only be set at server start.
</para>

<para>
This parameter determines the amount of memory address space to reserve
in each backend for expanding the buffer pool in future. While the
memory for buffer pool is allocated on demand as it is resized, the
memory required to hold the buffer manager metadata is allocated
statically at the server start accounting for the largest buffer pool
size allowed by this parameter.
</para>
</listitem>
</varlistentry>

Expand Down
Loading
Loading