mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
PQ tidy up message queue processing, add in missing tests, fix...
in pwt_worker_base::init_worker_thd()
This commit is contained in:
@@ -75,6 +75,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
||||
../sql/sql_parallel_workers.cc
|
||||
../sql/sql_parallel_execution.cc
|
||||
../sql/sql_parallel_costs.cc
|
||||
../sql/sql_parallel_transport.cc
|
||||
../sql/sql_parallel_thread.cc
|
||||
../sql/opt_rewrite_date_cmp.cc
|
||||
../sql/opt_rewrite_remove_casefold.cc
|
||||
../sql/opt_sargable_left.cc
|
||||
|
||||
58
mysql-test/main/parallel_query_trace.result
Normal file
58
mysql-test/main/parallel_query_trace.result
Normal file
@@ -0,0 +1,58 @@
|
||||
SET @save_trace= @@optimizer_trace;
|
||||
SET optimizer_trace='enabled=on';
|
||||
SET SESSION parallel_worker_threads=4;
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq*10 FROM seq_1_to_2000;
|
||||
#
|
||||
# A plan that keeps its parallel scan reports both keys and abandons
|
||||
# nothing (expect 1 1 0)
|
||||
#
|
||||
EXPLAIN SELECT a, b FROM t1 WHERE b > 19980;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ALL_parallel NULL NULL NULL NULL 2000 Using where
|
||||
SELECT a, b FROM t1 WHERE b > 19980;
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
candidate chosen abandoned
|
||||
1 1 0
|
||||
# and it is a table that really is scanned in the workers (expect 1)
|
||||
FLUSH STATUS;
|
||||
SELECT a, b FROM t1 WHERE b > 19980;
|
||||
ran_in_the_workers
|
||||
1
|
||||
#
|
||||
# The gate examines the driving table, so a query it declines is not a
|
||||
# candidate in the first place and names no table at all (expect 0 0 0).
|
||||
# ORDER BY is declined by the query-shape half of the gate, and the
|
||||
# driving table is still a plain full scan
|
||||
#
|
||||
EXPLAIN SELECT a, b FROM t1 WHERE b > 19980 ORDER BY b;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ALL NULL NULL NULL NULL 2000 Using where; Using filesort
|
||||
SELECT a, b FROM t1 WHERE b > 19980 ORDER BY b;
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
candidate chosen abandoned
|
||||
0 0 0
|
||||
# and a covering index read by the driving-table half: the rows arrive in
|
||||
# key order from an index the engine is not asked to divide (expect 0 0 0)
|
||||
#
|
||||
CREATE TABLE t2 (a INT, b INT, KEY(a)) ENGINE=InnoDB;
|
||||
INSERT INTO t2 SELECT seq, seq*10 FROM seq_1_to_2000;
|
||||
EXPLAIN SELECT a FROM t2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 index NULL a 5 NULL 2000 Using index
|
||||
SELECT a FROM t2;
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
candidate chosen abandoned
|
||||
0 0 0
|
||||
DROP TABLE t1, t2;
|
||||
SET SESSION parallel_worker_threads=default;
|
||||
SET optimizer_trace= @save_trace;
|
||||
80
mysql-test/main/parallel_query_trace.test
Normal file
80
mysql-test/main/parallel_query_trace.test
Normal file
@@ -0,0 +1,80 @@
|
||||
#
|
||||
# MDEV-39492 Parallel Query: what the optimizer trace says became of the
|
||||
# parallel scan.
|
||||
#
|
||||
# The gate in make_join_readinfo() records the table it picks as
|
||||
# "parallel_scan_candidate", and JOIN::optimize_stage2() reports what became of
|
||||
# it once the plan has stopped changing: "chosen_for_parallel_scan" if the
|
||||
# access path can still be handed out in chunks, "parallel_scan_abandoned" and
|
||||
# "parallel_scan_abandoned_because" if the plan took it away. So
|
||||
# chosen_for_parallel_scan names only tables that really are scanned in
|
||||
# parallel, and a query that is never a candidate says neither.
|
||||
#
|
||||
--source include/have_innodb.inc
|
||||
--source include/not_embedded.inc
|
||||
--source include/have_sequence.inc
|
||||
|
||||
--disable_service_connection
|
||||
|
||||
SET @save_trace= @@optimizer_trace;
|
||||
SET optimizer_trace='enabled=on';
|
||||
SET SESSION parallel_worker_threads=4;
|
||||
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq*10 FROM seq_1_to_2000;
|
||||
|
||||
--echo #
|
||||
--echo # A plan that keeps its parallel scan reports both keys and abandons
|
||||
--echo # nothing (expect 1 1 0)
|
||||
--echo #
|
||||
EXPLAIN SELECT a, b FROM t1 WHERE b > 19980;
|
||||
--disable_result_log
|
||||
SELECT a, b FROM t1 WHERE b > 19980;
|
||||
--enable_result_log
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
|
||||
--echo # and it is a table that really is scanned in the workers (expect 1)
|
||||
FLUSH STATUS;
|
||||
--disable_result_log
|
||||
SELECT a, b FROM t1 WHERE b > 19980;
|
||||
--enable_result_log
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers
|
||||
--enable_query_log
|
||||
|
||||
--echo #
|
||||
--echo # The gate examines the driving table, so a query it declines is not a
|
||||
--echo # candidate in the first place and names no table at all (expect 0 0 0).
|
||||
--echo # ORDER BY is declined by the query-shape half of the gate, and the
|
||||
--echo # driving table is still a plain full scan
|
||||
--echo #
|
||||
EXPLAIN SELECT a, b FROM t1 WHERE b > 19980 ORDER BY b;
|
||||
--disable_result_log
|
||||
SELECT a, b FROM t1 WHERE b > 19980 ORDER BY b;
|
||||
--enable_result_log
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
|
||||
--echo # and a covering index read by the driving-table half: the rows arrive in
|
||||
--echo # key order from an index the engine is not asked to divide (expect 0 0 0)
|
||||
--echo #
|
||||
CREATE TABLE t2 (a INT, b INT, KEY(a)) ENGINE=InnoDB;
|
||||
INSERT INTO t2 SELECT seq, seq*10 FROM seq_1_to_2000;
|
||||
EXPLAIN SELECT a FROM t2;
|
||||
--disable_result_log
|
||||
SELECT a FROM t2;
|
||||
--enable_result_log
|
||||
SELECT JSON_EXTRACT(trace,'$**.parallel_scan_candidate') IS NOT NULL AS candidate,
|
||||
JSON_EXTRACT(trace,'$**.chosen_for_parallel_scan') IS NOT NULL AS chosen,
|
||||
JSON_EXTRACT(trace,'$**.parallel_scan_abandoned') IS NOT NULL AS abandoned
|
||||
FROM information_schema.optimizer_trace;
|
||||
|
||||
DROP TABLE t1, t2;
|
||||
SET SESSION parallel_worker_threads=default;
|
||||
SET optimizer_trace= @save_trace;
|
||||
135
mysql-test/main/parallel_query_transport.result
Normal file
135
mysql-test/main/parallel_query_transport.result
Normal file
@@ -0,0 +1,135 @@
|
||||
set @save_dbug=@@global.debug_dbug;
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c VARCHAR(20)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq MOD 7, CONCAT('row', seq MOD 5)
|
||||
FROM seq_1_to_5000;
|
||||
CREATE TABLE t2 (k INT PRIMARY KEY, v INT) ENGINE=InnoDB;
|
||||
INSERT INTO t2 SELECT seq, seq*2 FROM seq_1_to_5000;
|
||||
SET SESSION parallel_worker_threads=4;
|
||||
#
|
||||
# A worker writing its rows into a container is a temporary-table write,
|
||||
# which the session can see once the workers are reaped. So the transport
|
||||
# in use is visible: rows through tmp tables, none through batches.
|
||||
#
|
||||
# tmp-table transport (expect 1 1)
|
||||
FLUSH STATUS;
|
||||
SELECT a, b, c FROM t1 WHERE a > 4995;
|
||||
a b c
|
||||
4996 5 row1
|
||||
4997 6 row2
|
||||
4998 0 row3
|
||||
4999 1 row4
|
||||
5000 2 row0
|
||||
ran_in_the_workers rows_went_through_tmp_tables
|
||||
1 1
|
||||
# batch transport, same rows (expect 1 1)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
SELECT a, b, c FROM t1 WHERE a > 4995;
|
||||
a b c
|
||||
4996 5 row1
|
||||
4997 6 row2
|
||||
4998 0 row3
|
||||
4999 1 row4
|
||||
5000 2 row0
|
||||
ran_in_the_workers no_tmp_table_rows
|
||||
1 1
|
||||
set global debug_dbug=@save_dbug;
|
||||
#
|
||||
# A container is a temporary table, and a temporary table given a unique
|
||||
# key would silently collapse repeated rows. t3 has no unique column and
|
||||
# the query reads only its two low-cardinality ones, so all 2000 rows a
|
||||
# worker stores are duplicates of each other bar six. Every one of them
|
||||
# has to come back.
|
||||
#
|
||||
CREATE TABLE t3 (b INT, c VARCHAR(20)) ENGINE=InnoDB;
|
||||
INSERT INTO t3 SELECT seq MOD 3, CONCAT('v', seq MOD 2) FROM seq_1_to_2000;
|
||||
# tmp-table transport (expect 1 1)
|
||||
FLUSH STATUS;
|
||||
SELECT b, c FROM t3;
|
||||
ran_in_the_workers every_duplicate_arrived
|
||||
1 1
|
||||
# batch transport (expect 1 1)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
SELECT b, c FROM t3;
|
||||
ran_in_the_workers every_duplicate_arrived
|
||||
1 1
|
||||
set global debug_dbug=@save_dbug;
|
||||
#
|
||||
# And over a join, the two transports agree row for row: the two blocks
|
||||
# below are the same query and must record identically.
|
||||
#
|
||||
# tmp-table transport
|
||||
SELECT t1.a, t1.c, t2.v FROM t1, t2
|
||||
WHERE t1.a = t2.k AND t1.b = 3 AND t1.a > 4900;
|
||||
a c v
|
||||
4903 row3 9806
|
||||
4910 row0 9820
|
||||
4917 row2 9834
|
||||
4924 row4 9848
|
||||
4931 row1 9862
|
||||
4938 row3 9876
|
||||
4945 row0 9890
|
||||
4952 row2 9904
|
||||
4959 row4 9918
|
||||
4966 row1 9932
|
||||
4973 row3 9946
|
||||
4980 row0 9960
|
||||
4987 row2 9974
|
||||
4994 row4 9988
|
||||
ran_in_the_workers
|
||||
1
|
||||
# batch transport
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
SELECT t1.a, t1.c, t2.v FROM t1, t2
|
||||
WHERE t1.a = t2.k AND t1.b = 3 AND t1.a > 4900;
|
||||
a c v
|
||||
4903 row3 9806
|
||||
4910 row0 9820
|
||||
4917 row2 9834
|
||||
4924 row4 9848
|
||||
4931 row1 9862
|
||||
4938 row3 9876
|
||||
4945 row0 9890
|
||||
4952 row2 9904
|
||||
4959 row4 9918
|
||||
4966 row1 9932
|
||||
4973 row3 9946
|
||||
4980 row0 9960
|
||||
4987 row2 9974
|
||||
4994 row4 9988
|
||||
ran_in_the_workers
|
||||
1
|
||||
set global debug_dbug=@save_dbug;
|
||||
#
|
||||
# Known limitation, pinned here so it is not discovered by surprise: a
|
||||
# worker's container is a heap table, nothing converts it to disk when it
|
||||
# fills, and so a result set bigger than the session's temporary-table
|
||||
# limit fails the statement. The serial plan for this query uses no
|
||||
# temporary table at all, and neither does the batch transport, so this
|
||||
# is the transport's own boundary and not the query's.
|
||||
#
|
||||
CREATE TABLE t4 (a INT PRIMARY KEY, b VARCHAR(200)) ENGINE=InnoDB;
|
||||
INSERT INTO t4 SELECT seq, REPEAT('x',200) FROM seq_1_to_20000;
|
||||
SET SESSION tmp_memory_table_size=65536;
|
||||
SET SESSION max_heap_table_size=65536;
|
||||
# tmp-table transport: 20000 rows of 200 bytes will not fit
|
||||
SELECT a, b FROM t4;
|
||||
ERROR HY000: Internal error: parallel query: the worker's result table is full (spilling a worker result set to disk is not implemented)
|
||||
# batch transport keeps nothing, so it answers (expect 20000)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
SELECT a, b FROM t4;
|
||||
rows_sent
|
||||
20000
|
||||
set global debug_dbug=@save_dbug;
|
||||
# and the server is unharmed: the same query fits once the limit does
|
||||
SET SESSION tmp_memory_table_size=default;
|
||||
SET SESSION max_heap_table_size=default;
|
||||
FLUSH STATUS;
|
||||
SELECT a, b FROM t1;
|
||||
ran_in_the_workers all_rows
|
||||
1 1
|
||||
DROP TABLE t1, t2, t3, t4;
|
||||
SET SESSION parallel_worker_threads=default;
|
||||
172
mysql-test/main/parallel_query_transport.test
Normal file
172
mysql-test/main/parallel_query_transport.test
Normal file
@@ -0,0 +1,172 @@
|
||||
#
|
||||
# MDEV-39492 Parallel Query: the result-row transport.
|
||||
#
|
||||
# A worker's finished rows reach the manager through one of two transports
|
||||
# (sql_parallel_transport.h): the temporary-table one, where a worker
|
||||
# materialises its result set into its own container and the manager reads
|
||||
# those back, or the batch one, where rows travel as record images through a
|
||||
# buffer the worker blocks on. The temporary-table transport is what a query
|
||||
# gets; the batch one is reachable under a debug flag while the two are
|
||||
# compared, so that the code kept for that comparison stays exercised.
|
||||
#
|
||||
# Everything here has to be a plain SELECT that really runs in the workers:
|
||||
# the gate declines aggregates and a derived table is materialised serially, so
|
||||
# a COUNT(*) over the query under test would measure nothing at all. Row counts
|
||||
# are read from Rows_sent for the same reason, and the statements they are read
|
||||
# for run with the prepared-statement protocol disabled: it executes a statement
|
||||
# twice and the counter would then read double.
|
||||
#
|
||||
--source include/have_innodb.inc
|
||||
--source include/not_embedded.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
|
||||
--disable_service_connection
|
||||
|
||||
set @save_dbug=@@global.debug_dbug;
|
||||
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c VARCHAR(20)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq MOD 7, CONCAT('row', seq MOD 5)
|
||||
FROM seq_1_to_5000;
|
||||
CREATE TABLE t2 (k INT PRIMARY KEY, v INT) ENGINE=InnoDB;
|
||||
INSERT INTO t2 SELECT seq, seq*2 FROM seq_1_to_5000;
|
||||
SET SESSION parallel_worker_threads=4;
|
||||
|
||||
--echo #
|
||||
--echo # A worker writing its rows into a container is a temporary-table write,
|
||||
--echo # which the session can see once the workers are reaped. So the transport
|
||||
--echo # in use is visible: rows through tmp tables, none through batches.
|
||||
--echo #
|
||||
--echo # tmp-table transport (expect 1 1)
|
||||
FLUSH STATUS;
|
||||
--sorted_result
|
||||
SELECT a, b, c FROM t1 WHERE a > 4995;
|
||||
--let $tw= query_get_value(SHOW SESSION STATUS LIKE 'Handler_tmp_write', Value, 1)
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers, $tw > 0 AS rows_went_through_tmp_tables
|
||||
--enable_query_log
|
||||
|
||||
--echo # batch transport, same rows (expect 1 1)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
--sorted_result
|
||||
SELECT a, b, c FROM t1 WHERE a > 4995;
|
||||
--let $tw= query_get_value(SHOW SESSION STATUS LIKE 'Handler_tmp_write', Value, 1)
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers, $tw = 0 AS no_tmp_table_rows
|
||||
--enable_query_log
|
||||
set global debug_dbug=@save_dbug;
|
||||
|
||||
--echo #
|
||||
--echo # A container is a temporary table, and a temporary table given a unique
|
||||
--echo # key would silently collapse repeated rows. t3 has no unique column and
|
||||
--echo # the query reads only its two low-cardinality ones, so all 2000 rows a
|
||||
--echo # worker stores are duplicates of each other bar six. Every one of them
|
||||
--echo # has to come back.
|
||||
--echo #
|
||||
CREATE TABLE t3 (b INT, c VARCHAR(20)) ENGINE=InnoDB;
|
||||
INSERT INTO t3 SELECT seq MOD 3, CONCAT('v', seq MOD 2) FROM seq_1_to_2000;
|
||||
|
||||
--echo # tmp-table transport (expect 1 1)
|
||||
FLUSH STATUS;
|
||||
--disable_ps_protocol
|
||||
--disable_result_log
|
||||
SELECT b, c FROM t3;
|
||||
--enable_result_log
|
||||
--enable_ps_protocol
|
||||
--let $rs= query_get_value(SHOW SESSION STATUS LIKE 'Rows_sent', Value, 1)
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers, $rs = 2000 AS every_duplicate_arrived
|
||||
--enable_query_log
|
||||
|
||||
--echo # batch transport (expect 1 1)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
--disable_ps_protocol
|
||||
--disable_result_log
|
||||
SELECT b, c FROM t3;
|
||||
--enable_result_log
|
||||
--enable_ps_protocol
|
||||
--let $rs= query_get_value(SHOW SESSION STATUS LIKE 'Rows_sent', Value, 1)
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers, $rs = 2000 AS every_duplicate_arrived
|
||||
--enable_query_log
|
||||
set global debug_dbug=@save_dbug;
|
||||
|
||||
--echo #
|
||||
--echo # And over a join, the two transports agree row for row: the two blocks
|
||||
--echo # below are the same query and must record identically.
|
||||
--echo #
|
||||
--echo # tmp-table transport
|
||||
--sorted_result
|
||||
SELECT t1.a, t1.c, t2.v FROM t1, t2
|
||||
WHERE t1.a = t2.k AND t1.b = 3 AND t1.a > 4900;
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers
|
||||
--enable_query_log
|
||||
|
||||
--echo # batch transport
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
--sorted_result
|
||||
SELECT t1.a, t1.c, t2.v FROM t1, t2
|
||||
WHERE t1.a = t2.k AND t1.b = 3 AND t1.a > 4900;
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers
|
||||
--enable_query_log
|
||||
set global debug_dbug=@save_dbug;
|
||||
|
||||
--echo #
|
||||
--echo # Known limitation, pinned here so it is not discovered by surprise: a
|
||||
--echo # worker's container is a heap table, nothing converts it to disk when it
|
||||
--echo # fills, and so a result set bigger than the session's temporary-table
|
||||
--echo # limit fails the statement. The serial plan for this query uses no
|
||||
--echo # temporary table at all, and neither does the batch transport, so this
|
||||
--echo # is the transport's own boundary and not the query's.
|
||||
--echo #
|
||||
CREATE TABLE t4 (a INT PRIMARY KEY, b VARCHAR(200)) ENGINE=InnoDB;
|
||||
INSERT INTO t4 SELECT seq, REPEAT('x',200) FROM seq_1_to_20000;
|
||||
SET SESSION tmp_memory_table_size=65536;
|
||||
SET SESSION max_heap_table_size=65536;
|
||||
|
||||
--echo # tmp-table transport: 20000 rows of 200 bytes will not fit
|
||||
--error ER_INTERNAL_ERROR
|
||||
SELECT a, b FROM t4;
|
||||
|
||||
--echo # batch transport keeps nothing, so it answers (expect 20000)
|
||||
set global debug_dbug="+d,pwt_batch_transport";
|
||||
FLUSH STATUS;
|
||||
--disable_ps_protocol
|
||||
--disable_result_log
|
||||
SELECT a, b FROM t4;
|
||||
--enable_result_log
|
||||
--enable_ps_protocol
|
||||
--let $rs= query_get_value(SHOW SESSION STATUS LIKE 'Rows_sent', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $rs AS rows_sent
|
||||
--enable_query_log
|
||||
set global debug_dbug=@save_dbug;
|
||||
|
||||
--echo # and the server is unharmed: the same query fits once the limit does
|
||||
SET SESSION tmp_memory_table_size=default;
|
||||
SET SESSION max_heap_table_size=default;
|
||||
FLUSH STATUS;
|
||||
--disable_ps_protocol
|
||||
--disable_result_log
|
||||
SELECT a, b FROM t1;
|
||||
--enable_result_log
|
||||
--enable_ps_protocol
|
||||
--let $rs= query_get_value(SHOW SESSION STATUS LIKE 'Rows_sent', Value, 1)
|
||||
--let $pq= query_get_value(SHOW SESSION STATUS LIKE 'Parallel_queries_executed', Value, 1)
|
||||
--disable_query_log
|
||||
--eval SELECT $pq > 0 AS ran_in_the_workers, $rs = 5000 AS all_rows
|
||||
--enable_query_log
|
||||
|
||||
DROP TABLE t1, t2, t3, t4;
|
||||
SET SESSION parallel_worker_threads=default;
|
||||
@@ -243,7 +243,7 @@ void pwt_worker_base::init_and_run_thread_func()
|
||||
mysql_mutex_lock(&thd->LOCK_thd_kill);
|
||||
mysql_mutex_unlock(&thd->LOCK_thd_kill);
|
||||
thd->pop_internal_handler(); // maybe not needed
|
||||
//
|
||||
|
||||
/*
|
||||
Null it while we still hold LOCK_worker, and tear the THD down from a local
|
||||
copy afterwards. abort_worker() reads this member under the same lock and
|
||||
@@ -268,10 +268,12 @@ void pwt_worker_base::init_and_run_thread_func()
|
||||
destroy_background_thd(worker_thd);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief
|
||||
Empty warning/error queue from workers into our manager thread
|
||||
|
||||
void pwt_manager_base::process_pending_warnings()
|
||||
{
|
||||
/*
|
||||
|
||||
@description
|
||||
Surface errors/warnings the workers queued via PWT_error_handler. A worker
|
||||
error that mattered to the result has already aborted the join during
|
||||
execution (fatal_error or a propagated kill), so thd is already in error by
|
||||
@@ -279,7 +281,13 @@ void pwt_manager_base::process_pending_warnings()
|
||||
status" assertion in the diagnostics area. So only raise a queued ERROR
|
||||
when thd is not already in error -- otherwise keep it as a warning. Plain
|
||||
warnings are always safe to add.
|
||||
*/
|
||||
|
||||
@parameters
|
||||
skip_interrupted whether or not to ignore a deluge of interrupted
|
||||
errors
|
||||
*/
|
||||
void pwt_manager_base::process_pending_warnings(bool skip_interrupted)
|
||||
{
|
||||
bool surface_drop;
|
||||
mysql_mutex_lock(&LOCK_pwt_manager);
|
||||
surface_drop= messages_dropped;
|
||||
@@ -289,12 +297,16 @@ void pwt_manager_base::process_pending_warnings()
|
||||
{
|
||||
if (pwt_error_message *err= event->error)
|
||||
{
|
||||
if (err->level == Sql_condition::enum_warning_level::WARN_LEVEL_ERROR &&
|
||||
!thd->is_error())
|
||||
my_message_sql(err->code, err->message, MYF(0));
|
||||
else
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, err->code,
|
||||
// if called during initialization, skip processing
|
||||
if (!skip_interrupted || err->code != ER_QUERY_INTERRUPTED)
|
||||
{
|
||||
if (err->level == Sql_condition::enum_warning_level::WARN_LEVEL_ERROR &&
|
||||
!thd->is_error())
|
||||
my_message_sql(err->code, err->message, MYF(0));
|
||||
else
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, err->code,
|
||||
err->message);
|
||||
}
|
||||
my_free(err->message);
|
||||
my_free(err);
|
||||
}
|
||||
@@ -336,6 +348,7 @@ bool pwt_worker_base::init_worker_thd(pwt_manager_base *manager_arg, THD *parent
|
||||
thd= create_background_thd();
|
||||
if (!thd)
|
||||
{
|
||||
my_free(const_cast<char*>(new_db.str));
|
||||
my_error(ER_INTERNAL_ERROR, MYF(0),
|
||||
"init_parallel_workers: failed to create worker thread THD");
|
||||
return true; // Failed
|
||||
@@ -435,29 +448,3 @@ pwt_manager_base::~pwt_manager_base()
|
||||
{
|
||||
mysql_mutex_destroy(&LOCK_pwt_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief
|
||||
Free our message queue, discard the messages
|
||||
*/
|
||||
|
||||
void pwt_manager_base::discard_pending_warnings()
|
||||
{
|
||||
// process queue
|
||||
if (!parallel_messages.head())
|
||||
return;
|
||||
|
||||
mysql_mutex_lock(&LOCK_pwt_manager);
|
||||
pwt_queued_event *event;
|
||||
while ((event= parallel_messages.get()))
|
||||
{
|
||||
if (pwt_error_message *err= event->error)
|
||||
{
|
||||
my_free(err->message);
|
||||
my_free(err);
|
||||
}
|
||||
my_free(event);
|
||||
}
|
||||
mysql_mutex_unlock(&LOCK_pwt_manager);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,9 +41,7 @@ public:
|
||||
|
||||
THD *thd; /* Manager thread */
|
||||
|
||||
//TODO: why does this exist, shouldn't we process them all?
|
||||
void discard_pending_warnings(); // called on initialization failure
|
||||
void process_pending_warnings(); // called at end of normal executio
|
||||
void process_pending_warnings(bool skip_interrupted);
|
||||
|
||||
// These are used by class PWT_error_handler:
|
||||
void record_event(pwt_queued_event *event)
|
||||
|
||||
@@ -331,7 +331,6 @@ cleanup_old_workers:
|
||||
mysql_mutex_unlock(&LOCK_data);
|
||||
for (uint j= 0; j < i; j++)
|
||||
workers[j].abort_worker();
|
||||
discard_pending_warnings();
|
||||
/*
|
||||
Release the transport's hold on the containers before freeing them: a sink
|
||||
may still be naming a worker THD that is now gone. Null for a worker whose
|
||||
@@ -346,6 +345,7 @@ cleanup_old_workers:
|
||||
delete[] workers;
|
||||
workers= nullptr;
|
||||
nworkers= 0;
|
||||
process_pending_warnings(true);
|
||||
mysql_cond_destroy(&COND_data_avail);
|
||||
mysql_mutex_destroy(&LOCK_data);
|
||||
file->parallel_end_coordinator();
|
||||
@@ -487,7 +487,7 @@ void pwt_manager::finalize_parallel_workers(THD *thd, JOIN *join)
|
||||
|
||||
quiesce_workers(); // stop + join (no-op if already reaped)
|
||||
exec.scan_tab->table->file->parallel_end_coordinator();
|
||||
process_pending_warnings();
|
||||
process_pending_warnings(false);
|
||||
for (uint i= 0; i < nworkers; i++) // workers are joined; both ends idle
|
||||
if (workers[i].sink)
|
||||
workers[i].sink->cleanup();
|
||||
|
||||
Reference in New Issue
Block a user