Skip to content

Commit 6b4f856

Browse files
Alena Rybakinadanolivo
Alena Rybakina
authored andcommitted
Add smart statement timeout for learning aqo in special quesries within through manual retraining.
AQO evaluates whether enough to execute the query through comparison integral error value with its fixed value (0.1), also if integral error didn't change compared to previous iterations, smart statemet timeout value will be increased. Besides, smart statemet timeout value won't be increased, if there is reached limit value, namely statement timeout. The initial smart_statement_timeout value is aqo statement timeout value or 0. Smart statement timeout value and number of its using are saved in aqo_queries.
1 parent dab95f1 commit 6b4f856

13 files changed

+299
-13
lines changed

‎aqo--1.5--1.6.sql

+21
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
44
\echo Use "ALTER EXTENSION aqo UPDATE TO '1.6'" to load this file. \quit
55

6+
DROP VIEW aqo_queries;
7+
68
DROP FUNCTION aqo_enable_query;
79
DROP FUNCTION aqo_disable_query;
810
DROP FUNCTION aqo_cleanup;
11+
DROP FUNCTION aqo_queries;
912

1013
CREATE FUNCTION aqo_enable_class(queryid bigint)
1114
RETURNS void
@@ -77,3 +80,21 @@ CREATE FUNCTION aqo_data_update(
7780
RETURNS bool
7881
AS 'MODULE_PATHNAME', 'aqo_data_update'
7982
LANGUAGE C VOLATILE;
83+
84+
/*
85+
* VIEWs to discover AQO data.
86+
*/
87+
CREATE FUNCTION aqo_queries (
88+
OUT queryid bigint,
89+
OUT fs bigint,
90+
OUT learn_aqo boolean,
91+
OUT use_aqo boolean,
92+
OUT auto_tuning boolean,
93+
OUT smart_timeout bigint,
94+
OUT count_increase_timeout bigint
95+
)
96+
RETURNS SETOF record
97+
AS 'MODULE_PATHNAME', 'aqo_queries'
98+
LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
99+
100+
CREATE VIEW aqo_queries AS SELECT * FROM aqo_queries();

‎aqo.c

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void _PG_init(void);
3535
int aqo_mode = AQO_MODE_CONTROLLED;
3636
bool force_collect_stat;
3737
bool aqo_predict_with_few_neigrs;
38+
int aqo_statement_timeout;
3839

3940
/*
4041
* Show special info in EXPLAIN mode.
@@ -48,6 +49,7 @@ bool aqo_predict_with_few_neigrs;
4849
*/
4950
bool aqo_show_hash;
5051
bool aqo_show_details;
52+
bool change_flex_timeout;
5153

5254
/* GUC variables */
5355
static const struct config_enum_entry format_options[] = {
@@ -293,6 +295,17 @@ _PG_init(void)
293295
NULL,
294296
NULL
295297
);
298+
DefineCustomIntVariable("aqo.statement_timeout",
299+
"Time limit on learning.",
300+
NULL,
301+
&aqo_statement_timeout,
302+
0,
303+
0, INT_MAX,
304+
PGC_USERSET,
305+
0,
306+
NULL,
307+
NULL,
308+
NULL);
296309

297310
DefineCustomIntVariable("aqo.min_neigrs_for_predicting",
298311
"Set how many neigrs the cardinality prediction will be calculated",

‎aqo.h

+11
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,15 @@ typedef struct QueryContextData
199199

200200
instr_time start_execution_time;
201201
double planning_time;
202+
int64 smart_timeout;
203+
int64 count_increase_timeout;
202204
} QueryContextData;
203205

206+
/*
207+
* Indicator for using smart statement timeout for query
208+
*/
209+
extern bool change_flex_timeout;
210+
204211
struct StatEntry;
205212

206213
extern double predicted_ppi_rows;
@@ -250,6 +257,7 @@ extern ExplainOnePlan_hook_type prev_ExplainOnePlan_hook;
250257
extern ExplainOneNode_hook_type prev_ExplainOneNode_hook;
251258

252259
extern void ppi_hook(ParamPathInfo *ppi);
260+
extern int aqo_statement_timeout;
253261

254262
/* Hash functions */
255263
void get_eclasses(List *clauselist, int *nargs, int **args_hash,
@@ -298,5 +306,8 @@ extern void selectivity_cache_clear(void);
298306

299307
extern bool IsQueryDisabled(void);
300308

309+
extern bool update_query_timeout(uint64 queryid, int64 smart_timeout);
310+
extern double get_mean(double *elems, int nelems);
311+
301312
extern List *cur_classes;
302313
#endif

‎auto_tuning.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,15 @@
2626
*/
2727
double auto_tuning_convergence_error = 0.01;
2828

29-
static double get_mean(double *elems, int nelems);
3029
static double get_estimation(double *elems, int nelems);
3130
static bool is_stable(double *elems, int nelems);
3231
static bool converged_cq(double *elems, int nelems);
3332
static bool is_in_infinite_loop_cq(double *elems, int nelems);
3433

35-
3634
/*
3735
* Returns mean value of the array of doubles.
3836
*/
39-
static double
37+
double
4038
get_mean(double *elems, int nelems)
4139
{
4240
double sum = 0;

‎expected/smart_statement_timeout.out

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
DROP TABLE IF EXISTS a,b CASCADE;
2+
NOTICE: table "a" does not exist, skipping
3+
NOTICE: table "b" does not exist, skipping
4+
CREATE TABLE a (x1 int, x2 int, x3 int);
5+
INSERT INTO a (x1, x2, x3) SELECT mod(ival,4), mod(ival,10), mod(ival,10) FROM generate_series(1,100) As ival;
6+
CREATE TABLE b (y1 int, y2 int, y3 int);
7+
INSERT INTO b (y1, y2, y3) SELECT mod(ival + 1,4), mod(ival + 1,10), mod(ival + 1,10) FROM generate_series(1,100) As ival;
8+
CREATE EXTENSION IF NOT EXISTS aqo;
9+
SET aqo.join_threshold = 0;
10+
SET aqo.mode = 'learn';
11+
SET aqo.show_details = 'off';
12+
SET aqo.learn_statement_timeout = 'on';
13+
SET statement_timeout = 1500; -- [1.5s]
14+
SET aqo.statement_timeout = 500; -- [0.5s]
15+
SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;
16+
NOTICE: [AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data. Timeout is 0
17+
NOTICE: [AQO] Time limit for execution of the statement was increased. Current timeout is 1
18+
count | count
19+
-------+-------
20+
62500 | 62500
21+
(1 row)
22+
23+
select smart_timeout, count_increase_timeout from aqo_queries, aqo_query_texts
24+
where query_text = 'SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;'
25+
and aqo_query_texts.queryid = aqo_queries.queryid limit 1;
26+
smart_timeout | count_increase_timeout
27+
---------------+------------------------
28+
1 | 1
29+
(1 row)
30+
31+
SET aqo.learn_statement_timeout = 'off';
32+
SET aqo.statement_timeout = 1000; -- [1s]
33+
INSERT INTO a (x1, x2, x3) SELECT mod(ival,20), mod(ival,10), mod(ival,10) FROM generate_series(1,1000) As ival;
34+
SET aqo.learn_statement_timeout = 'on';
35+
SET aqo.statement_timeout = 500; -- [0.5s]
36+
SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;
37+
NOTICE: [AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data. Timeout is 1
38+
NOTICE: [AQO] Time limit for execution of the statement was increased. Current timeout is 6
39+
count | count
40+
--------+--------
41+
563300 | 562500
42+
(1 row)
43+
44+
select smart_timeout, count_increase_timeout from aqo_queries, aqo_query_texts
45+
where query_text = 'SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;'
46+
and aqo_query_texts.queryid = aqo_queries.queryid limit 1;
47+
smart_timeout | count_increase_timeout
48+
---------------+------------------------
49+
6 | 2
50+
(1 row)
51+
52+
SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;
53+
NOTICE: [AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data. Timeout is 6
54+
NOTICE: [AQO] Time limit for execution of the statement was increased. Current timeout is 63
55+
count | count
56+
--------+--------
57+
563300 | 562500
58+
(1 row)
59+
60+
select smart_timeout, count_increase_timeout from aqo_queries, aqo_query_texts
61+
where query_text = 'SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;'
62+
and aqo_query_texts.queryid = aqo_queries.queryid limit 1;
63+
smart_timeout | count_increase_timeout
64+
---------------+------------------------
65+
63 | 3
66+
(1 row)
67+
68+
SET statement_timeout = 100; -- [0.1s]
69+
SET aqo.statement_timeout = 150;
70+
SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;
71+
NOTICE: [AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data. Timeout is 63
72+
NOTICE: [AQO] Time limit for execution of the statement was increased. Current timeout is 1728
73+
count | count
74+
--------+--------
75+
563300 | 562500
76+
(1 row)
77+
78+
select smart_timeout, count_increase_timeout from aqo_queries, aqo_query_texts
79+
where query_text = 'SELECT count(a.x1),count(B.y1) FROM A a LEFT JOIN B ON a.x1 = B.y1 LEFT JOIN A a1 ON a1.x1 = B.y1;'
80+
and aqo_query_texts.queryid = aqo_queries.queryid limit 1;
81+
smart_timeout | count_increase_timeout
82+
---------------+------------------------
83+
1728 | 4
84+
(1 row)
85+
86+
SELECT 1 FROM aqo_reset();
87+
?column?
88+
----------
89+
1
90+
(1 row)
91+
92+
DROP TABLE a;
93+
DROP TABLE b;
94+
DROP EXTENSION aqo;

‎expected/statement_timeout.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ ERROR: canceling statement due to statement timeout
9191
SELECT check_estimated_rows('SELECT *, pg_sleep(1) FROM t;');
9292
check_estimated_rows
9393
----------------------
94-
2
94+
4
9595
(1 row)
9696

9797
SET statement_timeout = 800;

‎expected/update_functions.out

+4-4
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ ORDER BY res;
211211
(TABLE aqo_queries_dump EXCEPT TABLE aqo_queries)
212212
UNION ALL
213213
(TABLE aqo_queries EXCEPT TABLE aqo_queries_dump);
214-
queryid | fs | learn_aqo | use_aqo | auto_tuning
215-
---------+----+-----------+---------+-------------
214+
queryid | fs | learn_aqo | use_aqo | auto_tuning | smart_timeout | count_increase_timeout
215+
---------+----+-----------+---------+-------------+---------------+------------------------
216216
(0 rows)
217217

218218
-- Update aqo_queries with dump data.
@@ -234,8 +234,8 @@ ORDER BY res;
234234
(TABLE aqo_queries_dump EXCEPT TABLE aqo_queries)
235235
UNION ALL
236236
(TABLE aqo_queries EXCEPT TABLE aqo_queries_dump);
237-
queryid | fs | learn_aqo | use_aqo | auto_tuning
238-
---------+----+-----------+---------+-------------
237+
queryid | fs | learn_aqo | use_aqo | auto_tuning | smart_timeout | count_increase_timeout
238+
---------+----+-----------+---------+-------------+---------------+------------------------
239239
(0 rows)
240240

241241
--

‎postprocessing.c

+47-4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ typedef struct
4444

4545
static double cardinality_sum_errors;
4646
static int cardinality_num_objects;
47+
static int64 max_timeout_value;
48+
static int64 growth_rate = 3;
4749

4850
/*
4951
* Store an AQO-related query data into the Query Environment structure.
@@ -625,15 +627,46 @@ aqo_timeout_handler(void)
625627
ctx.learn = query_context.learn_aqo;
626628
ctx.isTimedOut = true;
627629

628-
elog(NOTICE, "[AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data.");
630+
if (aqo_statement_timeout == 0)
631+
elog(NOTICE, "[AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data.");
632+
else
633+
elog(NOTICE, "[AQO] Time limit for execution of the statement was expired. AQO tried to learn on partial data. Timeout is %ld", max_timeout_value);
634+
629635
learnOnPlanState(timeoutCtl.queryDesc->planstate, (void *) &ctx);
630636
MemoryContextSwitchTo(oldctx);
631637
}
632638

639+
/*
640+
* Function for updating smart statement timeout
641+
*/
642+
static int64
643+
increase_smart_timeout()
644+
{
645+
int64 smart_timeout_fin_time = (query_context.smart_timeout + 1) * pow(growth_rate, query_context.count_increase_timeout);
646+
647+
if (query_context.smart_timeout == max_timeout_value && !update_query_timeout(query_context.query_hash, smart_timeout_fin_time))
648+
elog(NOTICE, "[AQO] Timeout is not updated!");
649+
650+
return smart_timeout_fin_time;
651+
}
652+
633653
static bool
634654
set_timeout_if_need(QueryDesc *queryDesc)
635655
{
636-
TimestampTz fin_time;
656+
int64 fintime = (int64) get_timeout_finish_time(STATEMENT_TIMEOUT)-1;
657+
658+
if (aqo_learn_statement_timeout && aqo_statement_timeout > 0)
659+
{
660+
max_timeout_value = Min(query_context.smart_timeout, (int64) aqo_statement_timeout);
661+
if (max_timeout_value > fintime)
662+
{
663+
max_timeout_value = fintime;
664+
}
665+
}
666+
else
667+
{
668+
max_timeout_value = fintime;
669+
}
637670

638671
if (IsParallelWorker())
639672
/*
@@ -663,8 +696,7 @@ set_timeout_if_need(QueryDesc *queryDesc)
663696
else
664697
Assert(!get_timeout_active(timeoutCtl.id));
665698

666-
fin_time = get_timeout_finish_time(STATEMENT_TIMEOUT);
667-
enable_timeout_at(timeoutCtl.id, fin_time - 1);
699+
enable_timeout_at(timeoutCtl.id, (TimestampTz) max_timeout_value);
668700

669701
/* Save pointer to queryDesc to use at learning after a timeout interruption. */
670702
timeoutCtl.queryDesc = queryDesc;
@@ -720,6 +752,7 @@ aqo_ExecutorEnd(QueryDesc *queryDesc)
720752
instr_time endtime;
721753
EphemeralNamedRelation enr = get_ENR(queryDesc->queryEnv, PlanStateInfo);
722754
MemoryContext oldctx = MemoryContextSwitchTo(AQOLearnMemCtx);
755+
double error = .0;
723756

724757
cardinality_sum_errors = 0.;
725758
cardinality_num_objects = 0;
@@ -788,6 +821,16 @@ aqo_ExecutorEnd(QueryDesc *queryDesc)
788821
/* Store all learn data into the AQO service relations. */
789822
if (!query_context.adding_query && query_context.auto_tuning)
790823
automatical_query_tuning(query_context.query_hash, stat);
824+
825+
error = stat->est_error_aqo[stat->cur_stat_slot_aqo-1] - cardinality_sum_errors/(1 + cardinality_num_objects);
826+
827+
if ( aqo_learn_statement_timeout && aqo_statement_timeout > 0 && error >= 0.1)
828+
{
829+
int64 fintime = increase_smart_timeout();
830+
elog(NOTICE, "[AQO] Time limit for execution of the statement was increased. Current timeout is %ld", fintime);
831+
}
832+
833+
pfree(stat);
791834
}
792835
}
793836

‎preprocessing.c

+2
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ aqo_planner(Query *parse,
249249
elog(ERROR, "unrecognized mode in AQO: %d", aqo_mode);
250250
break;
251251
}
252+
query_context.count_increase_timeout = 0;
253+
query_context.smart_timeout = 0;
252254
}
253255
else /* Query class exists in a ML knowledge base. */
254256
{

‎regress_schedule

+2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ test: plancache
1515
test: update_functions
1616
# Performance-dependent test. Can be ignored if executes in containers or on slow machines
1717
ignore: statement_timeout
18+
ignore: smart_statement_timeout
1819
test: statement_timeout
1920
test: temp_tables
2021
test: top_queries
2122
test: relocatable
2223
test: look_a_like
2324
test: feature_subspace
25+
test: smart_statement_timeout

0 commit comments

Comments
 (0)