1 /*****************************************************************************
2
3 Copyright (c) 2011, 2019, Oracle and/or its affiliates. All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25 *****************************************************************************/
26
27 /**************************************************//**
28 @file buf/buf0dump.cc
29 Implements a buffer pool dump/load.
30
31 Created April 08, 2011 Vasil Dimov
32 *******************************************************/
33
34 #include "my_global.h"
35 #include "my_sys.h"
36 #include "my_thread.h"
37
38 #include "mysql/psi/mysql_stage.h"
39 #include "mysql/psi/psi.h"
40
41 #include "univ.i"
42
43 #include "buf0buf.h"
44 #include "buf0dump.h"
45 #include "dict0dict.h"
46 #include "os0file.h"
47 #include "os0thread.h"
48 #include "srv0srv.h"
49 #include "srv0start.h"
50 #include "sync0rw.h"
51 #include "ut0byte.h"
52
53 #include <algorithm>
54
55 enum status_severity {
56 STATUS_VERBOSE,
57 STATUS_INFO,
58 STATUS_ERR
59 };
60
61 #define SHUTTING_DOWN() (srv_shutdown_state != SRV_SHUTDOWN_NONE)
62
63 /* Flags that tell the buffer pool dump/load thread which action should it
64 take after being waked up. */
65 static ibool buf_dump_should_start = FALSE;
66 static ibool buf_load_should_start = FALSE;
67
68 static ibool buf_load_abort_flag = FALSE;
69
70 /* Used to temporary store dump info in order to avoid IO while holding
71 buffer pool mutex during dump and also to sort the contents of the dump
72 before reading the pages from disk during load.
73 We store the space id in the high 32 bits and page no in low 32 bits. */
74 typedef ib_uint64_t buf_dump_t;
75
76 /* Aux macros to create buf_dump_t and to extract space and page from it */
77 #define BUF_DUMP_CREATE(space, page) ut_ull_create(space, page)
78 #define BUF_DUMP_SPACE(a) ((ulint) ((a) >> 32))
79 #define BUF_DUMP_PAGE(a) ((ulint) ((a) & 0xFFFFFFFFUL))
80
81 /*****************************************************************//**
82 Wakes up the buffer pool dump/load thread and instructs it to start
83 a dump. This function is called by MySQL code via buffer_pool_dump_now()
84 and it should return immediately because the whole MySQL is frozen during
85 its execution. */
86 void
buf_dump_start()87 buf_dump_start()
88 /*============*/
89 {
90 buf_dump_should_start = TRUE;
91 os_event_set(srv_buf_dump_event);
92 }
93
94 /*****************************************************************//**
95 Wakes up the buffer pool dump/load thread and instructs it to start
96 a load. This function is called by MySQL code via buffer_pool_load_now()
97 and it should return immediately because the whole MySQL is frozen during
98 its execution. */
99 void
buf_load_start()100 buf_load_start()
101 /*============*/
102 {
103 buf_load_should_start = TRUE;
104 os_event_set(srv_buf_dump_event);
105 }
106
107 /*****************************************************************//**
108 Sets the global variable that feeds MySQL's innodb_buffer_pool_dump_status
109 to the specified string. The format and the following parameters are the
110 same as the ones used for printf(3). The value of this variable can be
111 retrieved by:
112 SELECT variable_value FROM information_schema.global_status WHERE
113 variable_name = 'INNODB_BUFFER_POOL_DUMP_STATUS';
114 or by:
115 SHOW STATUS LIKE 'innodb_buffer_pool_dump_status'; */
116 static MY_ATTRIBUTE((nonnull, format(printf, 2, 3)))
117 void
buf_dump_status(enum status_severity severity,const char * fmt,...)118 buf_dump_status(
119 /*============*/
120 enum status_severity severity,/*!< in: status severity */
121 const char* fmt, /*!< in: format */
122 ...) /*!< in: extra parameters according
123 to fmt */
124 {
125 va_list ap;
126
127 va_start(ap, fmt);
128
129 ut_vsnprintf(
130 export_vars.innodb_buffer_pool_dump_status,
131 sizeof(export_vars.innodb_buffer_pool_dump_status),
132 fmt, ap);
133
134 switch (severity) {
135 case STATUS_INFO:
136 ib::info() << export_vars.innodb_buffer_pool_dump_status;
137 break;
138
139 case STATUS_ERR:
140 ib::error() << export_vars.innodb_buffer_pool_dump_status;
141 break;
142
143 case STATUS_VERBOSE:
144 break;
145 }
146
147 va_end(ap);
148 }
149
150 /*****************************************************************//**
151 Sets the global variable that feeds MySQL's innodb_buffer_pool_load_status
152 to the specified string. The format and the following parameters are the
153 same as the ones used for printf(3). The value of this variable can be
154 retrieved by:
155 SELECT variable_value FROM information_schema.global_status WHERE
156 variable_name = 'INNODB_BUFFER_POOL_LOAD_STATUS';
157 or by:
158 SHOW STATUS LIKE 'innodb_buffer_pool_load_status'; */
159 static MY_ATTRIBUTE((nonnull, format(printf, 2, 3)))
160 void
buf_load_status(enum status_severity severity,const char * fmt,...)161 buf_load_status(
162 /*============*/
163 enum status_severity severity,/*!< in: status severity */
164 const char* fmt, /*!< in: format */
165 ...) /*!< in: extra parameters according to fmt */
166 {
167 va_list ap;
168
169 va_start(ap, fmt);
170
171 ut_vsnprintf(
172 export_vars.innodb_buffer_pool_load_status,
173 sizeof(export_vars.innodb_buffer_pool_load_status),
174 fmt, ap);
175
176 switch (severity) {
177 case STATUS_INFO:
178 ib::info() << export_vars.innodb_buffer_pool_load_status;
179 break;
180
181 case STATUS_ERR:
182 ib::error() << export_vars.innodb_buffer_pool_load_status;
183 break;
184
185 case STATUS_VERBOSE:
186 break;
187 }
188
189 va_end(ap);
190 }
191
192 /** Returns the directory path where the buffer pool dump file will be created.
193 @return directory path */
194 static
195 const char*
get_buf_dump_dir()196 get_buf_dump_dir()
197 {
198 const char* dump_dir;
199
200 /* The dump file should be created in the default data directory if
201 innodb_data_home_dir is set as an empty string. */
202 if (strcmp(srv_data_home, "") == 0) {
203 dump_dir = fil_path_to_mysql_datadir;
204 } else {
205 dump_dir = srv_data_home;
206 }
207
208 return(dump_dir);
209 }
210
211 /** Generate the path to the buffer pool dump/load file.
212 @param[out] path generated path
213 @param[in] path_size size of 'path', used as in snprintf(3). */
214 static
215 void
buf_dump_generate_path(char * path,size_t path_size)216 buf_dump_generate_path(
217 char* path,
218 size_t path_size)
219 {
220 char buf[FN_REFLEN];
221
222 ut_snprintf(buf, sizeof(buf), "%s%c%s", get_buf_dump_dir(),
223 OS_PATH_SEPARATOR, srv_buf_dump_filename);
224
225 os_file_type_t type;
226 bool exists = false;
227 bool ret;
228
229 ret = os_file_status(buf, &exists, &type);
230
231 /* For realpath() to succeed the file must exist. */
232
233 if (ret && exists) {
234 /* my_realpath() assumes the destination buffer is big enough
235 to hold FN_REFLEN bytes. */
236 ut_a(path_size >= FN_REFLEN);
237
238 my_realpath(path, buf, 0);
239 } else {
240 /* If it does not exist, then resolve only srv_data_home
241 and append srv_buf_dump_filename to it. */
242 char srv_data_home_full[FN_REFLEN];
243
244 my_realpath(srv_data_home_full, get_buf_dump_dir(), 0);
245
246 if (srv_data_home_full[strlen(srv_data_home_full) - 1]
247 == OS_PATH_SEPARATOR) {
248
249 ut_snprintf(path, path_size, "%s%s",
250 srv_data_home_full,
251 srv_buf_dump_filename);
252 } else {
253 ut_snprintf(path, path_size, "%s%c%s",
254 srv_data_home_full,
255 OS_PATH_SEPARATOR,
256 srv_buf_dump_filename);
257 }
258 }
259 }
260
261 /*****************************************************************//**
262 Perform a buffer pool dump into the file specified by
263 innodb_buffer_pool_filename. If any errors occur then the value of
264 innodb_buffer_pool_dump_status will be set accordingly, see buf_dump_status().
265 The dump filename can be specified by (relative to srv_data_home):
266 SET GLOBAL innodb_buffer_pool_filename='filename'; */
267 static
268 void
buf_dump(ibool obey_shutdown)269 buf_dump(
270 /*=====*/
271 ibool obey_shutdown) /*!< in: quit if we are in a shutting down
272 state */
273 {
274 #define SHOULD_QUIT() (SHUTTING_DOWN() && obey_shutdown)
275
276 char full_filename[OS_FILE_MAX_PATH];
277 char tmp_filename[OS_FILE_MAX_PATH + 11];
278 char now[32];
279 FILE* f;
280 ulint i;
281 int ret;
282
283 buf_dump_generate_path(full_filename, sizeof(full_filename));
284
285 ut_snprintf(tmp_filename, sizeof(tmp_filename),
286 "%s.incomplete", full_filename);
287
288 buf_dump_status(STATUS_INFO, "Dumping buffer pool(s) to %s",
289 full_filename);
290
291 f = fopen(tmp_filename, "w");
292 if (f == NULL) {
293 buf_dump_status(STATUS_ERR,
294 "Cannot open '%s' for writing: %s",
295 tmp_filename, strerror(errno));
296 return;
297 }
298 /* else */
299
300 /* walk through each buffer pool */
301 for (i = 0; i < srv_buf_pool_instances && !SHOULD_QUIT(); i++) {
302 buf_pool_t* buf_pool;
303 const buf_page_t* bpage;
304 buf_dump_t* dump;
305 ulint n_pages;
306 ulint j;
307
308 buf_pool = buf_pool_from_array(i);
309
310 /* obtain buf_pool mutex before allocate, since
311 UT_LIST_GET_LEN(buf_pool->LRU) could change */
312 buf_pool_mutex_enter(buf_pool);
313
314 n_pages = UT_LIST_GET_LEN(buf_pool->LRU);
315
316 /* skip empty buffer pools */
317 if (n_pages == 0) {
318 buf_pool_mutex_exit(buf_pool);
319 continue;
320 }
321
322 if (srv_buf_pool_dump_pct != 100) {
323 ut_ad(srv_buf_pool_dump_pct < 100);
324
325 n_pages = n_pages * srv_buf_pool_dump_pct / 100;
326
327 if (n_pages == 0) {
328 n_pages = 1;
329 }
330 }
331
332 dump = static_cast<buf_dump_t*>(ut_malloc_nokey(
333 n_pages * sizeof(*dump)));
334
335 if (dump == NULL) {
336 buf_pool_mutex_exit(buf_pool);
337 fclose(f);
338 buf_dump_status(STATUS_ERR,
339 "Cannot allocate " ULINTPF " bytes: %s",
340 (ulint) (n_pages * sizeof(*dump)),
341 strerror(errno));
342 /* leave tmp_filename to exist */
343 return;
344 }
345
346 for (bpage = UT_LIST_GET_FIRST(buf_pool->LRU), j = 0;
347 bpage != NULL && j < n_pages;
348 bpage = UT_LIST_GET_NEXT(LRU, bpage), j++) {
349
350 ut_a(buf_page_in_file(bpage));
351
352 dump[j] = BUF_DUMP_CREATE(bpage->id.space(),
353 bpage->id.page_no());
354 }
355
356 ut_a(j == n_pages);
357
358 buf_pool_mutex_exit(buf_pool);
359
360 for (j = 0; j < n_pages && !SHOULD_QUIT(); j++) {
361 ret = fprintf(f, ULINTPF "," ULINTPF "\n",
362 BUF_DUMP_SPACE(dump[j]),
363 BUF_DUMP_PAGE(dump[j]));
364 if (ret < 0) {
365 ut_free(dump);
366 fclose(f);
367 buf_dump_status(STATUS_ERR,
368 "Cannot write to '%s': %s",
369 tmp_filename, strerror(errno));
370 /* leave tmp_filename to exist */
371 return;
372 }
373
374 if (j % 128 == 0) {
375 buf_dump_status(
376 STATUS_VERBOSE,
377 "Dumping buffer pool"
378 " " ULINTPF "/" ULINTPF ","
379 " page " ULINTPF "/" ULINTPF,
380 i + 1, srv_buf_pool_instances,
381 j + 1, n_pages);
382 }
383 }
384
385 ut_free(dump);
386 }
387
388 ret = fclose(f);
389 if (ret != 0) {
390 buf_dump_status(STATUS_ERR,
391 "Cannot close '%s': %s",
392 tmp_filename, strerror(errno));
393 return;
394 }
395 /* else */
396
397 ret = unlink(full_filename);
398 if (ret != 0 && errno != ENOENT) {
399 buf_dump_status(STATUS_ERR,
400 "Cannot delete '%s': %s",
401 full_filename, strerror(errno));
402 /* leave tmp_filename to exist */
403 return;
404 }
405 /* else */
406
407 ret = rename(tmp_filename, full_filename);
408 if (ret != 0) {
409 buf_dump_status(STATUS_ERR,
410 "Cannot rename '%s' to '%s': %s",
411 tmp_filename, full_filename,
412 strerror(errno));
413 /* leave tmp_filename to exist */
414 return;
415 }
416 /* else */
417
418 /* success */
419
420 ut_sprintf_timestamp(now);
421
422 buf_dump_status(STATUS_INFO,
423 "Buffer pool(s) dump completed at %s", now);
424 }
425
426 /** Artificially delay the buffer pool loading if necessary. The idea of this
427 function is to prevent hogging the server with IO and slowing down too much
428 normal client queries.
429 @param[in,out] last_check_time milliseconds since epoch of the last
430 time we did check if throttling is
431 needed, we do the check every
432 srv_io_capacity IO ops.
433 @param[in] last_activity_count activity count
434 @param[in] n_io number of IO ops done since buffer
435 pool load has started */
436 UNIV_INLINE
437 void
buf_load_throttle_if_needed(ib_time_monotonic_ms_t * last_check_time,ulint * last_activity_count,ulint n_io)438 buf_load_throttle_if_needed(
439 /*========================*/
440 ib_time_monotonic_ms_t* last_check_time,
441 ulint* last_activity_count,
442 ulint n_io)
443 {
444 if (n_io % srv_io_capacity < srv_io_capacity - 1) {
445 return;
446 }
447
448 if (*last_check_time == 0 || *last_activity_count == 0) {
449 *last_check_time = ut_time_monotonic_ms();
450 *last_activity_count = srv_get_activity_count();
451 return;
452 }
453
454 /* srv_io_capacity IO operations have been performed by buffer pool
455 load since the last time we were here. */
456
457 /* If no other activity, then keep going without any delay. */
458 if (srv_get_activity_count() == *last_activity_count) {
459 return;
460 }
461
462 /* There has been other activity, throttle. */
463
464 ib_time_monotonic_ms_t now = ut_time_monotonic_ms();
465 ulint elapsed_time = now - *last_check_time;
466
467 /* Notice that elapsed_time is not the time for the last
468 srv_io_capacity IO operations performed by BP load. It is the
469 time elapsed since the last time we detected that there has been
470 other activity. This has a small and acceptable deficiency, e.g.:
471 1. BP load runs and there is no other activity.
472 2. Other activity occurs, we run N IO operations after that and
473 enter here (where 0 <= N < srv_io_capacity).
474 3. last_check_time is very old and we do not sleep at this time, but
475 only update last_check_time and last_activity_count.
476 4. We run srv_io_capacity more IO operations and call this function
477 again.
478 5. There has been more other activity and thus we enter here.
479 6. Now last_check_time is recent and we sleep if necessary to prevent
480 more than srv_io_capacity IO operations per second.
481 The deficiency is that we could have slept at 3., but for this we
482 would have to update last_check_time before the
483 "cur_activity_count == *last_activity_count" check and calling
484 ut_time_monotonic_ms() that often may turn out to be too expensive. */
485
486 if (elapsed_time < 1000 /* 1 sec (1000 milli secs) */) {
487 os_thread_sleep((1000 - elapsed_time) * 1000 /* micro secs */);
488 }
489
490 *last_check_time = ut_time_monotonic_ms();
491 *last_activity_count = srv_get_activity_count();
492 }
493
494 /*****************************************************************//**
495 Perform a buffer pool load from the file specified by
496 innodb_buffer_pool_filename. If any errors occur then the value of
497 innodb_buffer_pool_load_status will be set accordingly, see buf_load_status().
498 The dump filename can be specified by (relative to srv_data_home):
499 SET GLOBAL innodb_buffer_pool_filename='filename'; */
500 static
501 void
buf_load()502 buf_load()
503 /*======*/
504 {
505 char full_filename[OS_FILE_MAX_PATH];
506 char now[32];
507 FILE* f;
508 buf_dump_t* dump;
509 ulint dump_n;
510 ulint total_buffer_pools_pages;
511 ulint i;
512 ulint space_id;
513 ulint page_no;
514 int fscanf_ret;
515
516 /* Ignore any leftovers from before */
517 buf_load_abort_flag = FALSE;
518
519 buf_dump_generate_path(full_filename, sizeof(full_filename));
520
521 buf_load_status(STATUS_INFO,
522 "Loading buffer pool(s) from %s", full_filename);
523
524 f = fopen(full_filename, "r");
525 if (f == NULL) {
526 buf_load_status(STATUS_ERR,
527 "Cannot open '%s' for reading: %s",
528 full_filename, strerror(errno));
529 return;
530 }
531 /* else */
532
533 /* First scan the file to estimate how many entries are in it.
534 This file is tiny (approx 500KB per 1GB buffer pool), reading it
535 two times is fine. */
536 dump_n = 0;
537 while (fscanf(f, ULINTPF "," ULINTPF, &space_id, &page_no) == 2
538 && !SHUTTING_DOWN()) {
539 dump_n++;
540 }
541
542 if (!SHUTTING_DOWN() && !feof(f)) {
543 /* fscanf() returned != 2 */
544 const char* what;
545 if (ferror(f)) {
546 what = "reading";
547 } else {
548 what = "parsing";
549 }
550 fclose(f);
551 buf_load_status(STATUS_ERR, "Error %s '%s',"
552 " unable to load buffer pool (stage 1)",
553 what, full_filename);
554 return;
555 }
556
557 /* If dump is larger than the buffer pool(s), then we ignore the
558 extra trailing. This could happen if a dump is made, then buffer
559 pool is shrunk and then load is attempted. */
560 total_buffer_pools_pages = buf_pool_get_n_pages()
561 * srv_buf_pool_instances;
562 if (dump_n > total_buffer_pools_pages) {
563 dump_n = total_buffer_pools_pages;
564 }
565
566 if(dump_n != 0) {
567 dump = static_cast<buf_dump_t*>(ut_malloc_nokey(
568 dump_n * sizeof(*dump)));
569 } else {
570 fclose(f);
571 ut_sprintf_timestamp(now);
572 buf_load_status(STATUS_INFO,
573 "Buffer pool(s) load completed at %s"
574 " (%s was empty)", now, full_filename);
575 return;
576 }
577
578 if (dump == NULL) {
579 fclose(f);
580 buf_load_status(STATUS_ERR,
581 "Cannot allocate " ULINTPF " bytes: %s",
582 (ulint) (dump_n * sizeof(*dump)),
583 strerror(errno));
584 return;
585 }
586
587 rewind(f);
588
589 for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {
590 fscanf_ret = fscanf(f, ULINTPF "," ULINTPF,
591 &space_id, &page_no);
592
593 if (fscanf_ret != 2) {
594 if (feof(f)) {
595 break;
596 }
597 /* else */
598
599 ut_free(dump);
600 fclose(f);
601 buf_load_status(STATUS_ERR,
602 "Error parsing '%s', unable"
603 " to load buffer pool (stage 2)",
604 full_filename);
605 return;
606 }
607
608 if (space_id > ULINT32_MASK || page_no > ULINT32_MASK) {
609 ut_free(dump);
610 fclose(f);
611 buf_load_status(STATUS_ERR,
612 "Error parsing '%s': bogus"
613 " space,page " ULINTPF "," ULINTPF
614 " at line " ULINTPF ","
615 " unable to load buffer pool",
616 full_filename,
617 space_id, page_no,
618 i);
619 return;
620 }
621
622 dump[i] = BUF_DUMP_CREATE(space_id, page_no);
623 }
624
625 /* Set dump_n to the actual number of initialized elements,
626 i could be smaller than dump_n here if the file got truncated after
627 we read it the first time. */
628 dump_n = i;
629
630 fclose(f);
631
632 if (dump_n == 0) {
633 ut_free(dump);
634 ut_sprintf_timestamp(now);
635 buf_load_status(STATUS_INFO,
636 "Buffer pool(s) load completed at %s"
637 " (%s was empty)", now, full_filename);
638 return;
639 }
640
641 if (!SHUTTING_DOWN()) {
642 std::sort(dump, dump + dump_n);
643 }
644
645 ib_time_monotonic_ms_t last_check_time = 0;
646 ulint last_activity_cnt = 0;
647
648 /* Avoid calling the expensive fil_space_acquire_silent() for each
649 page within the same tablespace. dump[] is sorted by (space, page),
650 so all pages from a given tablespace are consecutive. */
651 ulint cur_space_id = BUF_DUMP_SPACE(dump[0]);
652 fil_space_t* space = fil_space_acquire_silent(cur_space_id);
653 page_size_t page_size(space ? space->flags : 0);
654
655 #ifdef HAVE_PSI_STAGE_INTERFACE
656 PSI_stage_progress* pfs_stage_progress
657 = mysql_set_stage(srv_stage_buffer_pool_load.m_key);
658 #endif /* HAVE_PSI_STAGE_INTERFACE */
659
660 mysql_stage_set_work_estimated(pfs_stage_progress, dump_n);
661 mysql_stage_set_work_completed(pfs_stage_progress, 0);
662
663 for (i = 0; i < dump_n && !SHUTTING_DOWN(); i++) {
664
665 /* space_id for this iteration of the loop */
666 const ulint this_space_id = BUF_DUMP_SPACE(dump[i]);
667
668 if (this_space_id != cur_space_id) {
669 if (space != NULL) {
670 fil_space_release(space);
671 }
672
673 cur_space_id = this_space_id;
674 space = fil_space_acquire_silent(cur_space_id);
675
676 if (space != NULL) {
677 const page_size_t cur_page_size(
678 space->flags);
679 page_size.copy_from(cur_page_size);
680 }
681 }
682
683 if (space == NULL) {
684 continue;
685 }
686
687 buf_read_page_background(
688 page_id_t(this_space_id, BUF_DUMP_PAGE(dump[i])),
689 page_size, true);
690
691 if (i % 64 == 63) {
692 os_aio_simulated_wake_handler_threads();
693 }
694
695 /* Update the progress every 32 MiB, which is every Nth page,
696 where N = 32*1024^2 / page_size. */
697 static const ulint update_status_every_n_mb = 32;
698 static const ulint update_status_every_n_pages
699 = update_status_every_n_mb * 1024 * 1024
700 / page_size.physical();
701
702 if (i % update_status_every_n_pages == 0) {
703 buf_load_status(STATUS_VERBOSE,
704 "Loaded " ULINTPF "/" ULINTPF " pages",
705 i + 1, dump_n);
706 mysql_stage_set_work_completed(pfs_stage_progress, i);
707 }
708
709 if (buf_load_abort_flag) {
710 if (space != NULL) {
711 fil_space_release(space);
712 }
713 buf_load_abort_flag = FALSE;
714 ut_free(dump);
715 buf_load_status(
716 STATUS_INFO,
717 "Buffer pool(s) load aborted on request");
718 /* Premature end, set estimated = completed = i and
719 end the current stage event. */
720 mysql_stage_set_work_estimated(pfs_stage_progress, i);
721 mysql_stage_set_work_completed(pfs_stage_progress, i);
722 #ifdef HAVE_PSI_STAGE_INTERFACE
723 mysql_end_stage();
724 #endif /* HAVE_PSI_STAGE_INTERFACE */
725 return;
726 }
727
728 buf_load_throttle_if_needed(
729 &last_check_time, &last_activity_cnt, i);
730 }
731
732 if (space != NULL) {
733 fil_space_release(space);
734 }
735
736 ut_free(dump);
737
738 ut_sprintf_timestamp(now);
739
740 buf_load_status(STATUS_INFO,
741 "Buffer pool(s) load completed at %s", now);
742
743 /* Make sure that estimated = completed when we end. */
744 mysql_stage_set_work_completed(pfs_stage_progress, dump_n);
745 /* End the stage progress event. */
746 #ifdef HAVE_PSI_STAGE_INTERFACE
747 mysql_end_stage();
748 #endif /* HAVE_PSI_STAGE_INTERFACE */
749 }
750
751 /*****************************************************************//**
752 Aborts a currently running buffer pool load. This function is called by
753 MySQL code via buffer_pool_load_abort() and it should return immediately
754 because the whole MySQL is frozen during its execution. */
755 void
buf_load_abort()756 buf_load_abort()
757 /*============*/
758 {
759 buf_load_abort_flag = TRUE;
760 }
761
762 /*****************************************************************//**
763 This is the main thread for buffer pool dump/load. It waits for an
764 event and when waked up either performs a dump or load and sleeps
765 again.
766 @return this function does not return, it calls os_thread_exit() */
767 extern "C"
768 os_thread_ret_t
DECLARE_THREAD(buf_dump_thread)769 DECLARE_THREAD(buf_dump_thread)(
770 /*============================*/
771 void* arg MY_ATTRIBUTE((unused))) /*!< in: a dummy parameter
772 required by os_thread_create */
773 {
774 my_thread_init();
775 ut_ad(!srv_read_only_mode);
776
777 #ifdef UNIV_PFS_THREAD
778 pfs_register_thread(buf_dump_thread_key);
779 #endif /* UNIV_PFS_THREAD */
780
781 srv_buf_dump_thread_active = TRUE;
782
783 buf_dump_status(STATUS_VERBOSE, "Dumping of buffer pool not started");
784 buf_load_status(STATUS_VERBOSE, "Loading of buffer pool not started");
785
786 if (srv_buffer_pool_load_at_startup) {
787 buf_load();
788 }
789
790 while (!SHUTTING_DOWN()) {
791
792 os_event_wait(srv_buf_dump_event);
793
794 if (buf_dump_should_start) {
795 buf_dump_should_start = FALSE;
796 buf_dump(TRUE /* quit on shutdown */);
797 }
798
799 if (buf_load_should_start) {
800 buf_load_should_start = FALSE;
801 buf_load();
802 }
803
804 os_event_reset(srv_buf_dump_event);
805 }
806
807 if (srv_buffer_pool_dump_at_shutdown && srv_fast_shutdown != 2) {
808 buf_dump(FALSE /* ignore shutdown down flag,
809 keep going even if we are in a shutdown state */);
810 }
811
812 srv_buf_dump_thread_active = FALSE;
813
814 my_thread_end();
815 /* We count the number of threads in os_thread_exit(). A created
816 thread should always use that to exit and not use return() to exit. */
817 os_thread_exit();
818
819 OS_THREAD_DUMMY_RETURN;
820 }
821