Skip to content

Commit 240ba05

Browse files
queenofpigeonsAlexandra Pervushina
and
Alexandra Pervushina
authored
Fix aqo.dsm_max_size segfault (#178)
Fix aqo.dsm_max_size segfault Add test for dsm_max_size --------- Co-authored-by: Alexandra Pervushina <[email protected]>
1 parent 470e709 commit 240ba05

File tree

6 files changed

+152
-15
lines changed

6 files changed

+152
-15
lines changed

aqo.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ _PG_init(void)
270270
&dsm_size_max,
271271
100,
272272
0, INT_MAX,
273-
PGC_SUSET,
274-
0,
273+
PGC_POSTMASTER,
274+
GUC_UNIT_MB,
275275
NULL,
276276
NULL,
277277
NULL
@@ -383,5 +383,5 @@ PG_FUNCTION_INFO_V1(invalidate_deactivated_queries_cache);
383383
Datum
384384
invalidate_deactivated_queries_cache(PG_FUNCTION_ARGS)
385385
{
386-
PG_RETURN_POINTER(NULL);
386+
PG_RETURN_POINTER(NULL);
387387
}

aqo_shared.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ aqo_init_shmem(void)
9797
/* Doesn't use DSA, so can be loaded in postmaster */
9898
aqo_stat_load();
9999
aqo_queries_load();
100+
101+
check_dsa_file_size();
100102
}
101103
}
102104

preprocessing.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,23 @@ aqo_planner(Query *parse, const char *query_string, int cursorOptions,
283283
query_context.learn_aqo, query_context.use_aqo,
284284
query_context.auto_tuning, &aqo_queries_nulls))
285285
{
286+
bool dsa_valid = true;
286287
/*
287288
* Add query text into the ML-knowledge base. Just for further
288289
* analysis. In the case of cached plans we may have NULL query text.
289290
*/
290-
if (!aqo_qtext_store(query_context.query_hash, query_string))
291+
if (!aqo_qtext_store(query_context.query_hash, query_string, &dsa_valid))
291292
{
292-
Assert(0); /* panic only on debug installation */
293-
elog(ERROR, "[AQO] Impossible situation was detected. Maybe not enough of shared memory?");
293+
if (!dsa_valid)
294+
{
295+
disable_aqo_for_query();
296+
elog(WARNING, "[AQO] Not enough DSA. AQO was disabled for this query");
297+
}
298+
else
299+
{
300+
Assert(0); /* panic only on debug installation */
301+
elog(ERROR, "[AQO] Impossible situation was detected. Maybe not enough of shared memory?");
302+
}
294303
}
295304
}
296305
else

storage.c

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ _form_qtext_record_cb(void *ctx, size_t *size)
507507
{
508508
HASH_SEQ_STATUS *hash_seq = (HASH_SEQ_STATUS *) ctx;
509509
QueryTextEntry *entry;
510-
void *data;
510+
void *data;
511511
char *query_string;
512512
char *ptr;
513513

@@ -784,7 +784,7 @@ _deform_qtexts_record_cb(void *data, size_t size)
784784
HASH_ENTER, &found);
785785
Assert(!found);
786786

787-
entry->qtext_dp = dsa_allocate(qtext_dsa, len);
787+
entry->qtext_dp = dsa_allocate_extended(qtext_dsa, len, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO);
788788
if (!_check_dsa_validity(entry->qtext_dp))
789789
{
790790
/*
@@ -829,7 +829,7 @@ aqo_qtexts_load(void)
829829

830830
if (!found)
831831
{
832-
if (!aqo_qtext_store(0, "COMMON feature space (do not delete!)"))
832+
if (!aqo_qtext_store(0, "COMMON feature space (do not delete!)", NULL))
833833
elog(PANIC, "[AQO] DSA Initialization was unsuccessful");
834834
}
835835
}
@@ -944,6 +944,49 @@ aqo_queries_load(void)
944944
}
945945
}
946946

947+
static long
948+
aqo_get_file_size(const char *filename)
949+
{
950+
FILE *file;
951+
long size = 0;
952+
953+
file = AllocateFile(filename, PG_BINARY_R);
954+
if (file == NULL)
955+
{
956+
if (errno != ENOENT)
957+
goto read_error;
958+
return size;
959+
}
960+
961+
fseek(file, 0L, SEEK_END);
962+
size = ftell(file);
963+
964+
FreeFile(file);
965+
return size;
966+
967+
read_error:
968+
ereport(LOG,
969+
(errcode_for_file_access(),
970+
errmsg("could not read file \"%s\": %m", filename)));
971+
if (file)
972+
FreeFile(file);
973+
unlink(filename);
974+
return -1;
975+
}
976+
977+
void
978+
check_dsa_file_size(void)
979+
{
980+
long qtext_size = aqo_get_file_size(PGAQO_TEXT_FILE);
981+
long data_size = aqo_get_file_size(PGAQO_DATA_FILE);
982+
983+
if (qtext_size == -1 || data_size == -1 ||
984+
qtext_size + data_size >= dsm_size_max * 1024 * 1024)
985+
{
986+
elog(ERROR, "aqo.dsm_size_max is too small");
987+
}
988+
}
989+
947990
static void
948991
data_load(const char *filename, deform_record_t callback, void *ctx)
949992
{
@@ -1090,13 +1133,16 @@ dsa_init()
10901133
* XXX: Maybe merge with aqo_queries ?
10911134
*/
10921135
bool
1093-
aqo_qtext_store(uint64 queryid, const char *query_string)
1136+
aqo_qtext_store(uint64 queryid, const char *query_string, bool *dsa_valid)
10941137
{
10951138
QueryTextEntry *entry;
10961139
bool found;
10971140
bool tblOverflow;
10981141
HASHACTION action;
10991142

1143+
if (dsa_valid)
1144+
*dsa_valid = true;
1145+
11001146
Assert(!LWLockHeldByMe(&aqo_state->qtexts_lock));
11011147

11021148
if (query_string == NULL || querytext_max_size == 0)
@@ -1135,7 +1181,7 @@ aqo_qtext_store(uint64 queryid, const char *query_string)
11351181

11361182
entry->queryid = queryid;
11371183
size = size > querytext_max_size ? querytext_max_size : size;
1138-
entry->qtext_dp = dsa_allocate0(qtext_dsa, size);
1184+
entry->qtext_dp = dsa_allocate_extended(qtext_dsa, size, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO);
11391185

11401186
if (!_check_dsa_validity(entry->qtext_dp))
11411187
{
@@ -1144,7 +1190,10 @@ aqo_qtext_store(uint64 queryid, const char *query_string)
11441190
* that caller recognize it and don't try to call us more.
11451191
*/
11461192
(void) hash_search(qtexts_htab, &queryid, HASH_REMOVE, NULL);
1193+
_aqo_queries_remove(queryid);
11471194
LWLockRelease(&aqo_state->qtexts_lock);
1195+
if (dsa_valid)
1196+
*dsa_valid = false;
11481197
return false;
11491198
}
11501199

@@ -1423,7 +1472,7 @@ aqo_data_store(uint64 fs, int fss, AqoDataArgs *data, List *reloids)
14231472
entry->nrels = nrels;
14241473

14251474
size = _compute_data_dsa(entry);
1426-
entry->data_dp = dsa_allocate0(data_dsa, size);
1475+
entry->data_dp = dsa_allocate_extended(data_dsa, size, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO);
14271476

14281477
if (!_check_dsa_validity(entry->data_dp))
14291478
{
@@ -1455,7 +1504,7 @@ aqo_data_store(uint64 fs, int fss, AqoDataArgs *data, List *reloids)
14551504

14561505
/* Need to re-allocate DSA chunk */
14571506
dsa_free(data_dsa, entry->data_dp);
1458-
entry->data_dp = dsa_allocate0(data_dsa, size);
1507+
entry->data_dp = dsa_allocate_extended(data_dsa, size, DSA_ALLOC_NO_OOM | DSA_ALLOC_ZERO);
14591508

14601509
if (!_check_dsa_validity(entry->data_dp))
14611510
{
@@ -2713,7 +2762,7 @@ aqo_query_texts_update(PG_FUNCTION_ARGS)
27132762

27142763
str_buff = (char*) palloc(str_len);
27152764
text_to_cstring_buffer(str, str_buff, str_len);
2716-
res = aqo_qtext_store(queryid, str_buff);
2765+
res = aqo_qtext_store(queryid, str_buff, NULL);
27172766
pfree(str_buff);
27182767

27192768
PG_RETURN_BOOL(res);

storage.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ extern StatEntry *aqo_stat_store(uint64 queryid, bool use_aqo,
138138
extern void aqo_stat_flush(void);
139139
extern void aqo_stat_load(void);
140140

141-
extern bool aqo_qtext_store(uint64 queryid, const char *query_string);
141+
extern bool aqo_qtext_store(uint64 queryid, const char *query_string, bool *dsa_valid);
142142
extern void aqo_qtexts_flush(void);
143143
extern void aqo_qtexts_load(void);
144144

@@ -156,6 +156,7 @@ extern bool aqo_queries_store(uint64 queryid, uint64 fs, bool learn_aqo,
156156
extern void aqo_queries_flush(void);
157157
extern void aqo_queries_load(void);
158158

159+
extern void check_dsa_file_size(void);
159160
/*
160161
* Machinery for deactivated queries cache.
161162
* TODO: Should live in a custom memory context

t/004_dsm_size_max.pl

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use strict;
2+
use warnings;
3+
4+
use Config;
5+
use PostgreSQL::Test::Cluster;
6+
use PostgreSQL::Test::Utils;
7+
8+
use Test::More tests => 5;
9+
10+
my $node = PostgreSQL::Test::Cluster->new('aqotest');
11+
$node->init;
12+
$node->append_conf('postgresql.conf', qq{
13+
shared_preload_libraries = 'aqo'
14+
aqo.mode = 'learn'
15+
log_statement = 'ddl'
16+
aqo.join_threshold = 0
17+
aqo.dsm_size_max = 4
18+
aqo.fs_max_items = 30000
19+
aqo.querytext_max_size = 1000000
20+
});
21+
22+
# Disable connection default settings, forced by PGOPTIONS in AQO Makefile
23+
$ENV{PGOPTIONS}="";
24+
25+
# General purpose variables.
26+
my $long_string = 'a' x 1000000;
27+
28+
$node->start();
29+
$node->psql('postgres', 'CREATE EXTENSION aqo;');
30+
31+
for my $i (1 .. 3) {
32+
$node->psql('postgres', "select aqo_query_texts_update(" . $i . ", \'" . $long_string . "\');");
33+
}
34+
$node->stop();
35+
36+
$node->append_conf('postgresql.conf', 'aqo.dsm_size_max = 1');
37+
is($node->start(fail_ok => 1),
38+
0, "node fails to start");
39+
40+
$node->append_conf('postgresql.conf', 'aqo.dsm_size_max = 4');
41+
is($node->start(),
42+
1, "node starts");
43+
$node->psql('postgres', 'select * from aqo_reset();');
44+
45+
$long_string = '1, ' x 10000;
46+
for my $i (1 .. 30) {
47+
$node->psql('postgres', "select aqo_data_update(" . $i . ", 1, 1, '{{1}}', '{1}', '{1}', '{" . $long_string . " 1}');");
48+
}
49+
$node->stop();
50+
51+
$node->append_conf('postgresql.conf', 'aqo.dsm_size_max = 1');
52+
is($node->start(fail_ok => 1),
53+
0, "node fails to start");
54+
55+
$node->append_conf('postgresql.conf', 'aqo.dsm_size_max = 4');
56+
is($node->start(),
57+
1, "node starts");
58+
$node->psql('postgres', 'select * from aqo_reset();');
59+
$node->stop();
60+
61+
my $regex;
62+
$long_string = 'a' x 100000;
63+
$regex = qr/.*WARNING: \[AQO\] Not enough DSA\. AQO was disabled for this query/;
64+
$node->append_conf('postgresql.conf', 'aqo.dsm_size_max = 1');
65+
$node->start();
66+
my ($stdout, $stderr);
67+
for my $i (1 .. 20) {
68+
$node->psql('postgres', "create table a as select s, md5(random()::text) from generate_Series(1,100) s;");
69+
$node->psql('postgres',
70+
"SELECT a.s FROM a CROSS JOIN ( SELECT '" . $long_string . "' as long_string) AS extra_rows;",
71+
stdout => \$stdout, stderr => \$stderr);
72+
$node->psql('postgres', "drop table a");
73+
}
74+
like($stderr, $regex, 'warning for exceeding the dsa limit');
75+
$node->stop;
76+
done_testing();

0 commit comments

Comments
 (0)