Skip to content

Commit d196862

Browse files
RekGRpthtuhaihe
authored andcommitted
Invalidate diskquota.table_size entries during startup (#406)
Diskquota calculates sizes and stores information in the diskquota.table_size table periodically with a pause in diskquota.naptime, 2 seconds by default. If we restart the cluster during this pause, then diskquota will lose all changes that have occurred since the last save to the diskquota.table_size table. We could create temporary tables, wait when it will be flushed to diskquota.table_size table, restart the cluster, and diskquota would remember the information about the temporary tables. Or we could delete the tables, restart the cluster, and again diskquota will remember information about the deleted tables. This happens because at the start of the cluster, diskquota remembers all the information written to the diskquota.table_size table, but does not check that some tables may have already been deleted. As a solution, we invalidate diskquota.table_size during diskquota worker start in addition to pg_class validation. The remaining problem: the incorrect table size cannot be refreshed until the corresponding table becomes an active table. Solution: call diskquota.init_table_size_table().
1 parent 4ac963b commit d196862

10 files changed

Lines changed: 384 additions & 7 deletions

File tree

gpcontrib/diskquota/src/diskquota.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ extern bool diskquota_hardlimit;
290290
extern int SEGCOUNT;
291291
extern int worker_spi_get_extension_version(int *major, int *minor);
292292
extern void truncateStringInfo(StringInfo str, int nchars);
293-
extern List *get_rel_oid_list(void);
293+
extern List *get_rel_oid_list(bool is_init);
294294
extern int64 calculate_relation_size_all_forks(RelFileNodeBackend *rnode, char relstorage, Oid relam);
295295
extern Relation diskquota_relation_open(Oid relid);
296296
extern bool get_rel_name_namespace(Oid relid, Oid *nsOid, char *relname);

gpcontrib/diskquota/src/diskquota_utility.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,6 @@ static float4 get_per_segment_ratio(Oid spcoid);
113113
static bool to_delete_quota(QuotaType type, int64 quota_limit_mb, float4 segratio);
114114
static void check_role(Oid roleoid, char *rolname, int64 quota_limit_mb);
115115

116-
List *get_rel_oid_list(void);
117-
118116
/* ---- Help Functions to set quota limit. ---- */
119117
/*
120118
* Initialize table diskquota.table_size.
@@ -1296,24 +1294,34 @@ worker_spi_get_extension_version(int *major, int *minor)
12961294
* Get the list of oids of the tables which diskquota
12971295
* needs to care about in the database.
12981296
* Firstly the all the table oids which relkind is 'r'
1299-
* or 'm' and not system table.
1297+
* or 'm' and not system table. On init stage, oids from
1298+
* diskquota.table_size are added to invalidate them.
13001299
* Then, fetch the indexes of those tables.
13011300
*/
13021301

13031302
List *
1304-
get_rel_oid_list(void)
1303+
get_rel_oid_list(bool is_init)
13051304
{
13061305
List *oidlist = NIL;
13071306
int ret;
13081307

1309-
ret = SPI_execute_with_args("select oid from pg_class where oid >= $1 and (relkind='r' or relkind='m')", 1,
1308+
#define SELECT_FROM_PG_CATALOG_PG_CLASS "select oid from pg_catalog.pg_class where oid >= $1 and relkind in ('r', 'm')"
1309+
1310+
ret = SPI_execute_with_args(is_init ? SELECT_FROM_PG_CATALOG_PG_CLASS
1311+
" union distinct"
1312+
" select tableid from diskquota.table_size where segid = -1"
1313+
: SELECT_FROM_PG_CATALOG_PG_CLASS,
1314+
1,
13101315
(Oid[]){
13111316
OIDOID,
13121317
},
13131318
(Datum[]){
13141319
ObjectIdGetDatum(FirstNormalObjectId),
13151320
},
13161321
NULL, false, 0);
1322+
1323+
#undef SELECT_FROM_PG_CATALOG_PG_CLASS
1324+
13171325
if (ret != SPI_OK_SELECT) elog(ERROR, "cannot fetch in pg_class. error code %d", ret);
13181326

13191327
TupleDesc tupdesc = SPI_tuptable->tupdesc;

gpcontrib/diskquota/src/quotamodel.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ static bool get_table_size_entry_flag(TableSizeEntry *entry, TableSizeEntryFlag
222222
static void reset_table_size_entry_flag(TableSizeEntry *entry, TableSizeEntryFlag flag);
223223
static void set_table_size_entry_flag(TableSizeEntry *entry, TableSizeEntryFlag flag);
224224

225+
static void delete_from_table_size_map(char *str);
226+
225227
/*
226228
* put QuotaInfoEntry into quota_info_map and return this entry.
227229
* return NULL: no free SHM for quota_info_map
@@ -918,6 +920,10 @@ calculate_table_disk_usage(bool is_init, HTAB *local_active_table_stat_map)
918920
TableEntryKey active_table_key;
919921
List *oidlist;
920922
ListCell *l;
923+
int delete_entries_num = 0;
924+
StringInfoData delete_statement;
925+
926+
initStringInfo(&delete_statement);
921927

922928
/*
923929
* unset is_exist flag for tsentry in table_size_map this is used to
@@ -934,7 +940,7 @@ calculate_table_disk_usage(bool is_init, HTAB *local_active_table_stat_map)
934940
* calculate the file size for active table and update namespace_size_map
935941
* and role_size_map
936942
*/
937-
oidlist = get_rel_oid_list();
943+
oidlist = get_rel_oid_list(is_init);
938944

939945
oidlist = merge_uncommitted_table_to_oidlist(oidlist);
940946

@@ -968,6 +974,23 @@ calculate_table_disk_usage(bool is_init, HTAB *local_active_table_stat_map)
968974
{
969975
elog(WARNING, "cache lookup failed for relation %u", relOid);
970976
LWLockRelease(diskquota_locks.relation_cache_lock);
977+
978+
if (!is_init) continue;
979+
980+
for (int i = -1; i < SEGCOUNT; i++)
981+
{
982+
appendStringInfo(&delete_statement, "%s(%u,%d)", (delete_entries_num == 0) ? " " : ", ", relOid, i);
983+
984+
delete_entries_num++;
985+
986+
if (delete_entries_num > SQL_MAX_VALUES_NUMBER)
987+
{
988+
delete_from_table_size_map(delete_statement.data);
989+
resetStringInfo(&delete_statement);
990+
delete_entries_num = 0;
991+
}
992+
}
993+
971994
continue;
972995
}
973996
relnamespace = relation_entry->namespaceoid;
@@ -1107,6 +1130,9 @@ calculate_table_disk_usage(bool is_init, HTAB *local_active_table_stat_map)
11071130
}
11081131
}
11091132

1133+
if (delete_entries_num) delete_from_table_size_map(delete_statement.data);
1134+
1135+
pfree(delete_statement.data);
11101136
list_free(oidlist);
11111137

11121138
/*
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
-- Ensure diskquota does not save information about dropped table during restart cluster by invalidates it at startup
2+
3+
!\retcode gpconfig -c diskquota.naptime -v 5 --skipvalidation;
4+
-- start_ignore
5+
-- end_ignore
6+
(exited with code 0)
7+
!\retcode gpstop -u;
8+
-- start_ignore
9+
-- end_ignore
10+
(exited with code 0)
11+
12+
1: CREATE SCHEMA dropped_schema;
13+
CREATE
14+
1: SET search_path TO dropped_schema;
15+
SET
16+
1: SELECT diskquota.set_schema_quota('dropped_schema', '1 MB');
17+
set_schema_quota
18+
------------------
19+
20+
(1 row)
21+
1: SELECT diskquota.wait_for_worker_new_epoch();
22+
wait_for_worker_new_epoch
23+
---------------------------
24+
t
25+
(1 row)
26+
1: CREATE TABLE dropped_table(id int) DISTRIBUTED BY (id);
27+
CREATE
28+
1: INSERT INTO dropped_table SELECT generate_series(1, 10000);
29+
INSERT 10000
30+
-- Wait for the diskquota bgworker refreshing the size of 'dropped_table'.
31+
1: SELECT diskquota.wait_for_worker_new_epoch();
32+
wait_for_worker_new_epoch
33+
---------------------------
34+
t
35+
(1 row)
36+
1: DROP TABLE dropped_table;
37+
DROP
38+
1q: ... <quitting>
39+
40+
-- Restart cluster fastly
41+
!\retcode gpstop -afr;
42+
-- start_ignore
43+
-- end_ignore
44+
(exited with code 0)
45+
46+
-- Indicates that there is no dropped table in pg_catalog.pg_class
47+
1: SELECT oid FROM pg_catalog.pg_class WHERE relname = 'dropped_table';
48+
oid
49+
-----
50+
(0 rows)
51+
-- Indicates that there are no entries in diskquota.table_size that are not present in pg_catalog.pg_class
52+
1: SELECT diskquota.wait_for_worker_new_epoch();
53+
wait_for_worker_new_epoch
54+
---------------------------
55+
t
56+
(1 row)
57+
1: SELECT tableid FROM diskquota.table_size WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class WHERE tableid = oid) AND segid = -1;
58+
tableid
59+
---------
60+
(0 rows)
61+
1: DROP SCHEMA dropped_schema CASCADE;
62+
DROP
63+
1q: ... <quitting>
64+
65+
!\retcode gpconfig -c diskquota.naptime -v 0 --skipvalidation;
66+
-- start_ignore
67+
-- end_ignore
68+
(exited with code 0)
69+
!\retcode gpstop -u;
70+
-- start_ignore
71+
-- end_ignore
72+
(exited with code 0)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
-- Ensure diskquota does not save information about temporary table during restart cluster by invalidates it at startup
2+
3+
!\retcode gpconfig -c diskquota.naptime -v 5 --skipvalidation;
4+
-- start_ignore
5+
-- end_ignore
6+
(exited with code 0)
7+
!\retcode gpstop -u;
8+
-- start_ignore
9+
-- end_ignore
10+
(exited with code 0)
11+
12+
1: CREATE SCHEMA temporary_schema;
13+
CREATE
14+
1: SET search_path TO temporary_schema;
15+
SET
16+
1: SELECT diskquota.set_schema_quota('temporary_schema', '1 MB');
17+
set_schema_quota
18+
------------------
19+
20+
(1 row)
21+
1: SELECT diskquota.wait_for_worker_new_epoch();
22+
wait_for_worker_new_epoch
23+
---------------------------
24+
t
25+
(1 row)
26+
1: CREATE TEMPORARY TABLE temporary_table(id int) DISTRIBUTED BY (id);
27+
CREATE
28+
1: INSERT INTO temporary_table SELECT generate_series(1, 10000);
29+
INSERT 10000
30+
-- Wait for the diskquota bgworker refreshing the size of 'temporary_table'.
31+
1: SELECT diskquota.wait_for_worker_new_epoch();
32+
wait_for_worker_new_epoch
33+
---------------------------
34+
t
35+
(1 row)
36+
1q: ... <quitting>
37+
38+
-- Restart cluster fastly
39+
!\retcode gpstop -afr;
40+
-- start_ignore
41+
-- end_ignore
42+
(exited with code 0)
43+
44+
-- Indicates that there is no temporary table in pg_catalog.pg_class
45+
1: SELECT oid FROM pg_catalog.pg_class WHERE relname = 'temporary_table';
46+
oid
47+
-----
48+
(0 rows)
49+
-- Indicates that there are no entries in diskquota.table_size that are not present in pg_catalog.pg_class
50+
1: SELECT diskquota.wait_for_worker_new_epoch();
51+
wait_for_worker_new_epoch
52+
---------------------------
53+
t
54+
(1 row)
55+
1: SELECT tableid FROM diskquota.table_size WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class WHERE tableid = oid) AND segid = -1;
56+
tableid
57+
---------
58+
(0 rows)
59+
1: DROP SCHEMA temporary_schema CASCADE;
60+
DROP
61+
1q: ... <quitting>
62+
63+
!\retcode gpconfig -c diskquota.naptime -v 0 --skipvalidation;
64+
-- start_ignore
65+
-- end_ignore
66+
(exited with code 0)
67+
!\retcode gpstop -u;
68+
-- start_ignore
69+
-- end_ignore
70+
(exited with code 0)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
-- Ensure diskquota does not save information about dropped table during restart cluster by invalidates it at startup
2+
3+
!\retcode gpconfig -c diskquota.naptime -v 5 --skipvalidation;
4+
-- start_ignore
5+
-- end_ignore
6+
(exited with code 0)
7+
!\retcode gpstop -u;
8+
-- start_ignore
9+
-- end_ignore
10+
(exited with code 0)
11+
12+
1: CREATE SCHEMA dropped_schema;
13+
CREATE SCHEMA
14+
1: SET search_path TO dropped_schema;
15+
SET
16+
1: SELECT diskquota.set_schema_quota('dropped_schema', '1 MB');
17+
set_schema_quota
18+
------------------
19+
20+
(1 row)
21+
1: SELECT diskquota.wait_for_worker_new_epoch();
22+
wait_for_worker_new_epoch
23+
---------------------------
24+
t
25+
(1 row)
26+
1: CREATE TABLE dropped_table(id int) DISTRIBUTED BY (id);
27+
CREATE TABLE
28+
1: INSERT INTO dropped_table SELECT generate_series(1, 10000);
29+
INSERT 0 10000
30+
-- Wait for the diskquota bgworker refreshing the size of 'dropped_table'.
31+
1: SELECT diskquota.wait_for_worker_new_epoch();
32+
wait_for_worker_new_epoch
33+
---------------------------
34+
t
35+
(1 row)
36+
1: DROP TABLE dropped_table;
37+
DROP TABLE
38+
1q: ... <quitting>
39+
40+
-- Restart cluster fastly
41+
!\retcode gpstop -afr;
42+
-- start_ignore
43+
-- end_ignore
44+
(exited with code 0)
45+
46+
-- Indicates that there is no dropped table in pg_catalog.pg_class
47+
1: SELECT oid FROM pg_catalog.pg_class WHERE relname = 'dropped_table';
48+
oid
49+
-----
50+
(0 rows)
51+
-- Indicates that there are no entries in diskquota.table_size that are not present in pg_catalog.pg_class
52+
1: SELECT diskquota.wait_for_worker_new_epoch();
53+
wait_for_worker_new_epoch
54+
---------------------------
55+
t
56+
(1 row)
57+
1: SELECT tableid FROM diskquota.table_size WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_class WHERE tableid = oid) AND segid = -1;
58+
tableid
59+
---------
60+
(0 rows)
61+
1: DROP SCHEMA dropped_schema CASCADE;
62+
DROP SCHEMA
63+
1q: ... <quitting>
64+
65+
!\retcode gpconfig -c diskquota.naptime -v 0 --skipvalidation;
66+
-- start_ignore
67+
-- end_ignore
68+
(exited with code 0)
69+
!\retcode gpstop -u;
70+
-- start_ignore
71+
-- end_ignore
72+
(exited with code 0)

0 commit comments

Comments
 (0)