Skip to content

Commit 66f9709

Browse files
nbyavuzCommitfest Bot
authored andcommitted
Add pg_buffercache_mark_dirty{,_relation,_all}() functions
This commit introduces three new functions for marking shared buffers as dirty: pg_buffercache_mark_dirty(): Marks a specific shared buffer as dirty. pg_buffercache_mark_dirt_relation(): Marks all shared buffers as dirty in a relation at once. pg_buffercache_mark_dirty_all(): Marks all shared buffers as dirty at once. The pg_buffercache_mark_dirty_relation() and pg_buffercache_mark_dirty_all() functions provide mechanism to mark multiple shared buffers as dirty at once. They are designed to address the inefficiency of repeatedly calling pg_buffercache_mark_dirty() for each individual buffer, which can be time-consuming when dealing with with large shared buffers pool. (e.g., ~550ms vs. ~70ms 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. Author: Nazir Bilal Yavuz <[email protected]> Reviewed-by: Aidar Imamov <[email protected]> Reviewed-by: Andres Freund <[email protected]> Reviewed-by: Amit Kapila <[email protected]> Reviewed-by: Joseph Koshakow <[email protected]> Reviewed-by: Xuneng Zhou <[email protected]> Discussion: https://postgr.es/m/CAN55FZ0h_YoSqqutxV6DES1RW8ig6wcA8CR9rJk358YRMxZFmw%40mail.gmail.com
1 parent 8b18ed6 commit 66f9709

File tree

10 files changed

+497
-10
lines changed

10 files changed

+497
-10
lines changed

contrib/pg_buffercache/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ EXTENSION = pg_buffercache
99
DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \
1010
pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \
1111
pg_buffercache--1.3--1.4.sql pg_buffercache--1.4--1.5.sql \
12-
pg_buffercache--1.5--1.6.sql
12+
pg_buffercache--1.5--1.6.sql pg_buffercache--1.6--1.7.sql
1313
PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time"
1414

1515
REGRESS = pg_buffercache pg_buffercache_numa

contrib/pg_buffercache/expected/pg_buffercache.out

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
5757

5858
RESET role;
5959
------
60-
---- Test pg_buffercache_evict* functions
60+
---- Test pg_buffercache_evict* and pg_buffercache_mark_dirty* functions
6161
------
6262
CREATE ROLE regress_buffercache_normal;
6363
SET ROLE regress_buffercache_normal;
@@ -68,6 +68,12 @@ SELECT * FROM pg_buffercache_evict_relation(1);
6868
ERROR: must be superuser to use pg_buffercache_evict_relation()
6969
SELECT * FROM pg_buffercache_evict_all();
7070
ERROR: must be superuser to use pg_buffercache_evict_all()
71+
SELECT * FROM pg_buffercache_mark_dirty(1);
72+
ERROR: must be superuser to use pg_buffercache_mark_dirty()
73+
SELECT * FROM pg_buffercache_mark_dirty_relation(1);
74+
ERROR: must be superuser to use pg_buffercache_mark_dirty_relation()
75+
SELECT * FROM pg_buffercache_mark_dirty_all();
76+
ERROR: must be superuser to use pg_buffercache_mark_dirty_all()
7177
RESET ROLE;
7278
-- These should return nothing, because these are STRICT functions
7379
SELECT * FROM pg_buffercache_evict(NULL);
@@ -82,6 +88,18 @@ SELECT * FROM pg_buffercache_evict_relation(NULL);
8288
| |
8389
(1 row)
8490

91+
SELECT * FROM pg_buffercache_mark_dirty(NULL);
92+
buffer_dirtied | buffer_already_dirty
93+
----------------+----------------------
94+
|
95+
(1 row)
96+
97+
SELECT * FROM pg_buffercache_mark_dirty_relation(NULL);
98+
buffers_dirtied | buffers_already_dirty | buffers_skipped
99+
-----------------+-----------------------+-----------------
100+
| |
101+
(1 row)
102+
85103
-- These should fail because they are not called by valid range of buffers
86104
-- Number of the shared buffers are limited by max integer
87105
SELECT 2147483647 max_buffers \gset
@@ -91,11 +109,18 @@ SELECT * FROM pg_buffercache_evict(0);
91109
ERROR: bad buffer ID: 0
92110
SELECT * FROM pg_buffercache_evict(:max_buffers);
93111
ERROR: bad buffer ID: 2147483647
94-
-- This should fail because pg_buffercache_evict_relation() doesn't accept
95-
-- local relations
112+
SELECT * FROM pg_buffercache_mark_dirty(-1);
113+
ERROR: bad buffer ID: -1
114+
SELECT * FROM pg_buffercache_mark_dirty(0);
115+
ERROR: bad buffer ID: 0
116+
SELECT * FROM pg_buffercache_mark_dirty(:max_buffers);
117+
ERROR: bad buffer ID: 2147483647
118+
-- These should fail because they don't accept local relations
96119
CREATE TEMP TABLE temp_pg_buffercache();
97120
SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
98121
ERROR: relation uses local buffers, pg_buffercache_evict_relation() is intended to be used for shared buffers only
122+
SELECT * FROM pg_buffercache_mark_dirty_relation('temp_pg_buffercache');
123+
ERROR: relation uses local buffers, pg_buffercache_mark_dirty_relation() is intended to be used for shared buffers only
99124
DROP TABLE temp_pg_buffercache;
100125
-- These shouldn't fail
101126
SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
@@ -117,5 +142,23 @@ SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg
117142
t
118143
(1 row)
119144

145+
SELECT buffers_dirtied IS NOT NULL FROM pg_buffercache_mark_dirty_relation('shared_pg_buffercache');
146+
?column?
147+
----------
148+
t
149+
(1 row)
150+
120151
DROP TABLE shared_pg_buffercache;
152+
SELECT pg_buffercache_mark_dirty(1) IS NOT NULL;
153+
?column?
154+
----------
155+
t
156+
(1 row)
157+
158+
SELECT pg_buffercache_mark_dirty_all() IS NOT NULL;
159+
?column?
160+
----------
161+
t
162+
(1 row)
163+
121164
DROP ROLE regress_buffercache_normal;

contrib/pg_buffercache/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ install_data(
2424
'pg_buffercache--1.3--1.4.sql',
2525
'pg_buffercache--1.4--1.5.sql',
2626
'pg_buffercache--1.5--1.6.sql',
27+
'pg_buffercache--1.6--1.7.sql',
2728
'pg_buffercache.control',
2829
kwargs: contrib_data_args,
2930
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.7'" to load this file. \quit
5+
6+
CREATE FUNCTION pg_buffercache_mark_dirty(
7+
IN int,
8+
OUT buffer_dirtied boolean,
9+
OUT buffer_already_dirty boolean)
10+
AS 'MODULE_PATHNAME', 'pg_buffercache_mark_dirty'
11+
LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
12+
13+
CREATE FUNCTION pg_buffercache_mark_dirty_relation(
14+
IN regclass,
15+
OUT buffers_dirtied int4,
16+
OUT buffers_already_dirty int4,
17+
OUT buffers_skipped int4)
18+
AS 'MODULE_PATHNAME', 'pg_buffercache_mark_dirty_relation'
19+
LANGUAGE C PARALLEL SAFE VOLATILE STRICT;
20+
21+
CREATE FUNCTION pg_buffercache_mark_dirty_all(
22+
OUT buffers_dirtied int4,
23+
OUT buffers_already_dirty int4,
24+
OUT buffers_skipped int4)
25+
AS 'MODULE_PATHNAME', 'pg_buffercache_mark_dirty_all'
26+
LANGUAGE C PARALLEL SAFE VOLATILE;
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.6'
3+
default_version = '1.7'
44
module_pathname = '$libdir/pg_buffercache'
55
relocatable = true

contrib/pg_buffercache/pg_buffercache_pages.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#define NUM_BUFFERCACHE_EVICT_ELEM 2
2626
#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
2727
#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
28+
#define NUM_BUFFERCACHE_MARK_DIRTY_ELEM 2
29+
#define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM 3
30+
#define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM 3
2831

2932
#define NUM_BUFFERCACHE_NUMA_ELEM 3
3033

@@ -100,6 +103,9 @@ PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
100103
PG_FUNCTION_INFO_V1(pg_buffercache_evict);
101104
PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation);
102105
PG_FUNCTION_INFO_V1(pg_buffercache_evict_all);
106+
PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty);
107+
PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty_relation);
108+
PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty_all);
103109

104110

105111
/* Only need to touch memory once per backend process lifetime */
@@ -777,3 +783,120 @@ pg_buffercache_evict_all(PG_FUNCTION_ARGS)
777783

778784
PG_RETURN_DATUM(result);
779785
}
786+
787+
/*
788+
* Try to mark a shared buffer as dirty.
789+
*/
790+
Datum
791+
pg_buffercache_mark_dirty(PG_FUNCTION_ARGS)
792+
{
793+
794+
Datum result;
795+
TupleDesc tupledesc;
796+
HeapTuple tuple;
797+
Datum values[NUM_BUFFERCACHE_MARK_DIRTY_ELEM];
798+
bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ELEM] = {0};
799+
800+
Buffer buf = PG_GETARG_INT32(0);
801+
bool buffer_already_dirty;
802+
803+
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
804+
elog(ERROR, "return type must be a row type");
805+
806+
pg_buffercache_superuser_check("pg_buffercache_mark_dirty");
807+
808+
if (buf < 1 || buf > NBuffers)
809+
elog(ERROR, "bad buffer ID: %d", buf);
810+
811+
812+
values[0] = BoolGetDatum(MarkDirtyUnpinnedBuffer(buf, &buffer_already_dirty));
813+
values[1] = BoolGetDatum(buffer_already_dirty);
814+
815+
tuple = heap_form_tuple(tupledesc, values, nulls);
816+
result = HeapTupleGetDatum(tuple);
817+
818+
PG_RETURN_DATUM(result);
819+
}
820+
821+
/*
822+
* Try to mark specified relation dirty.
823+
*/
824+
Datum
825+
pg_buffercache_mark_dirty_relation(PG_FUNCTION_ARGS)
826+
{
827+
Datum result;
828+
TupleDesc tupledesc;
829+
HeapTuple tuple;
830+
Datum values[NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM];
831+
bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM] = {0};
832+
833+
Oid relOid;
834+
Relation rel;
835+
836+
int32 buffers_already_dirty = 0;
837+
int32 buffers_dirtied = 0;
838+
int32 buffers_skipped = 0;
839+
840+
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
841+
elog(ERROR, "return type must be a row type");
842+
843+
pg_buffercache_superuser_check("pg_buffercache_mark_dirty_relation");
844+
845+
relOid = PG_GETARG_OID(0);
846+
847+
rel = relation_open(relOid, AccessShareLock);
848+
849+
if (RelationUsesLocalBuffers(rel))
850+
ereport(ERROR,
851+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
852+
errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
853+
"pg_buffercache_mark_dirty_relation")));
854+
855+
MarkDirtyRelUnpinnedBuffers(rel, &buffers_dirtied, &buffers_already_dirty,
856+
&buffers_skipped);
857+
858+
relation_close(rel, AccessShareLock);
859+
860+
values[0] = Int32GetDatum(buffers_dirtied);
861+
values[1] = Int32GetDatum(buffers_already_dirty);
862+
values[2] = Int32GetDatum(buffers_skipped);
863+
864+
tuple = heap_form_tuple(tupledesc, values, nulls);
865+
result = HeapTupleGetDatum(tuple);
866+
867+
PG_RETURN_DATUM(result);
868+
}
869+
870+
/*
871+
* Try to mark all the shared buffers as dirty.
872+
*/
873+
Datum
874+
pg_buffercache_mark_dirty_all(PG_FUNCTION_ARGS)
875+
{
876+
Datum result;
877+
TupleDesc tupledesc;
878+
HeapTuple tuple;
879+
Datum values[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM];
880+
bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM] = {0};
881+
882+
int32 buffers_already_dirty = 0;
883+
int32 buffers_dirtied = 0;
884+
int32 buffers_skipped = 0;
885+
886+
if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
887+
elog(ERROR, "return type must be a row type");
888+
889+
pg_buffercache_superuser_check("pg_buffercache_mark_dirty_all");
890+
891+
MarkDirtyAllUnpinnedBuffers(&buffers_dirtied, &buffers_already_dirty,
892+
&buffers_skipped);
893+
894+
values[0] = Int32GetDatum(buffers_dirtied);
895+
values[1] = Int32GetDatum(buffers_already_dirty);
896+
values[2] = Int32GetDatum(buffers_skipped);
897+
898+
tuple = heap_form_tuple(tupledesc, values, nulls);
899+
result = HeapTupleGetDatum(tuple);
900+
901+
PG_RETURN_DATUM(result);
902+
}

contrib/pg_buffercache/sql/pg_buffercache.sql

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ RESET role;
3030

3131

3232
------
33-
---- Test pg_buffercache_evict* functions
33+
---- Test pg_buffercache_evict* and pg_buffercache_mark_dirty* functions
3434
------
3535

3636
CREATE ROLE regress_buffercache_normal;
@@ -40,31 +40,42 @@ SET ROLE regress_buffercache_normal;
4040
SELECT * FROM pg_buffercache_evict(1);
4141
SELECT * FROM pg_buffercache_evict_relation(1);
4242
SELECT * FROM pg_buffercache_evict_all();
43+
SELECT * FROM pg_buffercache_mark_dirty(1);
44+
SELECT * FROM pg_buffercache_mark_dirty_relation(1);
45+
SELECT * FROM pg_buffercache_mark_dirty_all();
4346

4447
RESET ROLE;
4548

4649
-- These should return nothing, because these are STRICT functions
4750
SELECT * FROM pg_buffercache_evict(NULL);
4851
SELECT * FROM pg_buffercache_evict_relation(NULL);
52+
SELECT * FROM pg_buffercache_mark_dirty(NULL);
53+
SELECT * FROM pg_buffercache_mark_dirty_relation(NULL);
4954

5055
-- These should fail because they are not called by valid range of buffers
5156
-- Number of the shared buffers are limited by max integer
5257
SELECT 2147483647 max_buffers \gset
5358
SELECT * FROM pg_buffercache_evict(-1);
5459
SELECT * FROM pg_buffercache_evict(0);
5560
SELECT * FROM pg_buffercache_evict(:max_buffers);
61+
SELECT * FROM pg_buffercache_mark_dirty(-1);
62+
SELECT * FROM pg_buffercache_mark_dirty(0);
63+
SELECT * FROM pg_buffercache_mark_dirty(:max_buffers);
5664

57-
-- This should fail because pg_buffercache_evict_relation() doesn't accept
58-
-- local relations
65+
-- These should fail because they don't accept local relations
5966
CREATE TEMP TABLE temp_pg_buffercache();
6067
SELECT * FROM pg_buffercache_evict_relation('temp_pg_buffercache');
68+
SELECT * FROM pg_buffercache_mark_dirty_relation('temp_pg_buffercache');
6169
DROP TABLE temp_pg_buffercache;
6270

6371
-- These shouldn't fail
6472
SELECT buffer_evicted IS NOT NULL FROM pg_buffercache_evict(1);
6573
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_all();
6674
CREATE TABLE shared_pg_buffercache();
6775
SELECT buffers_evicted IS NOT NULL FROM pg_buffercache_evict_relation('shared_pg_buffercache');
76+
SELECT buffers_dirtied IS NOT NULL FROM pg_buffercache_mark_dirty_relation('shared_pg_buffercache');
6877
DROP TABLE shared_pg_buffercache;
78+
SELECT pg_buffercache_mark_dirty(1) IS NOT NULL;
79+
SELECT pg_buffercache_mark_dirty_all() IS NOT NULL;
6980

7081
DROP ROLE regress_buffercache_normal;

0 commit comments

Comments
 (0)