Skip to content

Commit 759e352

Browse files
committed
MDEV-38026 Recovery of FILE_CREATE fails to create a file
fil_ibd_create(): Add a DEBUG_SYNC point for the test case. fil_node_open_file_low(): If node->deferred is set, set the OS_FILE_ON_ERROR_SILENT flag on OS_FILE_OPEN and attempt OS_FILE_CREATE if needed. If this fails, then InnoDB will refuse to start up, giving the operator a chance to resolve the situation, for example by freeing up some space in the file system. recv_validate_tablespace(): Invoke deferred_spaces.add() on any missing tablespace for which we know the LSN of the FILE_CREATE record. In this way, fil_node_open_file_low() will end up being invoked on files that are supposed to be created. fil_name_process(): For FILE_CREATE, remember the create_lsn. recv_sys_t::parse(): Pass FILE_CREATE to fil_name_process(). Some existing tests have been adjusted for the improved recovery of file creation. Reviewed by: Thirunarayanan Balathandayuthapani Tested by: Saahil Alam
1 parent 14d9323 commit 759e352

File tree

11 files changed

+134
-102
lines changed

11 files changed

+134
-102
lines changed

mysql-test/suite/innodb/r/alter_crash.result

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,26 @@ SELECT * FROM t1;
220220
a b
221221
1 1
222222
2 1
223+
connect ddl, localhost, root;
224+
SET DEBUG_SYNC = 'fil_ibd_create_logged SIGNAL stuck WAIT_FOR ever';
225+
ALTER TABLE t1 FORCE;
226+
connection default;
227+
SET DEBUG_SYNC = 'now WAIT_FOR stuck';
228+
# Kill the server
229+
disconnect ddl;
230+
# restart
231+
CHECK TABLE t1;
232+
Table Op Msg_type Msg_text
233+
test.t1 check status OK
234+
SHOW CREATE TABLE t1;
235+
Table Create Table
236+
t1 CREATE TABLE `t1` (
237+
`a` int(11) NOT NULL,
238+
`b` int(11) DEFAULT NULL,
239+
PRIMARY KEY (`a`)
240+
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
241+
SELECT * FROM t1;
242+
a b
243+
1 1
244+
2 1
223245
DROP TABLE t1;

mysql-test/suite/innodb/r/alter_kill.result

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ BEGIN;
3636
INSERT INTO t1 VALUES(42);
3737
connect con1,localhost,root;
3838
CREATE TABLE bug16735660 (a INT PRIMARY KEY) ENGINE=InnoDB;
39+
SET GLOBAL innodb_max_purge_lag_wait=2;
40+
SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
41+
SET GLOBAL innodb_max_dirty_pages_pct=0.0;
42+
SET GLOBAL innodb_max_dirty_pages_pct=90.0;
3943
XA START 'x';
4044
INSERT INTO bug16735660 VALUES(1),(2),(3);
4145
XA END 'x';

mysql-test/suite/innodb/r/innodb-index.result

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,16 +1920,10 @@ CREATE TABLE t2(f1 INT PRIMARY KEY)ENGINE=InnoDB;
19201920
# Kill the server
19211921
# Wrong space_id in a dirty file and a missing file
19221922
# restart
1923-
SELECT * FROM INFORMATION_SCHEMA.ENGINES
1924-
WHERE engine = 'innodb'
1925-
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1926-
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
1927-
# Restore t1 and t2
1928-
# restart
1929-
SELECT * FROM t1;
1930-
f1
1931-
SELECT * FROM t2;
1932-
f1
1923+
CHECK TABLE t1, t2;
1924+
Table Op Msg_type Msg_text
1925+
test.t1 check status OK
1926+
test.t2 check status OK
19331927
DROP TABLE t1, t2;
19341928
#
19351929
# MDEV-18186 assertion failure on missing InnoDB index

mysql-test/suite/innodb/r/log_file_name.result

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,6 @@ WHERE engine = 'innodb'
1919
AND support IN ('YES', 'DEFAULT', 'ENABLED');
2020
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
2121
FOUND 1 /InnoDB: Ignoring data file '.*t2.ibd' with space ID \d+. Another data file called .*t1.ibd exists with the same space ID/ in mysqld.1.err
22-
# Fault 2: Wrong space_id in a dirty file, and a missing file.
23-
# restart
24-
SELECT * FROM INFORMATION_SCHEMA.ENGINES
25-
WHERE engine = 'innodb'
26-
AND support IN ('YES', 'DEFAULT', 'ENABLED');
27-
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
28-
FOUND 1 /InnoDB: Tablespace \d+ was not found at.*t3.ibd/ in mysqld.1.err
29-
# Fault 3: Wrong space_id in a dirty file, and no missing file.
30-
# restart
31-
SELECT * FROM INFORMATION_SCHEMA.ENGINES
32-
WHERE engine = 'innodb'
33-
AND support IN ('YES', 'DEFAULT', 'ENABLED');
34-
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
35-
FOUND 1 /InnoDB: Ignoring data file '.*t[23].ibd' with space ID/ in mysqld.1.err
36-
NOT FOUND /InnoDB: Tablespace \d+ was not found at .*t1.ibd/ in mysqld.1.err
37-
FOUND 1 /InnoDB: Tablespace \d+ was not found at .*t3.ibd/ in mysqld.1.err
38-
FOUND 2 /InnoDB: Set innodb_force_recovery=1 to ignore this and to permanently lose all changes to the tablespace/ in mysqld.1.err
3922
# Fault 4: Missing data file
4023
# restart
4124
SELECT * FROM INFORMATION_SCHEMA.ENGINES
@@ -57,6 +40,7 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
5740
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
5841
NOT FOUND /\[Note\] InnoDB: Cannot read first page of .*t2.ibd/ in mysqld.1.err
5942
FOUND 1 /.*\[ERROR\] InnoDB: Cannot apply log to \[page id: space=[1-9][0-9]*, page number=3\] of corrupted file './test/t2\.ibd'/ in mysqld.1.err
43+
# Fault 2: Wrong space_id in a dirty file, and a missing file.
6044
# restart
6145
SELECT * FROM t2;
6246
a
@@ -78,6 +62,16 @@ CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
7862
ERROR HY000: Can't create table `test`.`t0` (errno: 184 "Tablespace already exists")
7963
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
8064
DROP TABLE t0;
65+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
66+
ERROR HY000: Can't create table `test`.`t1` (errno: 184 "Tablespace already exists")
67+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
68+
ERROR HY000: Can't create table `test`.`t1` (errno: 184 "Tablespace already exists")
69+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
70+
DROP TABLE t1;
71+
SET GLOBAL innodb_max_purge_lag_wait=0;
72+
SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
73+
SET GLOBAL innodb_max_dirty_pages_pct=0.0;
74+
SET GLOBAL innodb_max_dirty_pages_pct=90.0;
8175
CREATE TABLE u1(a INT PRIMARY KEY) ENGINE=InnoDB;
8276
CREATE TABLE u2(a INT PRIMARY KEY) ENGINE=InnoDB;
8377
CREATE TABLE u3(a INT PRIMARY KEY) ENGINE=InnoDB;

mysql-test/suite/innodb/t/alter_crash.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ SET GLOBAL innodb_fil_make_page_dirty_debug=0;
223223
disconnect ddl;
224224
--source include/start_mysqld.inc
225225

226+
CHECK TABLE t1;
227+
SHOW CREATE TABLE t1;
228+
SELECT * FROM t1;
229+
230+
connect ddl, localhost, root;
231+
SET DEBUG_SYNC = 'fil_ibd_create_logged SIGNAL stuck WAIT_FOR ever';
232+
send ALTER TABLE t1 FORCE;
233+
connection default;
234+
SET DEBUG_SYNC = 'now WAIT_FOR stuck';
235+
236+
--source include/kill_mysqld.inc
237+
disconnect ddl;
238+
--source include/start_mysqld.inc
239+
226240
CHECK TABLE t1;
227241
SHOW CREATE TABLE t1;
228242
SELECT * FROM t1;

mysql-test/suite/innodb/t/alter_kill.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ INSERT INTO t1 VALUES(42);
140140

141141
CREATE TABLE bug16735660 (a INT PRIMARY KEY) ENGINE=InnoDB;
142142

143+
# Force a log checkpoint to "discard" any FILE_CREATE records.
144+
SET GLOBAL innodb_max_purge_lag_wait=2;
145+
SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
146+
SET GLOBAL innodb_max_dirty_pages_pct=0.0;
147+
let $wait_condition =
148+
SELECT variable_value = 0
149+
FROM information_schema.global_status
150+
WHERE variable_name = 'INNODB_BUFFER_POOL_PAGES_DIRTY';
151+
--source include/wait_condition.inc
152+
SET GLOBAL innodb_max_dirty_pages_pct=90.0;
153+
143154
XA START 'x';
144155
--source ../include/no_checkpoint_start.inc
145156
INSERT INTO bug16735660 VALUES(1),(2),(3);

mysql-test/suite/innodb/t/innodb-index.test

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,26 +1136,14 @@ CREATE TABLE t2(f1 INT PRIMARY KEY)ENGINE=InnoDB;
11361136
--let CLEANUP_IF_CHECKPOINT=DROP TABLE t1, t2;
11371137
--source include/no_checkpoint_end.inc
11381138

1139-
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
1140-
let $check_no_innodb=SELECT * FROM INFORMATION_SCHEMA.ENGINES
1141-
WHERE engine = 'innodb'
1142-
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1143-
11441139
--echo # Wrong space_id in a dirty file and a missing file
1145-
1146-
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t0.ibd
11471140
--move_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t1.ibd
11481141

11491142
--source include/start_mysqld.inc
1150-
--eval $check_no_innodb
1151-
--source include/shutdown_mysqld.inc
1152-
1153-
--echo # Restore t1 and t2
1154-
1155-
--move_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
1156-
--move_file $MYSQLD_DATADIR/test/t0.ibd $MYSQLD_DATADIR/test/t1.ibd
1143+
# Because the tables were created after the checkpoint,
1144+
# InnoDB will start up, but the tables will be corrupted.
1145+
CHECK TABLE t1, t2;
11571146

1158-
--source include/start_mysqld.inc
11591147
if ($have_debug) {
11601148
# Initiate shutdown in order to issue a redo log checkpoint and to discard
11611149
# the redo log record that was emitted due to '+d,fil_names_write_bogus'.
@@ -1164,9 +1152,6 @@ if ($have_debug) {
11641152
--let $restart_noprint=0
11651153
}
11661154

1167-
SELECT * FROM t1;
1168-
SELECT * FROM t2;
1169-
11701155
DROP TABLE t1, t2;
11711156

11721157
--echo #

mysql-test/suite/innodb/t/log_file_name.test

Lines changed: 25 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -45,53 +45,13 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
4545
eval $check_no_innodb;
4646
let SEARCH_PATTERN= InnoDB: Ignoring data file '.*t2.ibd' with space ID \d+. Another data file called .*t1.ibd exists with the same space ID;
4747
--source include/search_pattern_in_file.inc
48-
49-
--source include/shutdown_mysqld.inc
50-
51-
--remove_file $MYSQLD_DATADIR/test/t1.ibd
52-
53-
# This could fail to refuse InnoDB startup, in case there was a log
54-
# checkpoint after the CREATE TABLE t3. That is what we checked above.
55-
--echo # Fault 2: Wrong space_id in a dirty file, and a missing file.
56-
--move_file $MYSQLD_DATADIR/test/t3.ibd $MYSQLD_DATADIR/test/t1.ibd
57-
58-
--source include/start_mysqld.inc
59-
eval $check_no_innodb;
60-
61-
let SEARCH_PATTERN= InnoDB: Tablespace \d+ was not found at.*t3.ibd;
62-
--source include/search_pattern_in_file.inc
63-
6448
--source include/shutdown_mysqld.inc
6549

66-
--move_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t3.ibd
67-
68-
--echo # Fault 3: Wrong space_id in a dirty file, and no missing file.
69-
# Swap t2.ibd and t3.ibd.
70-
--move_file $MYSQLD_DATADIR/test/t3.ibd $MYSQLD_DATADIR/test/t.ibd
71-
--move_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t3.ibd
72-
--move_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t2.ibd
73-
74-
--source include/start_mysqld.inc
75-
eval $check_no_innodb;
76-
77-
let SEARCH_PATTERN= InnoDB: Ignoring data file '.*t[23].ibd' with space ID;
78-
--source include/search_pattern_in_file.inc
79-
80-
let SEARCH_PATTERN= InnoDB: Tablespace \d+ was not found at .*t1.ibd;
81-
--source include/search_pattern_in_file.inc
82-
let SEARCH_PATTERN= InnoDB: Tablespace \d+ was not found at .*t3.ibd;
83-
--source include/search_pattern_in_file.inc
84-
let SEARCH_PATTERN= InnoDB: Set innodb_force_recovery=1 to ignore this and to permanently lose all changes to the tablespace;
85-
--source include/search_pattern_in_file.inc
86-
87-
--source include/shutdown_mysqld.inc
88-
89-
# Swap back t3.ibd, but hide t2.ibd (which the redo log also knows as t1.ibd).
90-
--move_file $MYSQLD_DATADIR/test/t3.ibd $MYSQLD_DATADIR/test/t.ibd
91-
--move_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t3.ibd
92-
9350
--echo # Fault 4: Missing data file
9451

52+
--move_file $MYSQLD_DATADIR/test/t2.ibd $MYSQLD_DATADIR/test/t.ibd
53+
--remove_file $MYSQLD_DATADIR/test/t1.ibd
54+
9555
--source include/start_mysqld.inc
9656
eval $check_no_innodb;
9757
--source include/shutdown_mysqld.inc
@@ -108,15 +68,6 @@ let SEARCH_PATTERN= InnoDB: Tablespace \d+ was not found at .*t[12].ibd.
10868
eval $check_no_innodb;
10969
--source include/shutdown_mysqld.inc
11070

111-
# On Linux, lseek() would typically report EINVAL when invoked on a directory.
112-
# On other plaftorms as well as some GNU/Linux based environments, such as
113-
# a Ubuntu 22.04 based image on Amazon Web Services,
114-
# os_file_get_size() would succeed on a directory, and we would get another
115-
# error about inability to apply log to a corrupted page.
116-
117-
#--let SEARCH_PATTERN= InnoDB: Could not measure the size of single-table tablespace file '.*test/t2\\.ibd'
118-
#--source include/search_pattern_in_file.inc
119-
12071
--rmdir $MYSQLD_DATADIR/test/t2.ibd
12172

12273
# Create a short file.
@@ -136,6 +87,9 @@ let SEARCH_PATTERN= .*\[ERROR\] InnoDB: Cannot apply log to \\[page id: space=[1
13687
--remove_file $MYSQLD_DATADIR/test/t2.ibd
13788
--move_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t2.ibd
13889

90+
--echo # Fault 2: Wrong space_id in a dirty file, and a missing file.
91+
--move_file $MYSQLD_DATADIR/test/t3.ibd $MYSQLD_DATADIR/test/t1.ibd
92+
13993
--source include/start_mysqld.inc
14094

14195
SELECT * FROM t2;
@@ -150,13 +104,20 @@ CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
150104
--remove_file $MYSQLD_DATADIR/test/t0.ibd
151105
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
152106
DROP TABLE t0;
107+
--error ER_CANT_CREATE_TABLE
108+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
109+
--error ER_CANT_CREATE_TABLE
110+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
111+
--remove_file $MYSQLD_DATADIR/test/t1.ibd
112+
CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB;
113+
DROP TABLE t1;
153114

154115
--disable_query_log
155116
# The following are for the orphan file t0.ibd or for the directory t2.ibd:
156117
call mtr.add_suppression("InnoDB: Operating system error number [0-9]* in a file operation");
157118
call mtr.add_suppression("InnoDB: Error number [0-9]* means '(File exists|Is a directory|Invalid argument)'");
158119
call mtr.add_suppression("InnoDB: Cannot create file '.*t0.ibd'");
159-
call mtr.add_suppression("InnoDB: The file '.*t0\.ibd' already exists");
120+
call mtr.add_suppression("InnoDB: The file '.*t[01]\.ibd' already exists");
160121
call mtr.add_suppression("InnoDB: Cannot open datafile for read-write: '.*t2\.ibd'");
161122
call mtr.add_suppression("InnoDB: Could not measure the size of single-table tablespace file '.*test/t2\\.ibd'");
162123
# The following are for aborted startup without --innodb-force-recovery:
@@ -187,6 +148,17 @@ call mtr.add_suppression("InnoDB: Cannot read first page in datafile:");
187148
FLUSH TABLES;
188149
--enable_query_log
189150

151+
# Force a log checkpoint to "discard" any FILE_CREATE records.
152+
SET GLOBAL innodb_max_purge_lag_wait=0;
153+
SET GLOBAL innodb_max_dirty_pages_pct_lwm=0.0;
154+
SET GLOBAL innodb_max_dirty_pages_pct=0.0;
155+
let $wait_condition =
156+
SELECT variable_value = 0
157+
FROM information_schema.global_status
158+
WHERE variable_name = 'INNODB_BUFFER_POOL_PAGES_DIRTY';
159+
--source include/wait_condition.inc
160+
SET GLOBAL innodb_max_dirty_pages_pct=90.0;
161+
190162
--source include/no_checkpoint_start.inc
191163

192164
CREATE TABLE u1(a INT PRIMARY KEY) ENGINE=InnoDB;

storage/innobase/fil/fil0fil.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,19 +357,34 @@ static bool fil_node_open_file_low(fil_node_t *node, const byte *page,
357357
node->handle= os_file_create(innodb_data_file_key, node->name,
358358
node->is_raw_disk
359359
? OS_FILE_OPEN_RAW | OS_FILE_ON_ERROR_NO_EXIT
360+
: node->deferred
361+
? OS_FILE_OPEN | OS_FILE_ON_ERROR_NO_EXIT
362+
| OS_FILE_ON_ERROR_SILENT
360363
: OS_FILE_OPEN | OS_FILE_ON_ERROR_NO_EXIT,
361364
type,
362365
srv_read_only_mode, &success);
366+
ut_ad(success == node->is_open());
363367

364368
if (success && node->is_open())
365369
{
370+
created:
366371
#ifndef _WIN32
367372
if (!node->space->id && !srv_read_only_mode && my_disable_locking &&
368373
os_file_lock(node->handle, node->name))
369374
goto fail;
370375
#endif
371376
break;
372377
}
378+
else if (node->deferred)
379+
{
380+
node->handle= os_file_create(innodb_data_file_key, node->name,
381+
OS_FILE_CREATE | OS_FILE_ON_ERROR_NO_EXIT,
382+
type,
383+
srv_read_only_mode, &success);
384+
ut_ad(success == node->is_open());
385+
if (node->is_open())
386+
goto created;
387+
}
373388

374389
/* The following call prints an error message */
375390
if (os_file_get_last_error(true) == EMFILE + 100 &&
@@ -1912,6 +1927,8 @@ fil_ibd_create(
19121927
log_write_up_to(mtr.commit_lsn(), true);
19131928
}
19141929

1930+
DEBUG_SYNC_C("fil_ibd_create_logged");
1931+
19151932
bool success;
19161933
pfs_os_file_t file = os_file_create(
19171934
innodb_data_file_key, path,

storage/innobase/include/fil0fil.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,7 @@ struct fil_node_t final
11441144
/** whether the file actually is a raw device or disk partition */
11451145
unsigned is_raw_disk:1;
11461146
/** whether the tablespace discovery is being deferred during crash
1147-
recovery due to incompletely written page 0 */
1147+
recovery due to missing file or incompletely written page 0 */
11481148
unsigned deferred:1;
11491149

11501150
/** size of the file in database pages (0 if not known yet);

0 commit comments

Comments
 (0)