mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Add pg_buffercache_evict_{relation,all} functions
In addition to the added functions, the pg_buffercache_evict() function now
shows whether the buffer was flushed.
pg_buffercache_evict_relation(): Evicts all shared buffers in a
relation at once.
pg_buffercache_evict_all(): Evicts all shared buffers at once.
Both functions provide mechanism to evict multiple shared buffers at
once. They are designed to address the inefficiency of repeatedly calling
pg_buffercache_evict() for each individual buffer, which can be time-consuming
when dealing with large shared buffer pools. (e.g., ~477ms vs. ~2576ms for
16GB of fully populated shared buffers).
These functions are intended for developer testing and debugging
purposes and are available to superusers only.
Minimal tests for the new functions are included. Also, there was no test for
pg_buffercache_evict(), test for this added too.
No new extension version is needed, as it was already increased this release
by ba2a3c2302
.
Author: Nazir Bilal Yavuz <byavuz81@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Aidar Imamov <a.imamov@postgrespro.ru>
Reviewed-by: Joseph Koshakow <koshy44@gmail.com>
Discussion: https://postgr.es/m/CAN55FZ0h_YoSqqutxV6DES1RW8ig6wcA8CR9rJk358YRMxZFmw%40mail.gmail.com
This commit is contained in:
@ -55,3 +55,67 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
|
||||
t
|
||||
(1 row)
|
||||
|
||||
RESET role;
|
||||
------
|
||||
---- Test pg_buffercache_evict* functions
|
||||
------
|
||||
CREATE ROLE regress_buffercache_normal;
|
||||
SET ROLE regress_buffercache_normal;
|
||||
-- These should fail because they need to be called as SUPERUSER
|
||||
SELECT * FROM pg_buffercache_evict(1);
|
||||
ERROR: must be superuser to use pg_buffercache_evict()
|
||||
SELECT * FROM pg_buffercache_evict_relation(1);
|
||||
ERROR: must be superuser to use pg_buffercache_evict_relation()
|
||||
SELECT * FROM pg_buffercache_evict_all();
|
||||
ERROR: must be superuser to use pg_buffercache_evict_all()
|
||||
RESET ROLE;
|
||||
-- These should return nothing, because these are STRICT functions
|
||||
SELECT * FROM pg_buffercache_evict(NULL);
|
||||
buffer_evicted | buffer_flushed
|
||||
----------------+----------------
|
||||
|
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM pg_buffercache_evict_relation(NULL);
|
||||
buffers_evicted | buffers_flushed | buffers_skipped
|
||||
-----------------+-----------------+-----------------
|
||||
| |
|
||||
(1 row)
|
||||
|
||||
-- These should fail because they are not called by valid range of buffers
|
||||
-- Number of the shared buffers are limited by max integer
|
||||
SELECT 2147483647 max_buffers \gset
|
||||
SELECT * FROM pg_buffercache_evict(-1);
|
||||
ERROR: bad buffer ID: -1
|
||||
SELECT * FROM pg_buffercache_evict(0);
|
||||
ERROR: bad buffer ID: 0
|
||||
SELECT * FROM pg_buffercache_evict(:max_buffers);
|
||||
ERROR: bad buffer ID: 2147483647
|
||||
-- This should fail because pg_buffercache_evict_relation() doesn't accept
|
||||
-- local relations
|
||||
CREATE TEMP TABLE temp_pg_buffercache();
|
||||
SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
|
||||
ERROR: relation uses local buffers, pg_buffercache_evict_relation() is intended to be used for shared buffers only
|
||||
DROP TABLE temp_pg_buffercache;
|
||||
-- These shouldn't fail
|
||||
SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all();
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE shared_pg_buffercache();
|
||||
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache');
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
DROP TABLE shared_pg_buffercache;
|
||||
DROP ROLE regress_buffercache_normal;
|
||||
|
@ -20,3 +20,27 @@ REVOKE ALL ON pg_buffercache_numa FROM PUBLIC;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION pg_buffercache_numa_pages() TO pg_monitor;
|
||||
GRANT SELECT ON pg_buffercache_numa TO pg_monitor;
|
||||
|
||||
|
||||
DROP FUNCTION pg_buffercache_evict(integer);
|
||||
CREATE FUNCTION pg_buffercache_evict(
|
||||
IN int,
|
||||
OUT buffer_evicted boolean,
|
||||
OUT buffer_flushed boolean)
|
||||
AS 'MODULE_PATHNAME', 'pg_buffercache_evict'
|
||||
LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
|
||||
|
||||
CREATE FUNCTION pg_buffercache_evict_relation(
|
||||
IN regclass,
|
||||
OUT buffers_evicted int4,
|
||||
OUT buffers_flushed int4,
|
||||
OUT buffers_skipped int4)
|
||||
AS 'MODULE_PATHNAME', 'pg_buffercache_evict_relation'
|
||||
LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
|
||||
|
||||
CREATE FUNCTION pg_buffercache_evict_all(
|
||||
OUT buffers_evicted int4,
|
||||
OUT buffers_flushed int4,
|
||||
OUT buffers_skipped int4)
|
||||
AS 'MODULE_PATHNAME', 'pg_buffercache_evict_all'
|
||||
LANGUAGE C PARALLEL SAFE VOLATILE;
|
||||
|
@ -9,17 +9,22 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/htup_details.h"
|
||||
#include "access/relation.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "port/pg_numa.h"
|
||||
#include "storage/buf_internals.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
|
||||
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
|
||||
#define NUM_BUFFERCACHE_PAGES_ELEM 9
|
||||
#define NUM_BUFFERCACHE_SUMMARY_ELEM 5
|
||||
#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
|
||||
#define NUM_BUFFERCACHE_EVICT_ELEM 2
|
||||
#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
|
||||
#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
|
||||
|
||||
#define NUM_BUFFERCACHE_NUMA_ELEM 3
|
||||
|
||||
@ -93,6 +98,8 @@ PG_FUNCTION_INFO_V1(pg_buffercache_numa_pages);
|
||||
PG_FUNCTION_INFO_V1(pg_buffercache_summary);
|
||||
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);
|
||||
|
||||
|
||||
/* Only need to touch memory once per backend process lifetime */
|
||||
@ -637,21 +644,131 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to check if the user has superuser privileges.
|
||||
*/
|
||||
static void
|
||||
pg_buffercache_superuser_check(char *func_name)
|
||||
{
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to use %s()",
|
||||
func_name)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to evict a shared buffer.
|
||||
*/
|
||||
Datum
|
||||
pg_buffercache_evict(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Buffer buf = PG_GETARG_INT32(0);
|
||||
Datum result;
|
||||
TupleDesc tupledesc;
|
||||
HeapTuple tuple;
|
||||
Datum values[NUM_BUFFERCACHE_EVICT_ELEM];
|
||||
bool nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0};
|
||||
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to use pg_buffercache_evict function")));
|
||||
Buffer buf = PG_GETARG_INT32(0);
|
||||
bool buffer_flushed;
|
||||
|
||||
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
pg_buffercache_superuser_check("pg_buffercache_evict");
|
||||
|
||||
if (buf < 1 || buf > NBuffers)
|
||||
elog(ERROR, "bad buffer ID: %d", buf);
|
||||
|
||||
PG_RETURN_BOOL(EvictUnpinnedBuffer(buf));
|
||||
values[0] = BoolGetDatum(EvictUnpinnedBuffer(buf, &buffer_flushed));
|
||||
values[1] = BoolGetDatum(buffer_flushed);
|
||||
|
||||
tuple = heap_form_tuple(tupledesc, values, nulls);
|
||||
result = HeapTupleGetDatum(tuple);
|
||||
|
||||
PG_RETURN_DATUM(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to evict specified relation.
|
||||
*/
|
||||
Datum
|
||||
pg_buffercache_evict_relation(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum result;
|
||||
TupleDesc tupledesc;
|
||||
HeapTuple tuple;
|
||||
Datum values[NUM_BUFFERCACHE_EVICT_RELATION_ELEM];
|
||||
bool nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0};
|
||||
|
||||
Oid relOid;
|
||||
Relation rel;
|
||||
|
||||
int32 buffers_evicted = 0;
|
||||
int32 buffers_flushed = 0;
|
||||
int32 buffers_skipped = 0;
|
||||
|
||||
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
pg_buffercache_superuser_check("pg_buffercache_evict_relation");
|
||||
|
||||
relOid = PG_GETARG_OID(0);
|
||||
|
||||
rel = relation_open(relOid, AccessShareLock);
|
||||
|
||||
if (RelationUsesLocalBuffers(rel))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
|
||||
"pg_buffercache_evict_relation")));
|
||||
|
||||
EvictRelUnpinnedBuffers(rel, &buffers_evicted, &buffers_flushed,
|
||||
&buffers_skipped);
|
||||
|
||||
relation_close(rel, AccessShareLock);
|
||||
|
||||
values[0] = Int32GetDatum(buffers_evicted);
|
||||
values[1] = Int32GetDatum(buffers_flushed);
|
||||
values[2] = Int32GetDatum(buffers_skipped);
|
||||
|
||||
tuple = heap_form_tuple(tupledesc, values, nulls);
|
||||
result = HeapTupleGetDatum(tuple);
|
||||
|
||||
PG_RETURN_DATUM(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to evict all shared buffers.
|
||||
*/
|
||||
Datum
|
||||
pg_buffercache_evict_all(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum result;
|
||||
TupleDesc tupledesc;
|
||||
HeapTuple tuple;
|
||||
Datum values[NUM_BUFFERCACHE_EVICT_ALL_ELEM];
|
||||
bool nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0};
|
||||
|
||||
int32 buffers_evicted = 0;
|
||||
int32 buffers_flushed = 0;
|
||||
int32 buffers_skipped = 0;
|
||||
|
||||
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
|
||||
pg_buffercache_superuser_check("pg_buffercache_evict_all");
|
||||
|
||||
EvictAllUnpinnedBuffers(&buffers_evicted, &buffers_flushed,
|
||||
&buffers_skipped);
|
||||
|
||||
values[0] = Int32GetDatum(buffers_evicted);
|
||||
values[1] = Int32GetDatum(buffers_flushed);
|
||||
values[2] = Int32GetDatum(buffers_skipped);
|
||||
|
||||
tuple = heap_form_tuple(tupledesc, values, nulls);
|
||||
result = HeapTupleGetDatum(tuple);
|
||||
|
||||
PG_RETURN_DATUM(result);
|
||||
}
|
||||
|
@ -26,3 +26,45 @@ SET ROLE pg_monitor;
|
||||
SELECT count(*) > 0 FROM pg_buffercache;
|
||||
SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
|
||||
SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
|
||||
RESET role;
|
||||
|
||||
|
||||
------
|
||||
---- Test pg_buffercache_evict* functions
|
||||
------
|
||||
|
||||
CREATE ROLE regress_buffercache_normal;
|
||||
SET ROLE regress_buffercache_normal;
|
||||
|
||||
-- These should fail because they need to be called as SUPERUSER
|
||||
SELECT * FROM pg_buffercache_evict(1);
|
||||
SELECT * FROM pg_buffercache_evict_relation(1);
|
||||
SELECT * FROM pg_buffercache_evict_all();
|
||||
|
||||
RESET ROLE;
|
||||
|
||||
-- These should return nothing, because these are STRICT functions
|
||||
SELECT * FROM pg_buffercache_evict(NULL);
|
||||
SELECT * FROM pg_buffercache_evict_relation(NULL);
|
||||
|
||||
-- These should fail because they are not called by valid range of buffers
|
||||
-- Number of the shared buffers are limited by max integer
|
||||
SELECT 2147483647 max_buffers \gset
|
||||
SELECT * FROM pg_buffercache_evict(-1);
|
||||
SELECT * FROM pg_buffercache_evict(0);
|
||||
SELECT * FROM pg_buffercache_evict(:max_buffers);
|
||||
|
||||
-- This should fail because pg_buffercache_evict_relation() doesn't accept
|
||||
-- local relations
|
||||
CREATE TEMP TABLE temp_pg_buffercache();
|
||||
SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
|
||||
DROP TABLE temp_pg_buffercache;
|
||||
|
||||
-- These shouldn't fail
|
||||
SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
|
||||
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all();
|
||||
CREATE TABLE shared_pg_buffercache();
|
||||
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache');
|
||||
DROP TABLE shared_pg_buffercache;
|
||||
|
||||
DROP ROLE regress_buffercache_normal;
|
||||
|
Reference in New Issue
Block a user