Skip to content

Commit 2589434

Browse files
committed
pg_buffercache: Add pg_buffercache_summary()
Using pg_buffercache_summary() is significantly cheaper than querying pg_buffercache and summarizing in SQL. Author: Melih Mutlu <m.melihmutlu@gmail.com> Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: Aleksander Alekseev <aleksander@timescale.com> Reviewed-by: Zhang Mingli <zmlpostgres@gmail.com> Discussion: https://postgr.es/m/CAGPVpCQAXYo54Q%3D8gqBsS%3Du0uk9qhnnq4%2B710BtUhUisX1XGEg%40mail.gmail.com
1 parent 7f8d9ce commit 2589434

File tree

8 files changed

+250
-5
lines changed

8 files changed

+250
-5
lines changed

contrib/pg_buffercache/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ OBJS = \
77

88
EXTENSION = pg_buffercache
99
DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
10-
pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql
10+
pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \
11+
pg_buffercache--1.3--1.4.sql
1112
PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
1213

1314
REGRESS = pg_buffercache

contrib/pg_buffercache/expected/pg_buffercache.out

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,36 @@ from pg_buffercache;
88
t
99
(1 row)
1010

11+
select buffers_used + buffers_unused > 0,
12+
buffers_dirty <= buffers_used,
13+
buffers_pinned <= buffers_used
14+
from pg_buffercache_summary();
15+
?column? | ?column? | ?column?
16+
----------+----------+----------
17+
t | t | t
18+
(1 row)
19+
20+
-- Check that the functions / views can't be accessed by default. To avoid
21+
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
22+
SET ROLE pg_database_owner;
23+
SELECT * FROM pg_buffercache;
24+
ERROR: permission denied for view pg_buffercache
25+
SELECT * FROM pg_buffercache_pages() AS p (wrong int);
26+
ERROR: permission denied for function pg_buffercache_pages
27+
SELECT * FROM pg_buffercache_summary();
28+
ERROR: permission denied for function pg_buffercache_summary
29+
RESET role;
30+
-- Check that pg_monitor is allowed to query view / function
31+
SET ROLE pg_monitor;
32+
SELECT count(*) > 0 FROM pg_buffercache;
33+
?column?
34+
----------
35+
t
36+
(1 row)
37+
38+
SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
39+
?column?
40+
----------
41+
t
42+
(1 row)
43+

contrib/pg_buffercache/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ install_data(
1919
'pg_buffercache--1.1--1.2.sql',
2020
'pg_buffercache--1.2--1.3.sql',
2121
'pg_buffercache--1.2.sql',
22+
'pg_buffercache--1.3--1.4.sql',
2223
'pg_buffercache.control',
2324
kwargs: contrib_data_args,
2425
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql */
2+
3+
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
4+
\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.4'" to load this file. \quit
5+
6+
CREATE FUNCTION pg_buffercache_summary(
7+
OUT buffers_used int4,
8+
OUT buffers_unused int4,
9+
OUT buffers_dirty int4,
10+
OUT buffers_pinned int4,
11+
OUT usagecount_avg float8)
12+
AS 'MODULE_PATHNAME', 'pg_buffercache_summary'
13+
LANGUAGE C PARALLEL SAFE;
14+
15+
-- Don't want these to be available to public.
16+
REVOKE ALL ON FUNCTION pg_buffercache_summary() FROM PUBLIC;
17+
GRANT EXECUTE ON FUNCTION pg_buffercache_summary() TO pg_monitor;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pg_buffercache extension
22
comment = 'examine the shared buffer cache'
3-
default_version = '1.3'
3+
default_version = '1.4'
44
module_pathname = '$libdir/pg_buffercache'
55
relocatable = true

contrib/pg_buffercache/pg_buffercache_pages.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
1919
#define NUM_BUFFERCACHE_PAGES_ELEM 9
20+
#define NUM_BUFFERCACHE_SUMMARY_ELEM 5
2021

2122
PG_MODULE_MAGIC;
2223

@@ -59,6 +60,7 @@ typedef struct
5960
* relation node/tablespace/database/blocknum and dirty indicator.
6061
*/
6162
PG_FUNCTION_INFO_V1(pg_buffercache_pages);
63+
PG_FUNCTION_INFO_V1(pg_buffercache_summary);
6264

6365
Datum
6466
pg_buffercache_pages(PG_FUNCTION_ARGS)
@@ -237,3 +239,68 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
237239
else
238240
SRF_RETURN_DONE(funcctx);
239241
}
242+
243+
Datum
244+
pg_buffercache_summary(PG_FUNCTION_ARGS)
245+
{
246+
Datum result;
247+
TupleDesc tupledesc;
248+
HeapTuple tuple;
249+
Datum values[NUM_BUFFERCACHE_SUMMARY_ELEM];
250+
bool nulls[NUM_BUFFERCACHE_SUMMARY_ELEM];
251+
252+
int32 buffers_used = 0;
253+
int32 buffers_unused = 0;
254+
int32 buffers_dirty = 0;
255+
int32 buffers_pinned = 0;
256+
int64 usagecount_total = 0;
257+
258+
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
259+
elog(ERROR, "return type must be a row type");
260+
261+
for (int i = 0; i < NBuffers; i++)
262+
{
263+
BufferDesc *bufHdr;
264+
uint32 buf_state;
265+
266+
/*
267+
* This function summarizes the state of all headers. Locking the
268+
* buffer headers wouldn't provide an improved result as the state of
269+
* the buffer can still change after we release the lock and it'd
270+
* noticeably increase the cost of the function.
271+
*/
272+
bufHdr = GetBufferDescriptor(i);
273+
buf_state = pg_atomic_read_u32(&bufHdr->state);
274+
275+
if (buf_state & BM_VALID)
276+
{
277+
buffers_used++;
278+
usagecount_total += BUF_STATE_GET_USAGECOUNT(buf_state);
279+
280+
if (buf_state & BM_DIRTY)
281+
buffers_dirty++;
282+
}
283+
else
284+
buffers_unused++;
285+
286+
if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
287+
buffers_pinned++;
288+
}
289+
290+
memset(nulls, 0, sizeof(nulls));
291+
values[0] = Int32GetDatum(buffers_used);
292+
values[1] = Int32GetDatum(buffers_unused);
293+
values[2] = Int32GetDatum(buffers_dirty);
294+
values[3] = Int32GetDatum(buffers_pinned);
295+
296+
if (buffers_used != 0)
297+
values[4] = Float8GetDatum((double) usagecount_total / buffers_used);
298+
else
299+
nulls[4] = true;
300+
301+
/* Build and return the tuple. */
302+
tuple = heap_form_tuple(tupledesc, values, nulls);
303+
result = HeapTupleGetDatum(tuple);
304+
305+
PG_RETURN_DATUM(result);
306+
}

contrib/pg_buffercache/sql/pg_buffercache.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,21 @@ select count(*) = (select setting::bigint
44
from pg_settings
55
where name = 'shared_buffers')
66
from pg_buffercache;
7+
8+
select buffers_used + buffers_unused > 0,
9+
buffers_dirty <= buffers_used,
10+
buffers_pinned <= buffers_used
11+
from pg_buffercache_summary();
12+
13+
-- Check that the functions / views can't be accessed by default. To avoid
14+
-- having to create a dedicated user, use the pg_database_owner pseudo-role.
15+
SET ROLE pg_database_owner;
16+
SELECT * FROM pg_buffercache;
17+
SELECT * FROM pg_buffercache_pages() AS p (wrong int);
18+
SELECT * FROM pg_buffercache_summary();
19+
RESET role;
20+
21+
-- Check that pg_monitor is allowed to query view / function
22+
SET ROLE pg_monitor;
23+
SELECT count(*) > 0 FROM pg_buffercache;
24+
SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();

doc/src/sgml/pgbuffercache.sgml

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,28 @@
1616
<primary>pg_buffercache_pages</primary>
1717
</indexterm>
1818

19+
<indexterm>
20+
<primary>pg_buffercache_summary</primary>
21+
</indexterm>
22+
23+
<para>
24+
The module provides the <function>pg_buffercache_pages()</function>
25+
function, wrapped in the <structname>pg_buffercache</structname> view, and
26+
the <function>pg_buffercache_summary()</function> function.
27+
</para>
28+
1929
<para>
20-
The module provides a C function <function>pg_buffercache_pages</function>
21-
that returns a set of records, plus a view
22-
<structname>pg_buffercache</structname> that wraps the function for
30+
The <function>pg_buffercache_pages()</function> function returns a set of
31+
records, each row describing the state of one shared buffer entry. The
32+
<structname>pg_buffercache</structname> view wraps the function for
2333
convenient use.
2434
</para>
2535

36+
<para>
37+
The <function>pg_buffercache_summary()</function> function returns a single
38+
row summarizing the state of the shared buffer cache.
39+
</para>
40+
2641
<para>
2742
By default, use is restricted to superusers and roles with privileges of the
2843
<literal>pg_monitor</literal> role. Access may be granted to others
@@ -164,6 +179,92 @@
164179
</para>
165180
</sect2>
166181

182+
<sect2>
183+
<title>The <function>pg_buffercache_summary()</function> Function</title>
184+
185+
<para>
186+
The definitions of the columns exposed by the function are shown in <xref linkend="pgbuffercache_summary-columns"/>.
187+
</para>
188+
189+
<table id="pgbuffercache_summary-columns">
190+
<title><function>pg_buffercache_summary()</function> Output Columns</title>
191+
<tgroup cols="1">
192+
<thead>
193+
<row>
194+
<entry role="catalog_table_entry"><para role="column_definition">
195+
Column Type
196+
</para>
197+
<para>
198+
Description
199+
</para></entry>
200+
</row>
201+
</thead>
202+
203+
<tbody>
204+
<row>
205+
<entry role="catalog_table_entry"><para role="column_definition">
206+
<structfield>buffers_used</structfield> <type>int4</type>
207+
</para>
208+
<para>
209+
Number of unused shared buffers
210+
</para></entry>
211+
</row>
212+
213+
<row>
214+
<entry role="catalog_table_entry"><para role="column_definition">
215+
<structfield>buffers_unused</structfield> <type>int4</type>
216+
</para>
217+
<para>
218+
Number of unused shared buffers
219+
</para></entry>
220+
</row>
221+
222+
<row>
223+
<entry role="catalog_table_entry"><para role="column_definition">
224+
<structfield>buffers_dirty</structfield> <type>int4</type>
225+
</para>
226+
<para>
227+
Number of dirty shared buffers
228+
</para></entry>
229+
</row>
230+
231+
<row>
232+
<entry role="catalog_table_entry"><para role="column_definition">
233+
<structfield>buffers_pinned</structfield> <type>int4</type>
234+
</para>
235+
<para>
236+
Number of pinned shared buffers
237+
</para></entry>
238+
</row>
239+
240+
<row>
241+
<entry role="catalog_table_entry"><para role="column_definition">
242+
<structfield>usagecount_avg</structfield> <type>float8</type>
243+
</para>
244+
<para>
245+
Average usagecount of used shared buffers
246+
</para></entry>
247+
</row>
248+
</tbody>
249+
</tgroup>
250+
</table>
251+
252+
<para>
253+
The <function>pg_buffercache_summary()</function> function returns a
254+
single row summarizing the state of all shared buffers. Similar and more
255+
detailed information is provided by the
256+
<structname>pg_buffercache</structname> view, but
257+
<function>pg_buffercache_summary()</function> is significantly cheaper.
258+
</para>
259+
260+
<para>
261+
Like the <structname>pg_buffercache</structname> view,
262+
<function>pg_buffercache_summary()</function> does not acquire buffer
263+
manager locks. Therefore concurrent activity can lead to minor inaccuracies
264+
in the result.
265+
</para>
266+
</sect2>
267+
167268
<sect2>
168269
<title>Sample Output</title>
169270

@@ -191,6 +292,13 @@ regression=# SELECT n.nspname, c.relname, count(*) AS buffers
191292
public | gin_test_tbl | 188
192293
public | spgist_text_tbl | 182
193294
(10 rows)
295+
296+
297+
regression=# SELECT * FROM pg_buffercache_summary();
298+
buffers_used | buffers_unused | buffers_dirty | buffers_pinned | usagecount_avg
299+
--------------+----------------+---------------+----------------+----------------
300+
248 | 2096904 | 39 | 0 | 3.141129
301+
(1 row)
194302
</screen>
195303
</sect2>
196304

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy