Skip to content
Closed
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
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This software, pg_dropcache, is released under the terms of the PostgreSQL License.

Copyright (c) 2018, Ildar Musin.
Copyright (c) 2026, Pierre Forstmann.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this paragraph
and the following two paragraphs appear in all copies.

IN NO EVENT SHALL Pierre Forstmann BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF Pierre Forstmann
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Pierre Forstmann SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
AND Pierre Forstmann HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
48 changes: 48 additions & 0 deletions META.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "pg_dropcache",
"abstract": "drop buffer cache",
"version": "1.0.0",
"maintainer": [
"Pierre Forstmann"
],
"license": {
"PostgreSQL": "http://www.postgresql.org/about/licence"
},
"prereqs": {
"runtime": {
"requires": {
"PostgreSQL": "9.5.21"
},
"recommends": {
"PostgreSQL": "9.5.21"
}
}
},
"provides": {
"pg_dropcache": {
"abstract": "drop buffer cache",
"file": "pg_dropcache.c",
"docfile": "README.md",
"version": "1.0.0"
}
},
"resources": {
"bugtracker": {
"web": "http://github.com/pierreforstmann/pg_dropcache/issues"
},
"repository": {
"url": "https://github.com/pierreforstmann/pg_dropcache.git",
"web": "https://github.com/pierreforstmann/pg_dropcache",
"type": "git"
}
},
"generated_by": "Pierre Forstmann",
"meta-spec": {
"version": "1.0.0",
"url": "http://pgxn.org/meta/spec.txt"
},
"tags": [
"drop buffer cache",
"evict buffer cache"
]
}
17 changes: 8 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
# contrib/pg_dropcache/Makefile
# pg_dropcache Makefile

MODULE_big = pg_dropcache
OBJS = pg_dropcache.o $(WIN32RES)

REGRESS=test
REGRESS_OPTS = --temp-instance=/tmp/5555 --port=5555

EXTENSION = pg_dropcache
DATA = pg_dropcache--0.1.sql
DATA = pg_dropcache--1.0.0.sql
PGFILEDESC = "pg_dropcache - clears buffer cache"

ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/pg_dropcache
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

pgxn:
git archive --format zip --output ../pgxn/pg_dropcache/pg_dropcache-1.0.0.zip master
29 changes: 12 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# pg_dropcache

`pg_dropcache` is a PostgreSQL extension that invalidates `shared_buffers` cache
`pg_dropcache` is a PostgreSQL extension that invalidates `shared_buffers` cache.

PostgreSQL supported versions are 11, 12, 13, 14, 15 and 16.

PostgreSQL 17 and 18 are not supported because `pg_buffercache` extension provides following new functions:
- `pg_buffercache_evict()`
- `pg_buffercache_evict_relation()`
- `pg_buffercache_evict_all()`

## Installation

Expand All @@ -20,28 +27,16 @@ create extension pg_dropcache;

**WARNING**: Dirty pages will be just dropped, therefore they won't be flushed on the disk! It should be used with extreme caution!

To clear whole buffer cache run:
To clear whole buffer cache for the current database run:

```
select pg_dropcache();
select pg_drop_cache();
```

To clear cache buffers for just a single relation:

```
select pg_drop_rel_cache(<relation>);
select pg_drop_rel_cache(<relation oid>);
```

If you need to clear a specific buffer cache, you can specify it as second parameter:

```
select pg_drop_rel_cache(<relation>, <fork>);
```

`fork` can have one of the following values:
* 'main'
* 'vm'
* 'fsm'
* 'init'

Have fun!
Use it at your own risk.
78 changes: 78 additions & 0 deletions expected/test.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
create extension pg_dropcache;
create extension pg_buffercache;
--
create table t(x int);
insert into t select * from generate_series(1, 1000000);
select page_count > 4000 as page_count_gt_4000 from (
select count(*) as page_count from pg_buffercache where relfilenode = pg_relation_filenode('t'::regclass)
) as foo;
page_count_gt_4000
--------------------
t
(1 row)

select pg_drop_rel_cache('t'::regclass);
pg_drop_rel_cache
-------------------

(1 row)

select count(*) from pg_buffercache where relfilenode = pg_relation_filenode('t'::regclass);
count
-------
0
(1 row)

--
create table t1(x int);
create table t2(x int primary key);
insert into t1 select * from generate_series(1, 1000000);
insert into t2 select * from generate_series(1, 1000000);
select page_count_t1 > 4000 as page_count_t1_gt_4000 from (
select count(*) as page_count_t1 from pg_buffercache where relfilenode = pg_relation_filenode('t1'::regclass)
) as foo;
page_count_t1_gt_4000
-----------------------
t
(1 row)

select page_count_t2 > 4000 as page_count_t2_gt_4000 from (
select count(*) as page_count_t2 from pg_buffercache where relfilenode = pg_relation_filenode('t2'::regclass)
) as foo;
page_count_t2_gt_4000
-----------------------
t
(1 row)

select page_count_t2_pkey > 40 as page_count_t2_pkey_gt_40 from (
select count(*) as page_count_t2_pkey from pg_buffercache where relfilenode = pg_relation_filenode('t2_pkey'::regclass)
) as foo;
page_count_t2_pkey_gt_40
--------------------------
t
(1 row)

select pg_drop_cache();
pg_drop_cache
---------------

(1 row)

select count(*) from pg_buffercache where relfilenode = pg_relation_filenode('t1'::regclass);
count
-------
0
(1 row)

select count(*) from pg_buffercache where relfilenode = pg_relation_filenode('t2'::regclass);
count
-------
0
(1 row)

select count(*) from pg_buffercache where relfilenode = pg_relation_filenode('t2_pkey'::regclass);
count
-------
0
(1 row)

11 changes: 0 additions & 11 deletions pg_dropcache--0.1.sql

This file was deleted.

27 changes: 27 additions & 0 deletions pg_dropcache--1.0.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
\echo Use "CREATE EXTENSION pg_dropcache" to load this file. \quit

do
$$
declare
version int;
begin
select current_setting('server_version_num')::int / 10000 into version AS major_version;
if version >=17 then
raise exception 'PostgreSQL version % not supported: use pg_buffercache_evict functions from pg_buffercache extension', version;
end if;
if version < 11 then
raise exception 'PostgreSQL version % not supported', version;
end if;
end;
$$;


CREATE OR REPLACE FUNCTION pg_drop_cache()
RETURNS VOID
AS 'pg_dropcache', 'pg_drop_cache'
LANGUAGE C;

CREATE OR REPLACE FUNCTION pg_drop_rel_cache(relation regclass)
RETURNS VOID
AS 'pg_dropcache', 'pg_drop_rel_cache'
LANGUAGE C;
102 changes: 76 additions & 26 deletions pg_dropcache.c
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@

/*-------------------------------------------------------------------------
*
* pg_dropcache.c
*
*
* Portions Copyright (c) 2018, Ildar Musin
* https://github.com/zilder/pg_dropcache
*
*
* Portions Copyright (c) 2026, Pierre Forstmann
*
*-------------------------------------------------------------------------
*/

#include "postgres.h"
#include "storage/bufmgr.h"
#include "utils/syscache.h"
#include "utils/builtins.h"
#include "access/htup_details.h"
#include "miscadmin.h"
#include "catalog/pg_class.h"
#include "storage/smgr.h"
#include "common/relpath.h"
#include "utils/rel.h"
#if PG_VERSION_NUM >= 120000
#include "access/relation.h"
#else
#include "nodes/relation.h"
#endif

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(pg_dropcache);
PG_FUNCTION_INFO_V1(pg_drop_cache);
PG_FUNCTION_INFO_V1(pg_drop_rel_cache);

Datum
pg_dropcache(PG_FUNCTION_ARGS)
pg_drop_cache(PG_FUNCTION_ARGS)
{
DropDatabaseBuffers(MyDatabaseId);
PG_RETURN_VOID();
Expand All @@ -22,33 +45,60 @@ Datum
pg_drop_rel_cache(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int forkNum;
HeapTuple tp;
RelFileNodeBackend rnode;
BlockNumber nblocks = 0;
SMgrRelation srel;
#if PG_VERSION_NUM >= 120000
Relation rel;
#endif
#if PG_VERSION_NUM >= 130000
ForkNumber forks[MAX_FORKNUM];
int nforks = 0;
#endif
#if PG_VERSION_NUM < 140000
RelFileNodeBackend relfnode;
#endif
/*
* see RelationTruncate (storage.c)
* -> smgrtruncate (smgr.c)
* -> DropRelationBuffers (bufmgr.c)
*/

tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(tp))
for (ForkNumber fork = 0; fork < MAX_FORKNUM; fork++)
{
Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);

rnode.node.relNode = reltup->relfilenode;
rnode.node.spcNode = (reltup->reltablespace == InvalidOid) ?
MyDatabaseTableSpace :
reltup->reltablespace;
rnode.node.dbNode = MyDatabaseId;
rnode.backend = InvalidBackendId;

ReleaseSysCache(tp);
#if PG_VERSION_NUM >= 130000
forks[0] = fork;
nforks = 1;
#endif
#if PG_VERSION_NUM >= 120000
rel = relation_open(relid, AccessShareLock);
#else
Relation rel = RelationIdGetRelation(relid);
RelationOpenSmgr(rel);
#endif
#if PG_VERSION_NUM >= 160000
srel = smgropen(rel->rd_locator, rel->rd_backend);
DropRelationBuffers(srel, forks, nforks, &nblocks);
#elif PG_VERSION_NUM >=140000
srel = smgropen(rel->rd_node, InvalidBackendId);
DropRelFileNodeBuffers(srel, forks, nforks, &nblocks);
#elif PG_VERSION_NUM >= 130000
srel = smgropen(rel->rd_node, InvalidBackendId);
relfnode.node = rel->rd_node;
relfnode.backend = InvalidBackendId;
DropRelFileNodeBuffers(relfnode, forks, nforks, &nblocks);
#else
srel = smgropen(rel->rd_node, InvalidBackendId);
relfnode.node = rel->rd_node;
relfnode.backend = InvalidBackendId;
DropRelFileNodeBuffers(relfnode, fork, nblocks);
#endif
smgrclose(srel);
#if PG_VERSION_NUM >= 120000
relation_close(rel, AccessShareLock);
#else
RelationClose(rel);
#endif
}
else
{
elog(ERROR, "Unable to get cache for relation %u", relid);
PG_RETURN_VOID();
}

forkNum = PG_ARGISNULL(1) ? 0 : forkname_to_number(text_to_cstring(PG_GETARG_TEXT_P(1)));
for (; forkNum <= MAX_FORKNUM; ++forkNum)
DropRelFileNodeBuffers(rnode, forkNum, 0);

PG_RETURN_VOID();
}
Loading