File: log_writer_extra_margin.test

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (204 lines) | stat: -rw-r--r-- 8,477 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# This test forces InnoDB to behave in a wrong way by missing a lot of calls
# to the log_free_check(). The page flushing is disabled, so the log writer
# thread quickly enters its private so-called extra_margin. This results in
# a warning being emitted (verified by the test). Soon the log_writer runs
# out of space in the extra_margin. Then error is emitted (verified).

# Since then an attempt to update a row ends up in a hanging UPDATE query.

# This is a moment, which simulates the situation that could potentially
# have happened if some code-path was missing calls to the log_free_check()
# and the oldest dirty page became re-modified and latched preventing the
# last_checkpoint_lsn from being advanced (simulated by the disabled page
# cleaners for sake of simplicity). If that happened during ordinary workload,
# the only help can come from DBA, who needs to observe the warning / error
# that were emitted and resize the redo log, increasing its capacity.

# Therefore this is what is done in this test. The redo log becomes increased
# by 1MB. However page cleaners are still disabled so the redo log becomes
# filled again (verified). That happens without exiting the extra_margin
# meanwhile, because the extra_margin is slightly bigger than 1MB.

# Then the page cleaners are enabled and the hanging UPDATE becomes finished
# (verified). Then the original redo log capacity is restored and it might
# take some time but eventually the redo log should become resized down.

# A next UPDATE should be finished easily (verified).

--source include/have_debug.inc
--source include/have_debug_sync.inc

# Required to avoid full log buffer flushes and unexpected waits on mutex
# inside binlog code when some threads are waiting for free space in redo.
--source include/not_log_bin.inc

# Save the initial number of concurrent sessions.
--source include/count_sessions.inc

SELECT @@innodb_redo_log_capacity INTO @orig_redo_log_capacity;
SELECT @@innodb_flush_log_at_trx_commit INTO @orig_flush_log_at_trx_commit;

SET GLOBAL innodb_flush_log_at_trx_commit = 0;

--echo # Prepare schema used in the tests.
--source include/ib_log_spammer_init.inc
CREATE TABLE t (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
INSERT INTO t(a,b) VALUES (1,0);

--echo # Disable page cleaners and prevent page flushing/checkpointing.
--source ../include/log_disable_page_cleaners.inc

--echo # Skip log free checks to fill redo log up to its very end.
SET GLOBAL DEBUG = '+d,log_free_check_skip';

--echo # Request the log writer to generate a signal when it enters the extra_margin.
SET GLOBAL DEBUG = '+d,syncpoint_log_writer_entered_extra_margin';

--echo # Fill redo log until the log writer enters the extra_margin...
--connect(spam_worker_connection,localhost,root,,test)
--send CALL log_spammer()
--connection default

--echo # Redo log data is being generated...

--echo # Wait until the log_writer signaled that it entered the extra margin.
SET DEBUG_SYNC = 'now WAIT_FOR reached_log_writer_entered_extra_margin';

--echo # Log writer entered the extra margin !
let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=\[Warning\] \[[^]]*\] \[[^]]*\] Redo log is running out of free space, pausing user threads;
--source include/search_pattern.inc

SET GLOBAL innodb_flush_log_at_trx_commit = 2;

--echo # Request the log_writer to generate a signal when it runs out of free space in the extra_margin.
SET GLOBAL DEBUG = '+d,syncpoint_log_writer_ran_out_space';

--echo # Let the log writer continue, and wait until it runs out of free space in the extra_margin.
SET GLOBAL DEBUG = '-d,syncpoint_log_writer_entered_extra_margin';
SET DEBUG_SYNC = 'now SIGNAL continue_log_writer_entered_extra_margin WAIT_FOR reached_log_writer_ran_out_space';

--echo # Log writer ran out of free space !
let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=\[ERROR\] \[[^]]*\] \[[^]]*\] Out of space in the redo log. Checkpoint LSN:;
--source include/search_pattern.inc

--echo # Let log writer continue to try... It cannot succeed without checkpoint being advanced.
SET GLOBAL DEBUG = '-d,syncpoint_log_writer_ran_out_space';
SET DEBUG_SYNC = 'now SIGNAL continue_log_writer_ran_out_space';

SET GLOBAL innodb_flush_log_at_trx_commit = 1;

--echo # Start a DML query which should be hanging on trx_commit_complete_for_mysql...
--connect(dml_connection,localhost,root,,test)
--send UPDATE t SET b=1 WHERE a=1
--connection default

--echo # Wait until UPDATE becomes visible in information_schema.processlist...
--let $wait_condition=SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE info = 'UPDATE t SET b=1 WHERE a=1'
--let $wait_timeout=60
--source include/wait_condition.inc

--echo # Sleep for 1s to validate it is still there (not yet completed).
--sleep 1

--echo # Ensure the DML has not been completed (no redo space)...
--let $assert_text='The UPDATE is still not finished'
--let $assert_cond= "[SELECT COUNT(*) FROM information_schema.processlist WHERE info = \'UPDATE t SET b=1 WHERE a=1\']" > 0
--source include/assert.inc

--echo # Assume that after resizing up we will be more lucky, not to hit the same issue again
--echo # but at higher redo log size, so let's resume log_free_check() (else: we could quickly
--echo # eat the whole extended space and remain in deadlock)

SET GLOBAL DEBUG = '-d,log_free_check_skip';

--echo # Resize-up redo log a little bit to ensure it will be filled again.
SET GLOBAL innodb_redo_log_capacity = @@innodb_redo_log_capacity + 1048576;

--echo # Ensure the log writer has still NOT exited the extra margin.
let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=\[Note\] \[[^]]*\] \[[^]]*\] Redo log reclaimed some free space, resuming user threads;
--source include/search_pattern.inc

--echo # Request the log writer to generate a signal when it exits the extra_margin.
SET GLOBAL DEBUG = '+d,syncpoint_log_writer_exited_extra_margin';

--echo ###############################
--echo # Enable page cleaners.
--echo ###############################

--source ../include/log_enable_page_cleaners.inc

--echo # Ensure the started DML becomes completed eventually.
--connection dml_connection
--reap
--connection default
--disconnect dml_connection

--let $assert_text='The UPDATE has been completed'
--let $assert_cond="[SELECT COUNT(*) FROM information_schema.processlist WHERE info = \'UPDATE t SET b=1 WHERE a=1\']" = 0
--source include/assert.inc

--echo # Request the spammer to stop, but do not wait because earlier the log_writer/log_checkpointer
--echo # can be trapped at log_writer_exited_extra_margin

--connect(spam_controller_connection,localhost,root,,test)
--send UPDATE log_spam_controller SET status = 'Stopping' WHERE id = 1
--connection default

--echo # Wait until the log_writer signaled that it exited the extra margin.

SET DEBUG_SYNC = 'now WAIT_FOR reached_log_writer_exited_extra_margin';
SET GLOBAL DEBUG = '-d,syncpoint_log_writer_exited_extra_margin';
SET DEBUG_SYNC = 'now SIGNAL continue_log_writer_exited_extra_margin';

--echo # Ensure the log writer has exited the extra margin.
let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=\[Note\] \[[^]]*\] \[[^]]*\] Redo log reclaimed some free space, resuming user threads;
--source include/search_pattern.inc

--echo # Wait until the request to stop spammer is completed.

--connection spam_controller_connection
--reap
--connection default
--disconnect spam_controller_connection

--echo # Wait until the spammer is stopped.

--connection spam_worker_connection
--reap
--connection default

--disconnect spam_worker_connection

--let $assert_text='The log spammer is stopped'
--let $assert_cond="[SELECT status FROM log_spam_controller WHERE id = 1]" = "Stopped"
--source include/assert.inc

--echo # Ensure new changes can be done quickly too.
UPDATE t SET b=2 WHERE a=1;
SELECT * FROM t;

--echo # Resize the redo log to its original capacity.

SET GLOBAL innodb_redo_log_capacity = @orig_redo_log_capacity;

--echo # Waiting for status = OK....
let $wait_timeout= 600;
let $wait_condition =
  SELECT variable_value = 'OK'
  FROM performance_schema.global_status
  WHERE variable_name = 'Innodb_redo_log_resize_status';
--source include/wait_condition.inc

--echo # Cleanup
DROP TABLE t;
--source include/ib_log_spammer_cleanup.inc

SET GLOBAL innodb_flush_log_at_trx_commit = @orig_flush_log_at_trx_commit;

SET DEBUG_SYNC = 'RESET';

--source include/wait_until_count_sessions.inc