Skip to content

Commit

Permalink
MDEV-11738: Mariadb uses 100% of several of my 8 cpus doing nothing
Browse files Browse the repository at this point in the history
MDEV-11581: Mariadb starts InnoDB encryption threads
when key has not changed or data scrubbing turned off

Background: Key rotation is based on background threads
(innodb-encryption-threads) periodically going through
all tablespaces on fil_system. For each tablespace
current used key version is compared to max key age
(innodb-encryption-rotate-key-age). This process
naturally takes CPU. Similarly, in same time need for
scrubbing is investigated. Currently, key rotation
is fully supported on Amazon AWS key management plugin
only but InnoDB does not have knowledge what key
management plugin is used.

This patch re-purposes innodb-encryption-rotate-key-age=0
to disable key rotation and background data scrubbing.
All new tables are added to special list for key rotation
and key rotation is based on sending a event to
background encryption threads instead of using periodic
checking (i.e. timeout).

fil0fil.cc: Added functions fil_space_acquire_low()
to acquire a tablespace when it could be dropped concurrently.
This function is used from fil_space_acquire() or
fil_space_acquire_silent() that will not print
any messages if we try to acquire space that does not exist.
fil_space_release() to release a acquired tablespace.
fil_space_next() to iterate tablespaces in fil_system
using fil_space_acquire() and fil_space_release().
Similarly, fil_space_keyrotation_next() to iterate new
list fil_system->rotation_list where new tables.
are added if key rotation is disabled.
Removed unnecessary functions fil_get_first_space_safe()
fil_get_next_space_safe()

fil_node_open_file(): After page 0 is read read also
crypt_info if it is not yet read.

btr_scrub_lock_dict_func()
buf_page_check_corrupt()
buf_page_encrypt_before_write()
buf_merge_or_delete_for_page()
lock_print_info_all_transactions()
row_fts_psort_info_init()
row_truncate_table_for_mysql()
row_drop_table_for_mysql()
    Use fil_space_acquire()/release() to access fil_space_t.

buf_page_decrypt_after_read():
    Use fil_space_get_crypt_data() because at this point
    we might not yet have read page 0.

fil0crypt.cc/fil0fil.h: Lot of changes. Pass fil_space_t* directly
to functions needing it and store fil_space_t* to rotation state.
Use fil_space_acquire()/release() when iterating tablespaces
and removed unnecessary is_closing from fil_crypt_t. Use
fil_space_t::is_stopping() to detect when access to
tablespace should be stopped. Removed unnecessary
fil_space_get_crypt_data().

fil_space_create(): Inform key rotation that there could
be something to do if key rotation is disabled and new
table with encryption enabled is created.
Remove unnecessary functions fil_get_first_space_safe()
and fil_get_next_space_safe(). fil_space_acquire()
and fil_space_release() are used instead. Moved
fil_space_get_crypt_data() and fil_space_set_crypt_data()
to fil0crypt.cc.

fsp_header_init(): Acquire fil_space_t*, write crypt_data
and release space.

check_table_options()
	Renamed FIL_SPACE_ENCRYPTION_* TO FIL_ENCRYPTION_*

i_s.cc: Added ROTATING_OR_FLUSHING field to
information_schema.innodb_tablespace_encryption
to show current status of key rotation.
  • Loading branch information
Jan Lindström committed Mar 14, 2017
1 parent a2f3480 commit 50eb40a
Show file tree
Hide file tree
Showing 58 changed files with 3,073 additions and 2,710 deletions.
4 changes: 2 additions & 2 deletions mysql-test/suite/encryption/r/debug_key_management.result
Expand Up @@ -8,13 +8,13 @@ innodb_encryption_rotation_iops 100
innodb_encryption_threads 4
select space,name,current_key_version from information_schema.innodb_tablespaces_encryption order by space;
space name current_key_version
0 NULL 1
0 ./ibdata1 1
1 mysql/innodb_table_stats 1
2 mysql/innodb_index_stats 1
set global debug_key_management_version=10;
select space,name,current_key_version from information_schema.innodb_tablespaces_encryption order by space;
space name current_key_version
0 NULL 10
0 ./ibdata1 10
1 mysql/innodb_table_stats 10
2 mysql/innodb_index_stats 10
set global innodb_encrypt_tables=OFF;
Expand Down
35 changes: 32 additions & 3 deletions mysql-test/suite/encryption/r/encrypt_and_grep.result
Expand Up @@ -6,6 +6,16 @@ insert t1 values (repeat('foobar', 42));
insert t2 values (repeat('temp', 42));
insert t3 values (repeat('dummy', 42));
# Wait max 10 min for key encryption threads to encrypt all spaces
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
NAME
test/t3
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
NAME
mysql/innodb_table_stats
mysql/innodb_index_stats
test/t1
test/t2
./ibdata1
# t1 yes on expecting NOT FOUND
NOT FOUND /foobar/ in t1.ibd
# t2 ... on expecting NOT FOUND
Expand All @@ -15,13 +25,23 @@ FOUND /dummy/ in t3.ibd
# ibdata1 expecting NOT FOUND
NOT FOUND /foobar/ in ibdata1
# Now turn off encryption and wait for threads to decrypt everything
SET GLOBAL innodb_encryption_threads = 4;
SET GLOBAL innodb_encryption_threads = 1;
SET GLOBAL innodb_encrypt_tables = off;
# Wait max 10 min for key encryption threads to decrypt all spaces
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
NAME
mysql/innodb_table_stats
mysql/innodb_index_stats
test/t2
test/t3
./ibdata1
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
NAME
test/t1
# t1 yes on expecting NOT FOUND
NOT FOUND /foobar/ in t1.ibd
# t2 ... on expecting FOUND
FOUND /temp/ in t2.ibd
NOT FOUND /temp/ in t2.ibd
# t3 no on expecting FOUND
FOUND /dummy/ in t3.ibd
# ibdata1 expecting NOT FOUND
Expand All @@ -30,6 +50,16 @@ NOT FOUND /foobar/ in ibdata1
SET GLOBAL innodb_encryption_threads = 4;
SET GLOBAL innodb_encrypt_tables = on;
# Wait max 10 min for key encryption threads to encrypt all spaces
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
NAME
test/t3
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
NAME
mysql/innodb_table_stats
mysql/innodb_index_stats
test/t1
test/t2
./ibdata1
# t1 yes on expecting NOT FOUND
NOT FOUND /foobar/ in t1.ibd
# t2 ... on expecting NOT FOUND
Expand All @@ -38,5 +68,4 @@ NOT FOUND /temp/ in t2.ibd
FOUND /dummy/ in t3.ibd
# ibdata1 expecting NOT FOUND
NOT FOUND /foobar/ in ibdata1
# TODO: add shutdown + grep tests
drop table t1, t2, t3;
66 changes: 66 additions & 0 deletions mysql-test/suite/encryption/r/innodb-key-rotation-disable.result
@@ -0,0 +1,66 @@
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
NAME
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
NAME
mysql/innodb_table_stats
mysql/innodb_index_stats
./ibdata1
create database enctests;
use enctests;
create table t1(a int not null primary key, b char(200)) engine=innodb;
create table t2(a int not null primary key, b char(200)) engine=innodb row_format=compressed;
create table t3(a int not null primary key, b char(200)) engine=innodb page_compressed=yes;
create table t4(a int not null primary key, b char(200)) engine=innodb encrypted=yes;
create table t5(a int not null primary key, b char(200)) engine=innodb encrypted=yes row_format=compressed;
create table t6(a int not null primary key, b char(200)) engine=innodb encrypted=yes page_compressed=yes;
create table t7(a int not null primary key, b char(200)) engine=innodb encrypted=no;
create table t8(a int not null primary key, b char(200)) engine=innodb encrypted=no row_format=compressed;
create table t9(a int not null primary key, b char(200)) engine=innodb encrypted=no page_compressed=yes;
insert into t1 values (1, 'secredmessage');
insert into t2 values (1, 'secredmessage');
insert into t3 values (1, 'secredmessagecompressedaaaaaaaaabbbbbbbbbbbbbbccccccccccccccc');
insert into t4 values (1, 'secredmessage');
insert into t5 values (1, 'secredmessage');
insert into t6 values (1, 'secredmessagecompressedaaaaaaaaabbbbbbbbbbbbbbccccccccccccccc');
insert into t7 values (1, 'publicmessage');
insert into t8 values (1, 'publicmessage');
insert into t9 values (1, 'pugliccompressedaaaaaaaaabbbbbbbbbbbbbbccccccccccccccc');
# should list tables t1-t6
SELECT NAME,ENCRYPTION_SCHEME,CURRENT_KEY_ID FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0 AND NAME LIKE 'enctests%';
NAME ENCRYPTION_SCHEME CURRENT_KEY_ID
enctests/t1 1 1
enctests/t2 1 1
enctests/t3 1 1
enctests/t4 1 1
enctests/t5 1 1
enctests/t6 1 1
# should list tables t7-t9
SELECT NAME,ENCRYPTION_SCHEME,CURRENT_KEY_ID FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0 and NAME LIKE 'enctests%';
NAME ENCRYPTION_SCHEME CURRENT_KEY_ID
enctests/t7 0 1
enctests/t8 0 1
enctests/t9 0 1
SET GLOBAL innodb_encrypt_tables=OFF;
ERROR 42000: Variable 'innodb_encrypt_tables' can't be set to the value of 'OFF'
SET GLOBAL innodb_encrypt_tables=ON;
ERROR 42000: Variable 'innodb_encrypt_tables' can't be set to the value of 'ON'
# t1 default on expecting NOT FOUND
NOT FOUND /secred/ in t1.ibd
# t2 default on expecting NOT FOUND
NOT FOUND /secred/ in t2.ibd
# t3 default on expecting NOT FOUND
NOT FOUND /secred/ in t3.ibd
# t4 on expecting NOT FOUND
NOT FOUND /secred/ in t4.ibd
# t5 on expecting NOT FOUND
NOT FOUND /secred/ in t5.ibd
# t6 on expecting NOT FOUND
NOT FOUND /secred/ in t6.ibd
# t7 off expecting FOUND
FOUND /public/ in t7.ibd
# t8 row compressed expecting NOT FOUND
FOUND /public/ in t8.ibd
# t9 page compressed expecting NOT FOUND
NOT FOUND /public/ in t9.ibd
use test;
drop database enctests;
1 change: 1 addition & 0 deletions mysql-test/suite/encryption/r/innodb_encryption.result
Expand Up @@ -17,6 +17,7 @@ CURRENT_KEY_VERSION int(11) unsigned NO 0
KEY_ROTATION_PAGE_NUMBER bigint(21) unsigned YES NULL
KEY_ROTATION_MAX_PAGE_NUMBER bigint(21) unsigned YES NULL
CURRENT_KEY_ID int(11) unsigned NO 0
ROTATING_OR_FLUSHING int(1) unsigned NO 0
# Wait max 5 min for key encryption threads to encrypt one space
# Success!
# Wait max 10 min for key encryption threads to encrypt all space
Expand Down
41 changes: 3 additions & 38 deletions mysql-test/suite/encryption/r/innodb_onlinealter_encryption.result
Expand Up @@ -24,7 +24,7 @@ end while;
end//
commit;
set autocommit=0;
call innodb_insert_proc(15000);
call innodb_insert_proc(1500);
commit;
set autocommit=1;
# Wait max 10 min for key encryption threads to encrypt all spaces
Expand All @@ -42,6 +42,8 @@ NOT FOUND /author/ in t5.ibd
NOT FOUND /mangled/ in t6.ibd
# t7 ... on expecting NOT FOUND
NOT FOUND /mysql/ in t7.ibd
SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON;
ALTER TABLE t1 ADD COLUMN b int default 2;
ALTER TABLE t2 ADD COLUMN b int default 2;
ALTER TABLE t7 ADD COLUMN b int default 2;
Expand Down Expand Up @@ -135,42 +137,5 @@ NOT FOUND /author/ in t5.ibd
NOT FOUND /mangled/ in t6.ibd
# t7 ... on expecting NOT FOUND
NOT FOUND /mysql/ in t7.ibd
# Restarting server
# Done restarting server
select count(1) from t1;
count(1)
15000
select count(1) from t2;
count(1)
15000
select count(1) from t3;
count(1)
15000
select count(1) from t4;
count(1)
15000
select count(1) from t5;
count(1)
15000
select count(1) from t6;
count(1)
15000
select count(1) from t7;
count(1)
15000
# t1 yes on expecting NOT FOUND
NOT FOUND /foobar/ in t1.ibd
# t2 ... on expecting NOT FOUND
NOT FOUND /temp/ in t2.ibd
# t3 ... on expecting NOT FOUND
NOT FOUND /barfoo/ in t3.ibd
# t4 ... on expecting NOT FOUND
NOT FOUND /repeat/ in t4.ibd
# t5 ... on expecting NOT FOUND
NOT FOUND /author/ in t5.ibd
# t6 ... on expecting NOT FOUND
NOT FOUND /mangled/ in t6.ibd
# t7 ... on expecting NOT FOUND
NOT FOUND /mysql/ in t7.ibd
DROP PROCEDURE innodb_insert_proc;
DROP TABLE t1, t2, t3, t4, t5, t6, t7;
3 changes: 1 addition & 2 deletions mysql-test/suite/encryption/t/encrypt_and_grep.opt
@@ -1,8 +1,7 @@
--innodb-encrypt-tables=ON
--innodb-encrypt-log=ON
--innodb-encryption-rotate-key-age=15
--innodb-encryption-threads=4
--innodb-encryption-threads=1
--innodb-tablespaces-encryption
--innodb-max-dirty-pages-pct=0.001


29 changes: 22 additions & 7 deletions mysql-test/suite/encryption/t/encrypt_and_grep.test
@@ -1,5 +1,5 @@
-- source include/have_innodb.inc
-- source include/have_example_key_management_plugin.inc
-- source include/have_file_key_management_plugin.inc

# embedded does not support restart
-- source include/not_embedded.inc
Expand Down Expand Up @@ -30,7 +30,10 @@ insert t3 values (repeat('dummy', 42));
--let $wait_condition=SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0
--source include/wait_condition.inc

--sleep 5
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;

--source include/shutdown_mysqld.inc

--let SEARCH_PATTERN=foobar
--echo # t1 yes on expecting NOT FOUND
Expand All @@ -49,15 +52,21 @@ insert t3 values (repeat('dummy', 42));
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc

-- source include/start_mysqld.inc

--echo # Now turn off encryption and wait for threads to decrypt everything
SET GLOBAL innodb_encryption_threads = 4;
SET GLOBAL innodb_encryption_threads = 1;
SET GLOBAL innodb_encrypt_tables = off;

--echo # Wait max 10 min for key encryption threads to decrypt all spaces
--let $wait_timeout= 600
--let $wait_condition=SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
--let $wait_condition=SELECT COUNT(*) = 5 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0 AND KEY_ROTATION_PAGE_NUMBER IS NULL;
--source include/wait_condition.inc
--sleep 5

SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;

--source include/shutdown_mysqld.inc

--let SEARCH_PATTERN=foobar
--echo # t1 yes on expecting NOT FOUND
Expand All @@ -76,6 +85,8 @@ SET GLOBAL innodb_encrypt_tables = off;
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc

-- source include/start_mysqld.inc

--echo # Now turn on encryption and wait for threads to encrypt all spaces
SET GLOBAL innodb_encryption_threads = 4;
SET GLOBAL innodb_encrypt_tables = on;
Expand All @@ -84,7 +95,11 @@ SET GLOBAL innodb_encrypt_tables = on;
--let $wait_timeout= 600
--let $wait_condition=SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
--source include/wait_condition.inc
--sleep 5

SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0;
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;

--source include/shutdown_mysqld.inc

--let SEARCH_PATTERN=foobar
--echo # t1 yes on expecting NOT FOUND
Expand All @@ -103,6 +118,6 @@ SET GLOBAL innodb_encrypt_tables = on;
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc

--echo # TODO: add shutdown + grep tests
-- source include/start_mysqld.inc

drop table t1, t2, t3;
5 changes: 5 additions & 0 deletions mysql-test/suite/encryption/t/innodb-key-rotation-disable.opt
@@ -0,0 +1,5 @@
--innodb-encrypt-tables
--innodb-encrypt-log
--innodb-encryption-rotate-key-age=0
--innodb-encryption-threads=4
--innodb-tablespaces-encryption

0 comments on commit 50eb40a

Please sign in to comment.