Control your disk usage in PG: Introduction to Disk Quota Extension
Hi all,
We implement disk quota feature on Postgresql as an extension(link:
https://github.com/greenplum-db/diskquota),
If you are interested, try and use it to limit the amount of disk space that
a schema or a role can use in Postgresql.
Any feedback or question are appreciated.
Overview
Diskquota is an extension that provides disk usage enforcement for database
objects in Postgresql. Currently it supports to set quota limit on schema
and role in a given database and limit the amount of disk space that a
schema or a role can use.
This project is inspired by Heikki's pg_quota project (link:
https://github.com/hlinnaka/pg_quota) and enhance it to support different
kinds of DDL and DML which may change the disk usage of database objects.
Diskquota is a soft limit of disk uages. It has some delay to detect the
schemas or roles whose quota limit is exceeded. Here 'soft limit' supports
two kinds of encforcement: Query loading data into out-of-quota schema/role
will be forbidden before query is running. Query loading data into
schema/role with rooms will be cancelled when the quota limit is reached
dynamically during the query is running.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#design>
Design
Diskquota extension is based on background worker framework in Postgresql.
There are two kinds of background workers: diskquota launcher and diskquota
worker.
There is only one laucher process per database cluster(i.e. one laucher per
postmaster). Launcher process is reponsible for manage worker processes:
Calling RegisterDynamicBackgroundWorker() to create new workers and keep
their handle. Calling TerminateBackgroundWorker() to terminate workers
which are disabled when DBA modify diskquota.monitor_databases
There are many worker processes, one for each database which is listed in
diskquota.monitor_databases. Currently, we support to monitor at most 10
databases at the same time. Worker processes are responsible for monitoring
the disk usage of schemas and roles for the target database, and do quota
enfocement. It will periodically (can be set via diskquota.naptime)
recalcualte the table size of active tables, and update their corresponding
schema or owner's disk usage. Then compare with quota limit for those
schemas or roles. If exceeds the limit, put the corresponding schemas or
roles into the blacklist in shared memory. Schemas or roles in blacklist
are used to do query enforcement to cancel queries which plan to load data
into these schemas or roles.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#active-table>Active
table
Active tables are the tables whose table size may change in the last quota
check interval. We use hooks in smgecreate(), smgrextend() and
smgrtruncate() to detect active tables and store them(currently
relfilenode) in the shared memory. Diskquota worker process will
periodically consuming active table in shared memories, convert relfilenode
to relaton oid, and calcualte table size by calling
pg_total_relation_size(), which will sum the size of table(including: base,
vm, fsm, toast and index).
<https://github.com/greenplum-db/diskquota/blob/master/README.md#enforcement>
Enforcement
Enforcement is implemented as hooks. There are two kinds of enforcement
hooks: enforcement before query is running and enforcement during query is
running. The 'before query' one is implemented at ExecutorCheckPerms_hook
in function ExecCheckRTPerms() The 'during query' one is implemented at
BufferExtendCheckPerms_hook in function ReadBufferExtended(). Note that the
implementation of BufferExtendCheckPerms_hook will firstly check whether
function request a new block, if not skip directyly.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#quota-setting-store>Quota
setting store
Quota limit of a schema or a role is stored in table 'quota_config' in
'diskquota' schema in monitored database. So each database stores and
manages its own disk quota configuration. Note that although role is a db
object in cluster level, we limit the diskquota of a role to be database
specific. That is to say, a role may has different quota limit on different
databases and their disk usage is isolated between databases.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#install>
Install
1. Add hook functions to Postgres by applying patch. It's required since
disk quota need to add some new hook functions in postgres core. This step
would be skipped after patch is merged into postgres in future.
# install patch into postgres_src and rebuild postgres.
cd postgres_src;
git apply $diskquota_src/patch/pg_hooks.patch;
make;
make install;
1. Compile and install disk quota.
cd $diskquota_src;
make;
make install;
1. Config postgresql.conf
# enable diskquota in preload library.
shared_preload_libraries = 'diskquota'
# set monitored databases
diskquota.monitor_databases = 'postgres'
# set naptime (second) to refresh the disk quota stats periodically
diskquota.naptime = 2
# restart database to load preload library.
pg_ctl restart
1. Create diskquota extension in monitored database.
create extension diskquota;
1. Reload database configuraion
# reset monitored database list in postgresql.conf
diskquota.monitor_databases = 'postgres, postgres2'
# reload configuration
pg_ctl reload
<https://github.com/greenplum-db/diskquota/blob/master/README.md#usage>Usage
1. Set/update/delete schema quota limit using diskquota.set_schema_quota
create schema s1;
select diskquota.set_schema_quota('s1', '1 MB');
set search_path to s1;
create table a(i int);
# insert small data succeeded
insert into a select generate_series(1,100);
# insert large data failed
insert into a select generate_series(1,10000000);
# insert small data failed
insert into a select generate_series(1,100);
# delete quota configuration
select diskquota.set_schema_quota('s1', '-1');
# insert small data succeed
select pg_sleep(5);
insert into a select generate_series(1,100);
reset search_path;
1. Set/update/delete role quota limit using diskquota.set_role_quota
create role u1 nologin;
create table b (i int);
alter table b owner to u1;
select diskquota.set_role_quota('u1', '1 MB');
# insert small data succeeded
insert into b select generate_series(1,100);
# insert large data failed
insert into b select generate_series(1,10000000);
# insert small data failed
insert into b select generate_series(1,100);
# delete quota configuration
select diskquota.set_role_quota('u1', '-1');
# insert small data succeed
select pg_sleep(5);
insert into a select generate_series(1,100);
reset search_path;
1. Show schema quota limit and current usage
select * from diskquota.show_schema_quota_view;
<https://github.com/greenplum-db/diskquota/blob/master/README.md#test>Test
Run regression tests.
cd contrib/diskquota;
make installcheck
<https://github.com/greenplum-db/diskquota/blob/master/README.md#benchmark--performence-test>Benchmark
& Performence Test
<https://github.com/greenplum-db/diskquota/blob/master/README.md#cost-of-diskquota-worker>Cost
of diskquota worker
During each refresh interval, the disk quota worker need to refresh the
disk quota model.
It take less than 100ms under 100K user tables with no avtive tables.
It take less than 200ms under 100K user tables with 1K active tables.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#impact-on-oltp-queries>Impact
on OLTP queries
We test OLTP queries to measure the impact of enabling diskquota feature.
The range is from 2k tables to 10k tables. Each connection will insert 100
rows into each table. And the parallel connections range is from 5 to 25.
Number of active tables will be around 1k.
Without diskquota enabled (seconds)
2k4k6k8k10k
5 4.002 11.356 18.460 28.591 41.123
10 4.832 11.988 21.113 32.829 45.832
15 6.238 16.896 28.722 45.375 64.642
20 8.036 21.711 38.499 61.763 87.875
25 9.909 27.175 47.996 75.688 106.648
With diskquota enabled (seconds)
2k4k6k8k10k
5 4.135 10.641 18.776 28.804 41.740
10 4.773 12.407 22.351 34.243 47.568
15 6.355 17.305 30.941 46.967 66.216
20 9.451 22.231 40.645 61.758 88.309
25 10.096 26.844 48.910 76.537 108.025
The performance difference between with/without diskquota enabled are less
then 2-3% in most case. Therefore, there is no significant performance
downgrade when diskquota is enabled.
<https://github.com/greenplum-db/diskquota/blob/master/README.md#notes>Notes
1. Drop database with diskquota enabled.
If DBA enable monitoring diskquota on a database, there will be a
connection to this database from diskquota worker process. DBA need to
first remove this database from diskquota.monitor_databases in
postgres.conf, and reload configuration by call pg_ctl reload. Then
database could be dropped successfully.
1. Temp table.
Diskquota supports to limit the disk usage of temp table as well. But
schema and role are different. For role, i.e. the owner of the temp table,
diakquota will treat it the same as normal tables and sum its table size to
its owner's quota. While for schema, temp table is located under namespace
'pg_temp_backend_id', so temp table size will not sum to the current
schema's qouta.
--
Thanks
Hubert Zhang, Haozhou Wang, Hao Wu, Jack WU
On Tue, 2018-11-13 at 16:47 +0800, Hubert Zhang wrote:
Hi all,
We implement disk quota feature on Postgresql as an extension(link:
https://github.com/greenplum-db/diskquota),
If you are interested, try and use it to limit the amount of disk
space that
a schema or a role can use in Postgresql.
Any feedback or question are appreciated.
It's not clear to me what's the goal of this thread? I understand what
quotas are about, but are you sharing it because (a) it's a useful
extension, (b) you propose adding a couple of new hooks (and keep the
extension separate) or (c) you propose adding both the hooks and the
extension (into contrib)?
I assume it's either (b) and (c), in which case you should add it to
2019-01 CF: https://commitfest.postgresql.org/21/
In either case, it might be useful to add a LICENSE to the github
repository, it's not clear what's the situation in this respect. That
probably matters especially for (b), because for (c) it'd end up with
PostgreSQL license automatically.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Thanks, Tomas,
Yes, we want to add the hooks into postgres repo.
But before that, we plan to firstly get some feedbacks from community about
the diskquota extension implementation and usage?
Later, we'll modify our license and submit the hooks into CF.
Thanks
Hubert
On Wed, Nov 14, 2018 at 3:54 AM Tomas Vondra <tomas.vondra@2ndquadrant.com>
wrote:
On Tue, 2018-11-13 at 16:47 +0800, Hubert Zhang wrote:
Hi all,
We implement disk quota feature on Postgresql as an extension(link:
https://github.com/greenplum-db/diskquota),
If you are interested, try and use it to limit the amount of disk
space that
a schema or a role can use in Postgresql.
Any feedback or question are appreciated.It's not clear to me what's the goal of this thread? I understand what
quotas are about, but are you sharing it because (a) it's a useful
extension, (b) you propose adding a couple of new hooks (and keep the
extension separate) or (c) you propose adding both the hooks and the
extension (into contrib)?I assume it's either (b) and (c), in which case you should add it to
2019-01 CF: https://commitfest.postgresql.org/21/In either case, it might be useful to add a LICENSE to the github
repository, it's not clear what's the situation in this respect. That
probably matters especially for (b), because for (c) it'd end up with
PostgreSQL license automatically.regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Thanks
Hubert Zhang
Hi all,
We prepared a patch that includes the hook points. And such hook points are
needed for disk quota extension.
There are two hooks.
One is SmgrStat_hook. It's used to perform ad-hoc logic in storage when
doing smgr create/extend/truncate in general. As for disk quota extension,
this hook is used to detect active tables(new created tables, tables
extending new blocks, or tables being truncated)
The other is BufferExtendCheckPerms_hook. It's used to perform ad-hoc logic
when buffer extend a new block. Since ReadBufferExtended is a hot function,
we call this hook only when blockNum == P_NEW. As for disk quota
extension, this hook is used to do query enforcement during the query is
loading data.
Any comments are appreciated.
Regards,
Haozhou
On Wed, Nov 14, 2018 at 6:07 PM Hubert Zhang <hzhang@pivotal.io> wrote:
Show quoted text
Thanks, Tomas,
Yes, we want to add the hooks into postgres repo.
But before that, we plan to firstly get some feedbacks from community
about the diskquota extension implementation and usage?
Later, we'll modify our license and submit the hooks into CF.Thanks
HubertOn Wed, Nov 14, 2018 at 3:54 AM Tomas Vondra <tomas.vondra@2ndquadrant.com>
wrote:On Tue, 2018-11-13 at 16:47 +0800, Hubert Zhang wrote:
Hi all,
We implement disk quota feature on Postgresql as an extension(link:
https://github.com/greenplum-db/diskquota),
If you are interested, try and use it to limit the amount of disk
space that
a schema or a role can use in Postgresql.
Any feedback or question are appreciated.It's not clear to me what's the goal of this thread? I understand what
quotas are about, but are you sharing it because (a) it's a useful
extension, (b) you propose adding a couple of new hooks (and keep the
extension separate) or (c) you propose adding both the hooks and the
extension (into contrib)?I assume it's either (b) and (c), in which case you should add it to
2019-01 CF: https://commitfest.postgresql.org/21/In either case, it might be useful to add a LICENSE to the github
repository, it's not clear what's the situation in this respect. That
probably matters especially for (b), because for (c) it'd end up with
PostgreSQL license automatically.regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services--
ThanksHubert Zhang
Attachments:
disk_quota_hooks_v1.patchapplication/octet-stream; name=disk_quota_hooks_v1.patchDownload
From ff8686c23badd5602bfb997c4fe761c19fa66f9e Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Tue, 6 Nov 2018 06:51:22 +0000
Subject: [PATCH] Add hooks for diskquota extension.
Add BufferExtendCheckPerms_hook to support quota enforcement
Add SmgrStat_hook to detect active relfilenodes.
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/buffer/bufmgr.c | 14 ++++++++++++++
src/backend/storage/smgr/smgr.c | 21 ++++++++++++++++++++-
src/include/storage/bufmgr.h | 8 ++++++++
src/include/storage/smgr.h | 6 ++++++
4 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 01eabe5..d977350 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -104,6 +104,13 @@ typedef struct CkptTsStatus
int index;
} CkptTsStatus;
+/*
+ * Hook for plugins to check permissions when doing a buffer extend.
+ * One example is to check whether there is additional disk quota for
+ * the table to be inserted.
+ */
+BufferExtendCheckPerms_hook_type BufferExtendCheckPerms_hook = NULL;
+
/* GUC variables */
bool zero_damaged_pages = false;
int bgwriter_lru_maxpages = 100;
@@ -661,6 +668,13 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
* miss.
*/
pgstat_count_buffer_read(reln);
+
+ /* check permissions when doing a buffer extend */
+ if (blockNum == P_NEW && BufferExtendCheckPerms_hook)
+ {
+ (*BufferExtendCheckPerms_hook)(reln->rd_id, blockNum);
+ }
+
buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence,
forkNum, blockNum, mode, strategy, &hit);
if (hit)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 189342e..c5b218e 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -90,7 +90,11 @@ static const f_smgr smgrsw[] = {
static const int NSmgr = lengthof(smgrsw);
-
+/*
+ * Hook for plugins to collect statistics from smgr functions
+ * One example is to record the active relfilenode information.
+ */
+SmgrStat_hook_type SmgrStat_hook = NULL;
/*
* Each backend has a hashtable that stores all extant SMgrRelation objects.
* In addition, "unowned" SMgrRelation objects are chained together in a list.
@@ -411,6 +415,11 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
isRedo);
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
+
+ if (SmgrStat_hook)
+ {
+ (*SmgrStat_hook)(reln);
+ }
}
/*
@@ -617,6 +626,11 @@ smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
{
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
+
+ if (SmgrStat_hook)
+ {
+ (*SmgrStat_hook)(reln);
+ }
}
/*
@@ -720,6 +734,11 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
* Do the truncation.
*/
smgrsw[reln->smgr_which].smgr_truncate(reln, forknum, nblocks);
+
+ if (SmgrStat_hook)
+ {
+ (*SmgrStat_hook)(reln);
+ }
}
/*
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 3cce390..153a7d3 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -160,6 +160,14 @@ extern PGDLLIMPORT int32 *LocalRefCount;
#define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
/*
+ * Hook for plugins to check permissions when doing a buffer extend.
+ * One example is to check whether there is additional disk quota for
+ * the table to be inserted.
+ */
+typedef bool (*BufferExtendCheckPerms_hook_type) (Oid, BlockNumber);
+extern PGDLLIMPORT BufferExtendCheckPerms_hook_type BufferExtendCheckPerms_hook;
+
+/*
* prototypes for functions in bufmgr.c
*/
extern bool ComputeIoConcurrency(int io_concurrency, double *target);
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index c843bbc..918e590 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -144,5 +144,11 @@ extern void RememberFsyncRequest(RelFileNode rnode, ForkNumber forknum,
extern void ForgetRelationFsyncRequests(RelFileNode rnode, ForkNumber forknum);
extern void ForgetDatabaseFsyncRequests(Oid dbid);
extern void DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo);
+/*
+ * Hook for plugins to collect statistics from smgr functions
+ * One example is to record the active relfilenode information.
+ */
+typedef void (*SmgrStat_hook_type)(SMgrRelation sreln);
+extern PGDLLIMPORT SmgrStat_hook_type SmgrStat_hook;
#endif /* SMGR_H */
--
1.8.3.1
On Tue, Nov 20, 2018 at 2:20 AM Haozhou Wang <hawang@pivotal.io> wrote:
We prepared a patch that includes the hook points. And such hook points are needed for disk quota extension.
There are two hooks.
One is SmgrStat_hook. It's used to perform ad-hoc logic in storage when doing smgr create/extend/truncate in general. As for disk quota extension, this hook is used to detect active tables(new created tables, tables extending new blocks, or tables being truncated)
The other is BufferExtendCheckPerms_hook. It's used to perform ad-hoc logic when buffer extend a new block. Since ReadBufferExtended is a hot function, we call this hook only when blockNum == P_NEW. As for disk quota extension, this hook is used to do query enforcement during the query is loading data.Any comments are appreciated.
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Thank you very much for your review.
We refactored our patch with new names and comments.
For ReadBufferExtended hook, yes, Readbuffer with P_NEW will then call
smgrextend.
But in smgrextend, we cannot get the oid of a relation, and it will take
some time to get the oid via smgrrelation.
We would like to add a hook just before the smgrextend to get the oid and
avoid use RelidByRelfilenode().
New patch is attached in the attachment.
Thank a lot!
Regards,
Haozhou
On Wed, Nov 21, 2018 at 10:48 PM Robert Haas <robertmhaas@gmail.com> wrote:
Show quoted text
On Tue, Nov 20, 2018 at 2:20 AM Haozhou Wang <hawang@pivotal.io> wrote:
We prepared a patch that includes the hook points. And such hook points
are needed for disk quota extension.
There are two hooks.
One is SmgrStat_hook. It's used to perform ad-hoc logic in storage whendoing smgr create/extend/truncate in general. As for disk quota extension,
this hook is used to detect active tables(new created tables, tables
extending new blocks, or tables being truncated)The other is BufferExtendCheckPerms_hook. It's used to perform ad-hoc
logic when buffer extend a new block. Since ReadBufferExtended is a hot
function, we call this hook only when blockNum == P_NEW. As for disk quota
extension, this hook is used to do query enforcement during the query is
loading data.Any comments are appreciated.
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
disk_quota_hooks_v2.patchapplication/octet-stream; name=disk_quota_hooks_v2.patchDownload
From 3d275c78b304b308d288bd227f6dcab45dc5f595 Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Tue, 6 Nov 2018 06:51:22 +0000
Subject: [PATCH] Add hooks for diskquota extension.
Add ReadBufferExtend_hook() and smgr*_hook()
hook points to extend logic of storage
management.
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/buffer/bufmgr.c | 14 ++++++++++++++
src/backend/storage/smgr/smgr.c | 33 +++++++++++++++++++++++++++++++++
src/include/storage/bufmgr.h | 10 ++++++++++
src/include/storage/smgr.h | 18 ++++++++++++++++++
4 files changed, 75 insertions(+)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 01eabe5706..5499495506 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -104,6 +104,13 @@ typedef struct CkptTsStatus
int index;
} CkptTsStatus;
+/*
+ * Hook for plugins to check permissions when doing a buffer extend.
+ * One example is to check whether there is additional disk quota for
+ * the table to be inserted.
+ */
+ReadBufferExtended_hook_type ReadBufferExtended_hook = NULL;
+
/* GUC variables */
bool zero_damaged_pages = false;
int bgwriter_lru_maxpages = 100;
@@ -661,6 +668,13 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
* miss.
*/
pgstat_count_buffer_read(reln);
+
+ /* hook function for doing a buffer extend */
+ if (blockNum == P_NEW && ReadBufferExtended_hook)
+ {
+ (*ReadBufferExtended_hook)(reln, forkNum, blockNum, mode, strategy);
+ }
+
buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence,
forkNum, blockNum, mode, strategy, &hit);
if (hit)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 189342ef86..fa36a18e15 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -90,6 +90,16 @@ static const f_smgr smgrsw[] = {
static const int NSmgr = lengthof(smgrsw);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* Each backend has a hashtable that stores all extant SMgrRelation objects.
@@ -397,6 +407,11 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
if (isRedo && reln->md_num_open_segs[forknum] > 0)
return;
+ if (smgrcreate_hook)
+ {
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+ }
+
/*
* We may be using the target table space for the first time in this
* database, so create a per-database subdirectory if needed.
@@ -411,6 +426,7 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
isRedo);
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
+
}
/*
@@ -492,6 +508,11 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ {
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+ }
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -615,8 +636,14 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ {
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+ }
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
+
}
/*
@@ -698,6 +725,11 @@ smgrnblocks(SMgrRelation reln, ForkNumber forknum)
void
smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
{
+ if (smgrtruncate_hook)
+ {
+ (*smgrtruncate_hook)(reln, forknum, nblocks);
+ }
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
@@ -720,6 +752,7 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
* Do the truncation.
*/
smgrsw[reln->smgr_which].smgr_truncate(reln, forknum, nblocks);
+
}
/*
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 3cce3906a0..f1dcc77bf7 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -159,6 +159,16 @@ extern PGDLLIMPORT int32 *LocalRefCount;
*/
#define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
+/*
+ * Hook for plugins to add external logic when doing a buffer extend.
+ * One example is to check whether there is additional disk quota for
+ * the table to be inserted.
+ */
+typedef bool (*ReadBufferExtended_hook_type) (Relation reln,
+ ForkNumber forkNum, BlockNumber blockNum,
+ ReadBufferMode mode, BufferAccessStrategy strategy);
+extern PGDLLIMPORT ReadBufferExtended_hook_type ReadBufferExtended_hook;
+
/*
* prototypes for functions in bufmgr.c
*/
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index c843bbc969..d070b3d573 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -144,5 +144,23 @@ extern void RememberFsyncRequest(RelFileNode rnode, ForkNumber forknum,
extern void ForgetRelationFsyncRequests(RelFileNode rnode, ForkNumber forknum);
extern void ForgetDatabaseFsyncRequests(Oid dbid);
extern void DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
#endif /* SMGR_H */
--
2.16.2
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?
For the usercase in diskquota.
BufferExtendCheckPerms_hook is used to do dynamic query enforcement, while
smgr related hooks are used to detect relfilenodeoid of active tables and
write them into shared memory(which is used to accelerate refreshing of
diskquota model).
The reason we don't use smgr_extend hook to replace ReadBuffer hook to do
enforcement has two folds:
1. As for enforcement, we don't want to affect the performance of insert
query. But hooks in smgr_extend need to convert relfilenode to reloid
firstly which need an indexscan.
2. Using hooks in ReadBuffer instead of smgr_extend could avoid to
enforcement on 'cluster relation' operator. For example, 'vacuum full
table' will firstly cluster and create a new table, and then delete the old
table. Because the disk usage will first grow and then shrink, if quota
limit is reached, then vacuum full will fail.(but in fact we want vacuum
full to reduce disk usage) Using hooks in ReadBuffer is one solution to
this problem. Of course, there are other solutions. But This is one of the
reason we use BufferExtendCheckPerms_hook to do enforcement at current
stage.
On Thu, Nov 22, 2018 at 7:26 PM Haozhou Wang <hawang@pivotal.io> wrote:
Thank you very much for your review.
We refactored our patch with new names and comments.For ReadBufferExtended hook, yes, Readbuffer with P_NEW will then call
smgrextend.But in smgrextend, we cannot get the oid of a relation, and it will take
some time to get the oid via smgrrelation.
We would like to add a hook just before the smgrextend to get the oid and
avoid use RelidByRelfilenode().New patch is attached in the attachment.
Thank a lot!Regards,
HaozhouOn Wed, Nov 21, 2018 at 10:48 PM Robert Haas <robertmhaas@gmail.com>
wrote:On Tue, Nov 20, 2018 at 2:20 AM Haozhou Wang <hawang@pivotal.io> wrote:
We prepared a patch that includes the hook points. And such hook points
are needed for disk quota extension.
There are two hooks.
One is SmgrStat_hook. It's used to perform ad-hoc logic in storage whendoing smgr create/extend/truncate in general. As for disk quota extension,
this hook is used to detect active tables(new created tables, tables
extending new blocks, or tables being truncated)The other is BufferExtendCheckPerms_hook. It's used to perform ad-hoc
logic when buffer extend a new block. Since ReadBufferExtended is a hot
function, we call this hook only when blockNum == P_NEW. As for disk quota
extension, this hook is used to do query enforcement during the query is
loading data.Any comments are appreciated.
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Thanks
Hubert Zhang
On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.
I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?
Yes, that's a bit awkward.
--
Michael
Thanks very much for your comments.
To the best of my knowledge, smgr is a layer that abstract the storage
operations. Therefore, it is a good place to control or collect information
the storage operations without touching the physical storage layer.
Moreover, smgr is coming with actual disk IO operation (not consider the OS
cache) for postgres. So we do not need to worry about the buffer management
in postgres.
It will make the purpose of hook is pure: a hook for actual disk IO.
Regards,
Haozhou
On Wed, Dec 26, 2018 at 1:56 PM Michael Paquier <michael@paquier.xyz> wrote:
Show quoted text
On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
--
Michael
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
Hi Michael, we revisit the ReadBuffer hook and remove it in the latest
patch.
ReadBuffer hook is original used to do enforcement(e.g. out of diskquota
limit) when query is loading data.
We plan to put the enforcement work of running query to separate diskquota
worker process.
Let worker process to detect the backends to be cancelled and send SIGINT
to these backends.
So there is no need for ReadBuffer hook anymore.
Our patch currently only contains smgr related hooks to catch the file
change and get the Active Table list for diskquota extension.
Thanks Hubert.
On Mon, Jan 7, 2019 at 6:56 PM Haozhou Wang <hawang@pivotal.io> wrote:
Thanks very much for your comments.
To the best of my knowledge, smgr is a layer that abstract the storage
operations. Therefore, it is a good place to control or collect information
the storage operations without touching the physical storage layer.
Moreover, smgr is coming with actual disk IO operation (not consider the
OS cache) for postgres. So we do not need to worry about the buffer
management in postgres.
It will make the purpose of hook is pure: a hook for actual disk IO.Regards,
HaozhouOn Wed, Dec 26, 2018 at 1:56 PM Michael Paquier <michael@paquier.xyz>
wrote:On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
--
Michael
--
Thanks
Hubert Zhang
Attachments:
disk_quota_hooks_v3.patchapplication/octet-stream; name=disk_quota_hooks_v3.patchDownload
From 4aac2c69cbb0001be5e95b6d482fc3b032208f81 Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Tue, 22 Jan 2019 11:46:29 +0800
Subject: [PATCH] Add smgr*_hook() hook points to extend the logic of storage
management.
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 30 ++++++++++++++++++++++++++++++
src/include/storage/smgr.h | 18 ++++++++++++++++++
2 files changed, 48 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 0c0bba4ab3..b4ba9d9a4d 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -90,6 +90,16 @@ static const f_smgr smgrsw[] = {
static const int NSmgr = lengthof(smgrsw);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* Each backend has a hashtable that stores all extant SMgrRelation objects.
@@ -397,6 +407,11 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
if (isRedo && reln->md_num_open_segs[forknum] > 0)
return;
+ if (smgrcreate_hook)
+ {
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+ }
+
/*
* We may be using the target table space for the first time in this
* database, so create a per-database subdirectory if needed.
@@ -492,6 +507,11 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ {
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+ }
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -615,6 +635,11 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ {
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+ }
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
}
@@ -698,6 +723,11 @@ smgrnblocks(SMgrRelation reln, ForkNumber forknum)
void
smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
{
+ if (smgrtruncate_hook)
+ {
+ (*smgrtruncate_hook)(reln, forknum, nblocks);
+ }
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 820d08ed4e..5426543b5c 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -144,5 +144,23 @@ extern void RememberFsyncRequest(RelFileNode rnode, ForkNumber forknum,
extern void ForgetRelationFsyncRequests(RelFileNode rnode, ForkNumber forknum);
extern void ForgetDatabaseFsyncRequests(Oid dbid);
extern void DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
#endif /* SMGR_H */
--
2.11.0 (Apple Git-81)
Hi Michael, Robert
For you question about the hook position, I want to explain more about the
background why we want to introduce these hooks.
We wrote a diskquota extension <https://github.com/greenplum-db/diskquota>
for Postgresql(which is inspired by Heikki's pg_quota
<https://github.com/hlinnaka/pg_quota>). Diskquota extension is used to
control the disk usage in Postgresql in a fine-grained way, which means:
1. You could set disk quota limit at schema level or role level.
2. A background worker will gather the current disk usage for each
schema/role in realtime.
3. A background worker will generate the blacklist for schema/role whose
quota limit is exceeded.
4. New transaction want to insert data into the schema/role in the
blacklist will be cancelled.
In step 2, gathering the current disk usage for each schema needs to sum
disk size of all the tables in this schema. This is a time consuming
operation. We want to use hooks in SMGR to detect the Active Table, and
only recalculate the disk size of all the Active Tables.
For example, the smgrextend hook indicates that you allocate a new block
and the table need to be treated as Active Table.
Do you have some better hook positions recommend to solve the above user
case?
Thanks in advance.
Hubert
On Tue, Jan 22, 2019 at 12:08 PM Hubert Zhang <hzhang@pivotal.io> wrote:
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
Hi Michael, we revisit the ReadBuffer hook and remove it in the latest
patch.
ReadBuffer hook is original used to do enforcement(e.g. out of diskquota
limit) when query is loading data.
We plan to put the enforcement work of running query to separate diskquota
worker process.
Let worker process to detect the backends to be cancelled and send SIGINT
to these backends.
So there is no need for ReadBuffer hook anymore.Our patch currently only contains smgr related hooks to catch the file
change and get the Active Table list for diskquota extension.Thanks Hubert.
On Mon, Jan 7, 2019 at 6:56 PM Haozhou Wang <hawang@pivotal.io> wrote:
Thanks very much for your comments.
To the best of my knowledge, smgr is a layer that abstract the storage
operations. Therefore, it is a good place to control or collect information
the storage operations without touching the physical storage layer.
Moreover, smgr is coming with actual disk IO operation (not consider the
OS cache) for postgres. So we do not need to worry about the buffer
management in postgres.
It will make the purpose of hook is pure: a hook for actual disk IO.Regards,
HaozhouOn Wed, Dec 26, 2018 at 1:56 PM Michael Paquier <michael@paquier.xyz>
wrote:On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
--
Michael--
ThanksHubert Zhang
--
Thanks
Hubert Zhang
Hi all,
Currently we add hooks in SMGR to detect which table is being modified(disk
size change).
Inserting rows into existing page with room will not introduce new block,
and thus should not be treated as active table. smgrextend is a good
position to meet this behavior.
We welcome suggestions on other better hook positions!
Besides, suppose we use smgr as hook position, I want to discuss the API
passed to the hook function.
Take smgrextend as example. The function interface of smgrextend is like
that:
```
void smgrextend
(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer,
bool skipFsync);
```
So the hook api of smgrextend could have two main options.
Hook API Option1
```
typedef void (*smgrextend_hook_type)
(RelFileNode smgr_rnode,ForkNumber forknum);
```
Hook API Option 2
```
typedef void (*smgrextend_hook_type)
(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,char *buffer,
bool skipFsync);
```
As for Option1. Since diskquota extension only needs the relfilenode
information to detect the active tables, Option1 just pass the RelFileNode
to the hook function. It's more clear and has semantic meaning.
While Option 2 is to pass the original parameters to the hook functions
without any filter. This is more general and let the different hook
implementations to decide how to use these parameters.
Option 1 also needs some additional work to handle smgrdounlinkall case,
whose input parameter is the SMgrRelation list. We may need to palloc
Relfilenode list and pfree it manually.
smgrdounlinkall function interface:
```
smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo, char
*relstorages)
```
Based on the assumption we use smgr as hook position, hook API option1 or
option2 which is better?
Or we could find some balanced API between option1 and option2?
Again comments on other better hook positions are appreciated!
Thanks
Hubert
On Wed, Jan 30, 2019 at 10:26 AM Hubert Zhang <hzhang@pivotal.io> wrote:
Hi Michael, Robert
For you question about the hook position, I want to explain more about the
background why we want to introduce these hooks.
We wrote a diskquota extension <https://github.com/greenplum-db/diskquota>
for Postgresql(which is inspired by Heikki's pg_quota
<https://github.com/hlinnaka/pg_quota>). Diskquota extension is used to
control the disk usage in Postgresql in a fine-grained way, which means:
1. You could set disk quota limit at schema level or role level.
2. A background worker will gather the current disk usage for each
schema/role in realtime.
3. A background worker will generate the blacklist for schema/role whose
quota limit is exceeded.
4. New transaction want to insert data into the schema/role in the
blacklist will be cancelled.In step 2, gathering the current disk usage for each schema needs to sum
disk size of all the tables in this schema. This is a time consuming
operation. We want to use hooks in SMGR to detect the Active Table, and
only recalculate the disk size of all the Active Tables.
For example, the smgrextend hook indicates that you allocate a new block
and the table need to be treated as Active Table.Do you have some better hook positions recommend to solve the above user
case?
Thanks in advance.Hubert
On Tue, Jan 22, 2019 at 12:08 PM Hubert Zhang <hzhang@pivotal.io> wrote:
For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
Hi Michael, we revisit the ReadBuffer hook and remove it in the latest
patch.
ReadBuffer hook is original used to do enforcement(e.g. out of diskquota
limit) when query is loading data.
We plan to put the enforcement work of running query to separate
diskquota worker process.
Let worker process to detect the backends to be cancelled and send SIGINT
to these backends.
So there is no need for ReadBuffer hook anymore.Our patch currently only contains smgr related hooks to catch the file
change and get the Active Table list for diskquota extension.Thanks Hubert.
On Mon, Jan 7, 2019 at 6:56 PM Haozhou Wang <hawang@pivotal.io> wrote:
Thanks very much for your comments.
To the best of my knowledge, smgr is a layer that abstract the storage
operations. Therefore, it is a good place to control or collect information
the storage operations without touching the physical storage layer.
Moreover, smgr is coming with actual disk IO operation (not consider the
OS cache) for postgres. So we do not need to worry about the buffer
management in postgres.
It will make the purpose of hook is pure: a hook for actual disk IO.Regards,
HaozhouOn Wed, Dec 26, 2018 at 1:56 PM Michael Paquier <michael@paquier.xyz>
wrote:On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
--
Michael--
ThanksHubert Zhang
--
ThanksHubert Zhang
--
Thanks
Hubert Zhang
Hi,
On 2019-01-30 10:26:52 +0800, Hubert Zhang wrote:
Hi Michael, Robert
For you question about the hook position, I want to explain more about the
background why we want to introduce these hooks.
We wrote a diskquota extension <https://github.com/greenplum-db/diskquota>
[ ...]
On Tue, Jan 22, 2019 at 12:08 PM Hubert Zhang <hzhang@pivotal.io> wrote:For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
Please note that on PG lists the customary and desired style is to quote
inline in messages rather than top-quote.
Greetings,
Andres Freund
Hi Andres
On Sat, Feb 16, 2019 at 12:53 PM Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2019-01-30 10:26:52 +0800, Hubert Zhang wrote:Hi Michael, Robert
For you question about the hook position, I want to explain more aboutthe
background why we want to introduce these hooks.
We wrote a diskquota extension <https://github.com/greenplum-db/diskquota>
[ ...]
On Tue, Jan 22, 2019 at 12:08 PM Hubert Zhang <hzhang@pivotal.io> wrote:For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?Yes, that's a bit awkward.
Please note that on PG lists the customary and desired style is to quote
inline in messages rather than top-quote.Greetings,
Andres Freund
Thanks for your note. I will reply the above questions again.
On Wed, Nov 21, 2018 at 10:48 PM Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Nov 20, 2018 at 2:20 AM Haozhou Wang <hawang@pivotal.io> wrote:
We prepared a patch that includes the hook points. And such hook points
are needed for disk quota extension.
There are two hooks.
One is SmgrStat_hook. It's used to perform ad-hoc logic in storage whendoing smgr create/extend/truncate in general. As for disk quota extension,
this hook is used to detect active tables(new created tables, tables
extending new blocks, or tables being truncated)The other is BufferExtendCheckPerms_hook. It's used to perform ad-hoc
logic when buffer extend a new block. Since ReadBufferExtended is a hot
function, we call this hook only when blockNum == P_NEW. As for disk quota
extension, this hook is used to do query enforcement during the query is
loading data.Any comments are appreciated.
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.For this particular purpose, I don't immediately see why you need a
hook in both places. If ReadBuffer is called with P_NEW, aren't we
guaranteed to end up in smgrextend()?
We have removed the hook in ReadBufferExtended and only keep the hooks in
SMGR.
On Wed, Dec 26, 2018 at 1:56 PM Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Nov 21, 2018 at 09:47:44AM -0500, Robert Haas wrote:
+1 for adding some hooks to support this kind of thing, but I think
the names you've chosen are not very good. The hook name should
describe the place from which it is called, not the purpose for which
one imagines that it will be used, because somebody else might imagine
another use. Both BufferExtendCheckPerms_hook_type and
SmgrStat_hook_type are imagining that they know what the hook does -
CheckPerms in the first case and Stat in the second case.I personally don't mind making Postgres more pluggable, but I don't
think that we actually need the extra ones proposed here at the layer
of smgr, as smgr is already a layer designed to call an underlying set
of APIs able to extend, unlink, etc. depending on the storage type.
The reason to use smgr as the hook position is as follows:
These hooks will be used by diskquota extension, which needs to gather the
current disk usage for each schema. We want to use hooks to detect the
Active Tables, and only recalculate the disk size of all the Active Tables.
As you mentioned, smgr is the layer to call underlying API to extend/unlink
files. So it's also the place to detect the table size change, i.e. the
Active Tables.
For example, the smgrextend hook indicates that you allocate a new block
and the corresponding table needs to be treated as Active Table.
Besides, suppose we use smgr as hook position, I want to discuss the API
passed to the hook function.
Take smgrextend as example. The function interface of smgrextend is like
that:
```
void smgrextend
(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer,
bool skipFsync);
```
So the hook api of smgrextend could have two main options.
Hook API Option1
```
typedef void (*smgrextend_hook_type)
(RelFileNode smgr_rnode,ForkNumber forknum);
```
Hook API Option 2
```
typedef void (*smgrextend_hook_type)
(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,char *buffer,
bool skipFsync);
```
As for Option1. Since diskquota extension only needs the relfilenode
information to detect the active tables, Option1 just pass the RelFileNode
to the hook function. It's more clear and has semantic meaning.
While Option 2 is to pass the original parameters to the hook functions
without any filter. This is more general and let the different hook
implementations to decide how to use these parameters.
Option 1 also needs some additional work to handle smgrdounlinkall case,
whose input parameter is the SMgrRelation list. We may need to palloc
Relfilenode list and pfree it manually.
smgrdounlinkall function interface:
```
smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo, char
*relstorages)
```
Based on the assumption we use smgr as hook position, hook API option1 or
option2 which is better?
Or we could find some balanced API between option1 and option2?
Again comments on other better hook positions are also appreciated!
--
Thanks
Hubert Zhang
On Mon, Feb 18, 2019 at 7:39 PM Hubert Zhang <hzhang@pivotal.io> wrote:
Based on the assumption we use smgr as hook position, hook API option1 or option2 which is better?
Or we could find some balanced API between option1 and option2?Again comments on other better hook positions are also appreciated!
Hi Hubert,
The July Commitfest is now running, and this entry is in "needs
review" state. Could you please post a rebased patch?
I have questions about how disk quotas should work and I think we'll
probably eventually want more hooks than these, but simply adding
these hooks so extensions can do whatever they want doesn't seem very
risky for core. I think it's highly likely that the hook signatures
will have to change in future releases too, but that seems OK for such
detailed internal hooks. As for your question, my first reaction was
that I preferred your option 1, because SMgrRelation seems quite
private and there are no existing examples of that object being
exposed to extensions. But on reflection, other callbacks don't take
such a mollycoddling approach. So my vote is for option 2 "just pass
all the arguments to the callback", which I understand to be the
approach of patch you have posted.
+ if (smgrcreate_hook)
+ {
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+ }
Usually we don't use curlies for single line if branches.
--
Thomas Munro
https://enterprisedb.com
Thanks, Thomas.
On Mon, Jul 8, 2019 at 6:47 AM Thomas Munro <thomas.munro@gmail.com> wrote:
On Mon, Feb 18, 2019 at 7:39 PM Hubert Zhang <hzhang@pivotal.io> wrote:
Based on the assumption we use smgr as hook position, hook API option1
or option2 which is better?
Or we could find some balanced API between option1 and option2?
Again comments on other better hook positions are also appreciated!
Hi Hubert,
The July Commitfest is now running, and this entry is in "needs
review" state. Could you please post a rebased patch?I have questions about how disk quotas should work and I think we'll
probably eventually want more hooks than these, but simply adding
these hooks so extensions can do whatever they want doesn't seem very
risky for core. I think it's highly likely that the hook signatures
will have to change in future releases too, but that seems OK for such
detailed internal hooks. As for your question, my first reaction was
that I preferred your option 1, because SMgrRelation seems quite
private and there are no existing examples of that object being
exposed to extensions. But on reflection, other callbacks don't take
such a mollycoddling approach. So my vote is for option 2 "just pass
all the arguments to the callback", which I understand to be the
approach of patch you have posted.+ if (smgrcreate_hook) + { + (*smgrcreate_hook)(reln, forknum, isRedo); + }Usually we don't use curlies for single line if branches.
I have rebased the patch to v4 and removed the unnecessary braces.
As your comments, Options 2 is still used in patch v4.
Agree that diskquota extension may use more hooks in future.
Currently the behavior of diskquota extension is that we use smgr hooks to
detect active tables and record them in the shared memory. Bgworkers of
diskquota extension will read these active tables from shared memory and
calculate the latest table size and sum them into the size of schema or
role. If size of schema of role exceeds their quota limit, they will be put
into a black list in shared memory. When a new query comes,
ExecutorCheckPerms_hook will be used to check the black list the cancel the
query if needed.
--
Thanks
Hubert Zhang
Attachments:
disk_quota_hooks_v4.patchapplication/octet-stream; name=disk_quota_hooks_v4.patchDownload
From 580ef70f73523d6ef654bf04296acb93c1009531 Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Mon, 15 Jul 2019 11:34:24 +0800
Subject: [PATCH] Add smgr hooks to extend the logic of storage management
One example is that these hooks could be used by diskquota extension
to detect heap table change(create/extend/truncate/unlink).
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 22 ++++++++++++++++++++++
src/include/storage/smgr.h | 19 +++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c397fe..5a1f7a3007 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -96,6 +96,16 @@ static dlist_head unowned_relns;
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -344,6 +354,9 @@ smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
if (isRedo && reln->md_num_open_segs[forknum] > 0)
return;
+ if (smgrcreate_hook)
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+
/*
* We may be using the target table space for the first time in this
* database, so create a per-database subdirectory if needed.
@@ -439,6 +452,9 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -562,6 +578,9 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
}
@@ -645,6 +664,9 @@ smgrnblocks(SMgrRelation reln, ForkNumber forknum)
void
smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
{
+ if (smgrtruncate_hook)
+ (*smgrtruncate_hook)(reln, forknum, nblocks);
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index d286c8c7b1..e473162a8b 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -107,4 +107,23 @@ extern void smgrtruncate(SMgrRelation reln, ForkNumber forknum,
extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
extern void AtEOXact_SMgr(void);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
+
#endif /* SMGR_H */
--
2.11.0 (Apple Git-81)
This patch no longer applies. Can you please rebase?
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Thanks Alvaro!
I rebased this patch with the newest master branch. Attached the new
patch disk_quota_hooks_v5.patch in the attachment.
Regards,
Haozhou
On Thu, Sep 26, 2019 at 3:54 AM Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
This patch no longer applies. Can you please rebase?
--
Álvaro Herrera
https://urldefense.proofpoint.com/v2/url?u=https-3A__www.2ndQuadrant.com_&d=DwIDAw&c=lnl9vOaLMzsy2niBC8-h_K-7QJuNJEsFrzdndhuJ3Sw&r=nGUCIcT5CVp6-pQcplYyagWnpAqoYm8YxWruds9UGI0&m=aNzGoEI15bAE-vAivY34BtG2WdgrVojH-B-kjvuXsYA&s=sT6zyiq4s8meelNuFw-lGD_mdvmUzv9zpVYWbFWusI0&e=
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Regards,
Haozhou
Attachments:
disk_quota_hooks_v5.patchapplication/octet-stream; name=disk_quota_hooks_v5.patchDownload
From 18e2f13ecd9af7594dee78f981873c5a18998b2c Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Mon, 15 Jul 2019 11:34:24 +0800
Subject: [PATCH] Add smgr hooks to extend the logic of storage management
One example is that these hooks could be used by diskquota extension
to detect heap table change(create/extend/truncate/unlink).
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 22 ++++++++++++++++++++++
src/include/storage/smgr.h | 19 +++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index b50c69b438..f1c759ce11 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -97,6 +97,16 @@ static dlist_head unowned_relns;
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -332,6 +342,9 @@ smgrclosenode(RelFileNodeBackend rnode)
void
smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
{
+ if (smgrcreate_hook)
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
}
@@ -411,6 +424,9 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -483,6 +499,9 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
}
@@ -572,6 +591,9 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nb
{
int i;
+ if (smgrtruncate_hook)
+ (*smgrtruncate_hook)(reln, forknum, nblocks);
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 1543d8d870..e94c8b0644 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -106,4 +106,23 @@ extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum,
extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
extern void AtEOXact_SMgr(void);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
+
#endif /* SMGR_H */
--
2.20.1 (Apple Git-117)
On Fri, Sep 27, 2019 at 11:30:08AM +0800, Haozhou Wang wrote:
I rebased this patch with the newest master branch. Attached the new
patch disk_quota_hooks_v5.patch in the attachment.
This again needs a rebase, so I have switched it as waiting on
author.
--
Michael
Hi Michael,
Thank you very much for your email. I rebased the code with the newest
master and attached it in the attachment.
Thank you very much!
Regards,
Haozhou
On Sun, Dec 1, 2019 at 11:20 AM Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Sep 27, 2019 at 11:30:08AM +0800, Haozhou Wang wrote:
I rebased this patch with the newest master branch. Attached the new
patch disk_quota_hooks_v5.patch in the attachment.This again needs a rebase, so I have switched it as waiting on
author.
--
Michael
--
Regards,
Haozhou
Attachments:
disk_quota_hooks_v6.patchapplication/octet-stream; name=disk_quota_hooks_v6.patchDownload
From aa0c97c64b48a45f502233c23867882f75caaef0 Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Mon, 15 Jul 2019 11:34:24 +0800
Subject: [PATCH] Add smgr hooks to extend the logic of storage management
One example is that these hooks could be used by diskquota extension
to detect heap table change(create/extend/truncate/unlink).
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 22 ++++++++++++++++++++++
src/include/storage/smgr.h | 19 +++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index b50c69b438..f1c759ce11 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -97,6 +97,16 @@ static dlist_head unowned_relns;
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -332,6 +342,9 @@ smgrclosenode(RelFileNodeBackend rnode)
void
smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
{
+ if (smgrcreate_hook)
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
}
@@ -411,6 +424,9 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -483,6 +499,9 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
}
@@ -572,6 +591,9 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nb
{
int i;
+ if (smgrtruncate_hook)
+ (*smgrtruncate_hook)(reln, forknum, nblocks);
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 1543d8d870..e94c8b0644 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -106,4 +106,23 @@ extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum,
extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
extern void AtEOXact_SMgr(void);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
+
#endif /* SMGR_H */
--
2.20.1 (Apple Git-117)
On 12/2/19 1:39 AM, Haozhou Wang wrote:
Thank you very much for your email. I rebased the code with the newest
master and attached it in the attachment.
This patch applies but no longer builds on Linux or Windows:
https://travis-ci.org/github/postgresql-cfbot/postgresql/builds/666113036
https://ci.appveyor.com/project/postgresql-cfbot/postgresql/build/1.0.85284
The CF entry has been updated to Waiting on Author.
Regards,
--
-David
david@pgmasters.net
Thanks David!
We rebased this patch with the newest master.
Thank you very much!
Regards,
Haozhou
On Wed, Mar 25, 2020 at 12:00 AM David Steele <david@pgmasters.net> wrote:
On 12/2/19 1:39 AM, Haozhou Wang wrote:
Thank you very much for your email. I rebased the code with the newest
master and attached it in the attachment.This patch applies but no longer builds on Linux or Windows:
The CF entry has been updated to Waiting on Author.
Regards,
--
-David
david@pgmasters.net
--
Regards,
Haozhou
Attachments:
disk_quota_hooks_v7.patchapplication/octet-stream; name=disk_quota_hooks_v7.patchDownload
From 38c49f9d699fe4e1a62aa15172d0716739fe1aaa Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Mon, 15 Jul 2019 11:34:24 +0800
Subject: [PATCH] Add smgr hooks to extend the logic of storage management
One example is that these hooks could be used by diskquota extension
to detect heap table change(create/extend/truncate/unlink).
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 22 ++++++++++++++++++++++
src/include/storage/smgr.h | 19 +++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 360b5bf5bf..13f6538f92 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -97,6 +97,16 @@ static dlist_head unowned_relns;
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
+
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -332,6 +342,9 @@ smgrclosenode(RelFileNodeBackend rnode)
void
smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
{
+ if (smgrcreate_hook)
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
}
@@ -411,6 +424,9 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+
/*
* create an array which contains all relations to be dropped, and close
* each relation's forks at the smgr level while at it
@@ -483,6 +499,9 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
char *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
}
@@ -572,6 +591,9 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nb
{
int i;
+ if (smgrtruncate_hook)
+ (*smgrtruncate_hook)(reln, forknum, nforks, nblocks);
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 243822137c..2560dd76d6 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -106,4 +106,23 @@ extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum,
extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
extern void AtEOXact_SMgr(void);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum,
+ char *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber *forknum, int nforks,
+ BlockNumber nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels,
+ bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
+
#endif /* SMGR_H */
--
2.20.1 (Apple Git-117)
On 27 Mar 2020, at 11:22, Haozhou Wang <hawang@pivotal.io> wrote:
We rebased this patch with the newest master.
This patch now fails to build according to Travis:
smgr.c: In function ‘smgrtruncate’:
smgr.c:578:47: error: passing argument 4 of ‘smgrtruncate_hook’ makes integer from pointer without a cast [-Werror=int-conversion]
(*smgrtruncate_hook)(reln, forknum, nforks, nblocks);
^
smgr.c:578:47: note: expected ‘BlockNumber {aka unsigned int}’ but argument is of type ‘BlockNumber * {aka unsigned int *}’
The warning is also present in the Windows build:
src/backend/storage/smgr/smgr.c(578): warning C4047: 'function' : 'BlockNumber' differs in levels of indirection from 'BlockNumber *' [C:\projects\postgresql\postgres.vcxproj]
src/backend/storage/smgr/smgr.c(578): warning C4024: 'smgrtruncate_hook' : different types for formal and actual parameter 4 [C:\projects\postgresql\postgres.vcxproj]
2 Warning(s)
Marking the patch as Waiting for Author.
cheers ./daniel
On 1 Jul 2020, at 10:36, Daniel Gustafsson <daniel@yesql.se> wrote:
On 27 Mar 2020, at 11:22, Haozhou Wang <hawang@pivotal.io> wrote:
We rebased this patch with the newest master.
This patch now fails to build according to Travis:
smgr.c: In function ‘smgrtruncate’:
smgr.c:578:47: error: passing argument 4 of ‘smgrtruncate_hook’ makes integer from pointer without a cast [-Werror=int-conversion]
(*smgrtruncate_hook)(reln, forknum, nforks, nblocks);
^
smgr.c:578:47: note: expected ‘BlockNumber {aka unsigned int}’ but argument is of type ‘BlockNumber * {aka unsigned int *}’The warning is also present in the Windows build:
src/backend/storage/smgr/smgr.c(578): warning C4047: 'function' : 'BlockNumber' differs in levels of indirection from 'BlockNumber *' [C:\projects\postgresql\postgres.vcxproj]
src/backend/storage/smgr/smgr.c(578): warning C4024: 'smgrtruncate_hook' : different types for formal and actual parameter 4 [C:\projects\postgresql\postgres.vcxproj]
2 Warning(s)Marking the patch as Waiting for Author.
As the thread has stalled and the above compilation issue hasn't been
addressed, I'm marking this entry Returned with Feedback. Feel free to open a
new entry when there is a fixed patch.
cheers ./daniel
On Mon, Feb 26, 2024 at 7:27 PM Daniel Gustafsson <daniel@yesql.se> wrote:
On 1 Jul 2020, at 10:36, Daniel Gustafsson <daniel@yesql.se> wrote:
On 27 Mar 2020, at 11:22, Haozhou Wang <hawang@pivotal.io> wrote:
We rebased this patch with the newest master.
This patch now fails to build according to Travis:
smgr.c: In function ‘smgrtruncate’:
smgr.c:578:47: error: passing argument 4 of ‘smgrtruncate_hook’ makes integer from pointer without a cast [-Werror=int-conversion]
(*smgrtruncate_hook)(reln, forknum, nforks, nblocks);
^
smgr.c:578:47: note: expected ‘BlockNumber {aka unsigned int}’ but argument is of type ‘BlockNumber * {aka unsigned int *}’The warning is also present in the Windows build:
src/backend/storage/smgr/smgr.c(578): warning C4047: 'function' : 'BlockNumber' differs in levels of indirection from 'BlockNumber *' [C:\projects\postgresql\postgres.vcxproj]
src/backend/storage/smgr/smgr.c(578): warning C4024: 'smgrtruncate_hook' : different types for formal and actual parameter 4 [C:\projects\postgresql\postgres.vcxproj]
2 Warning(s)Marking the patch as Waiting for Author.
As the thread has stalled and the above compilation issue hasn't been
addressed, I'm marking this entry Returned with Feedback. Feel free to open a
new entry when there is a fixed patch.
Hi,
Looks that many hackers are happy with the original patch except that
the original patch needs a simple rebase, though it has been 3 years.
Shall we push forward this patch so that it can benefit extensions not
only diskquota?
The attachment is a rebased version of the original patch.
Show quoted text
cheers ./daniel
Attachments:
v8-0001-Add-smgr-hooks-to-extend-the-logic-of-storage-man.patchtext/x-patch; charset=US-ASCII; name=v8-0001-Add-smgr-hooks-to-extend-the-logic-of-storage-man.patchDownload
From 6479e1ccc92c53468dc879d33fa1a93f66ae4521 Mon Sep 17 00:00:00 2001
From: Hubert Zhang <hubertzhang@apache.org>
Date: Mon, 26 Feb 2024 19:24:43 +0800
Subject: [PATCH v8] Add smgr hooks to extend the logic of storage management
One example is that these hooks could be used by diskquota extension
to detect heap table change(create/extend/truncate/unlink).
Co-authored-by: Haozhou Wang <hawang@pivotal.io>
Co-authored-by: Hubert Zhang <hzhang@pivotal.io>
Co-authored-by: Hao Wu <gfphoenix78@gmail.com>
---
src/backend/storage/smgr/smgr.c | 21 +++++++++++++++++++++
src/include/storage/smgr.h | 16 ++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index f7f7fe30b6..7992da84ce 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -142,6 +142,15 @@ static dlist_head unpinned_relns;
static void smgrshutdown(int code, Datum arg);
static void smgrdestroy(SMgrRelation reln);
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+smgrcreate_hook_type smgrcreate_hook = NULL;
+smgrextend_hook_type smgrextend_hook = NULL;
+smgrtruncate_hook_type smgrtruncate_hook = NULL;
+smgrdounlinkall_hook_type smgrdounlinkall_hook = NULL;
/*
* smgrinit(), smgrshutdown() -- Initialize or shut down storage
@@ -413,6 +422,9 @@ smgrexists(SMgrRelation reln, ForkNumber forknum)
void
smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
{
+ if (smgrcreate_hook)
+ (*smgrcreate_hook)(reln, forknum, isRedo);
+
smgrsw[reln->smgr_which].smgr_create(reln, forknum, isRedo);
}
@@ -471,6 +483,9 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
if (nrels == 0)
return;
+ if (smgrdounlinkall_hook)
+ (*smgrdounlinkall_hook)(rels, nrels, isRedo);
+
/*
* Get rid of any remaining buffers for the relations. bufmgr will just
* drop them without bothering to write the contents.
@@ -538,6 +553,9 @@ void
smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
const void *buffer, bool skipFsync)
{
+ if (smgrextend_hook)
+ (*smgrextend_hook)(reln, forknum, blocknum, buffer, skipFsync);
+
smgrsw[reln->smgr_which].smgr_extend(reln, forknum, blocknum,
buffer, skipFsync);
@@ -706,6 +724,9 @@ smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nb
{
int i;
+ if (smgrtruncate_hook)
+ (*smgrtruncate_hook)(reln, forknum, nforks, nblocks);
+
/*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents.
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 2b57addbdb..0ac1ed8f93 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -124,4 +124,20 @@ smgrwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
smgrwritev(reln, forknum, blocknum, &buffer, 1, skipFsync);
}
+/*
+ * Hook for plugins to extend smgr functions.
+ * for example, collect statistics from smgr functions
+ * via recording the active relfilenode information.
+ */
+typedef void (*smgrcreate_hook_type)(SMgrRelation reln, ForkNumber forknum, bool isRedo);
+extern PGDLLIMPORT smgrcreate_hook_type smgrcreate_hook;
+typedef void (*smgrextend_hook_type)(SMgrRelation reln, ForkNumber forknum,
+ BlockNumber blocknum, const void *buffer, bool skipFsync);
+extern PGDLLIMPORT smgrextend_hook_type smgrextend_hook;
+typedef void (*smgrtruncate_hook_type)(SMgrRelation reln, ForkNumber *forknum,
+ int nforks, BlockNumber *nblocks);
+extern PGDLLIMPORT smgrtruncate_hook_type smgrtruncate_hook;
+typedef void (*smgrdounlinkall_hook_type)(SMgrRelation *rels, int nrels, bool isRedo);
+extern PGDLLIMPORT smgrdounlinkall_hook_type smgrdounlinkall_hook;
+
#endif /* SMGR_H */
--
2.43.2
Greetings,
* Xing Guo (higuoxing@gmail.com) wrote:
Looks that many hackers are happy with the original patch except that
the original patch needs a simple rebase, though it has been 3 years.
I'm not completely against the idea of adding these hooks, but I have to
say that it's unfortunate to imagine having to use an extension for
something like quotas as it's really something that would ideally be in
core.
Shall we push forward this patch so that it can benefit extensions not
only diskquota?
Would be great to hear about other use-cases for these hooks, which
would also help us be comfortable that these are the right hooks to
introduce with the correct arguments. What are the other extensions
that you're referring to here..?
Thanks,
Stephen
On Tue, Feb 27, 2024 at 6:38 AM Stephen Frost <sfrost@snowman.net> wrote:
Greetings,
* Xing Guo (higuoxing@gmail.com) wrote:
Looks that many hackers are happy with the original patch except that
the original patch needs a simple rebase, though it has been 3 years.I'm not completely against the idea of adding these hooks, but I have to
say that it's unfortunate to imagine having to use an extension for
something like quotas as it's really something that would ideally be in
core.Shall we push forward this patch so that it can benefit extensions not
only diskquota?Would be great to hear about other use-cases for these hooks, which
would also help us be comfortable that these are the right hooks to
introduce with the correct arguments. What are the other extensions
that you're referring to here..?
Sorry, we don't have another concrete extension that utilizes the
smgrcreate / smgrtruncate / smgrdounlinkall hooks right now. But we
have a transparent data encryption extension that utilizes the
smgrread and smgrwrite hooks to mutate the read buffer and write
buffer in place to perform file level encryption and decryption. I
think smgrwrite / smgrread / smgrcreate / smgrtruncate and
smgrdounlinkall hooks fall into the same catagory and these hooks are
different from the recent proposed storage manager API hooks[1]/messages/by-id/CAEze2WgMySu2suO_TLvFyGY3URa4mAx22WeoEicnK=PCNWEMrA@mail.gmail.com.
Probably it is worth starting a new thread and discussing them.
[1]: /messages/by-id/CAEze2WgMySu2suO_TLvFyGY3URa4mAx22WeoEicnK=PCNWEMrA@mail.gmail.com
Thanks,
Stephen
Best Regards,
Xing