diff options
Diffstat (limited to 'storage/innobase/srv')
-rw-r--r-- | storage/innobase/srv/srv0mon.cc | 2108 | ||||
-rw-r--r-- | storage/innobase/srv/srv0srv.cc | 2135 | ||||
-rw-r--r-- | storage/innobase/srv/srv0start.cc | 2168 |
3 files changed, 6411 insertions, 0 deletions
diff --git a/storage/innobase/srv/srv0mon.cc b/storage/innobase/srv/srv0mon.cc new file mode 100644 index 00000000..f13af13c --- /dev/null +++ b/storage/innobase/srv/srv0mon.cc @@ -0,0 +1,2108 @@ +/***************************************************************************** + +Copyright (c) 2010, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2012, Facebook Inc. +Copyright (c) 2013, 2021, MariaDB Corporation. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file srv/srv0mon.cc +Database monitor counter interfaces + +Created 12/9/2009 Jimmy Yang +*******************************************************/ + +#include "buf0buf.h" +#include "dict0mem.h" +#include "ibuf0ibuf.h" +#include "lock0lock.h" +#include "mach0data.h" +#include "os0file.h" +#include "srv0mon.h" +#include "srv0srv.h" +#include "trx0rseg.h" +#include "trx0sys.h" + +/* Macro to standardize the counter names for counters in the +"monitor_buf_page" module as they have very structured defines */ +#define MONITOR_BUF_PAGE(name, description, code, op, op_code) \ + {"buffer_page_" op "_" name, "buffer_page_io", \ + "Number of " description " Pages " op, \ + MONITOR_GROUP_MODULE, MONITOR_DEFAULT_START, \ + MONITOR_##code##_##op_code} + +#define MONITOR_BUF_PAGE_READ(name, description, code) \ + MONITOR_BUF_PAGE(name, description, code, "read", PAGE_READ) + +#define MONITOR_BUF_PAGE_WRITTEN(name, description, code) \ + MONITOR_BUF_PAGE(name, description, code, "written", PAGE_WRITTEN) + +/** This array defines basic static information of monitor counters, +including each monitor's name, module it belongs to, a short +description and its property/type and corresponding monitor_id. +Please note: If you add a monitor here, please add its corresponding +monitor_id to "enum monitor_id_value" structure in srv0mon.h file. */ + +static monitor_info_t innodb_counter_info[] = +{ + /* A dummy item to mark the module start, this is + to accomodate the default value (0) set for the + global variables with the control system. */ + {"module_start", "module_start", "module_start", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_DEFAULT_START}, + + /* ========== Counters for Server Metadata ========== */ + {"module_metadata", "metadata", "Server Metadata", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_METADATA}, + + {"metadata_table_handles_opened", "metadata", + "Number of table handles opened", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLE_OPEN}, + + {"metadata_table_handles_closed", "metadata", + "Number of table handles closed", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLE_CLOSE}, + + {"metadata_table_reference_count", "metadata", + "Table reference counter", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLE_REFERENCE}, + + /* ========== Counters for Lock Module ========== */ + {"module_lock", "lock", "Lock Module", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_LOCK}, + + {"lock_deadlocks", "lock", "Number of deadlocks", + MONITOR_DEFAULT_ON, + MONITOR_DEFAULT_START, MONITOR_DEADLOCK}, + + {"lock_timeouts", "lock", "Number of lock timeouts", + MONITOR_DEFAULT_ON, + MONITOR_DEFAULT_START, MONITOR_TIMEOUT}, + + {"lock_rec_lock_waits", "lock", + "Number of times enqueued into record lock wait queue", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_LOCKREC_WAIT}, + + {"lock_table_lock_waits", "lock", + "Number of times enqueued into table lock wait queue", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLELOCK_WAIT}, + + {"lock_rec_lock_requests", "lock", + "Number of record locks requested", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_RECLOCK_REQ}, + + {"lock_rec_lock_created", "lock", "Number of record locks created", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_RECLOCK_CREATED}, + + {"lock_rec_lock_removed", "lock", + "Number of record locks removed from the lock queue", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_RECLOCK_REMOVED}, + + {"lock_rec_locks", "lock", + "Current number of record locks on tables", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_RECLOCK}, + + {"lock_table_lock_created", "lock", "Number of table locks created", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLELOCK_CREATED}, + + {"lock_table_lock_removed", "lock", + "Number of table locks removed from the lock queue", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TABLELOCK_REMOVED}, + + {"lock_table_locks", "lock", + "Current number of table locks on tables", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_TABLELOCK}, + + {"lock_row_lock_current_waits", "lock", + "Number of row locks currently being waited for" + " (innodb_row_lock_current_waits)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_ROW_LOCK_CURRENT_WAIT}, + + {"lock_row_lock_time", "lock", + "Time spent in acquiring row locks, in milliseconds" + " (innodb_row_lock_time)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOCK_WAIT_TIME}, + + {"lock_row_lock_time_max", "lock", + "The maximum time to acquire a row lock, in milliseconds" + " (innodb_row_lock_time_max)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOCK_MAX_WAIT_TIME}, + + {"lock_row_lock_waits", "lock", + "Number of times a row lock had to be waited for" + " (innodb_row_lock_waits)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_ROW_LOCK_WAIT}, + + {"lock_row_lock_time_avg", "lock", + "The average time to acquire a row lock, in milliseconds" + " (innodb_row_lock_time_avg)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOCK_AVG_WAIT_TIME}, + + /* ========== Counters for Buffer Manager and I/O ========== */ + {"module_buffer", "buffer", "Buffer Manager Module", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_BUFFER}, + + {"buffer_pool_size", "server", + "Server buffer pool size (all buffer pools) in bytes", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUFFER_POOL_SIZE}, + + {"buffer_pool_reads", "buffer", + "Number of reads directly from disk (innodb_buffer_pool_reads)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_READS}, + + {"buffer_pool_read_requests", "buffer", + "Number of logical read requests (innodb_buffer_pool_read_requests)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_READ_REQUESTS}, + + {"buffer_pool_write_requests", "buffer", + "Number of write requests (innodb_buffer_pool_write_requests)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_WRITE_REQUEST}, + + {"buffer_pool_wait_free", "buffer", + "Number of times waited for free buffer" + " (innodb_buffer_pool_wait_free)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_WAIT_FREE}, + + {"buffer_pool_read_ahead", "buffer", + "Number of pages read as read ahead (innodb_buffer_pool_read_ahead)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_READ_AHEAD}, + + {"buffer_pool_read_ahead_evicted", "buffer", + "Read-ahead pages evicted without being accessed" + " (innodb_buffer_pool_read_ahead_evicted)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_READ_AHEAD_EVICTED}, + + {"buffer_pool_pages_total", "buffer", + "Total buffer pool size in pages (innodb_buffer_pool_pages_total)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_PAGE_TOTAL}, + + {"buffer_pool_pages_misc", "buffer", + "Buffer pages for misc use such as row locks or the adaptive" + " hash index (innodb_buffer_pool_pages_misc)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_PAGE_MISC}, + + {"buffer_pool_pages_data", "buffer", + "Buffer pages containing data (innodb_buffer_pool_pages_data)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_PAGES_DATA}, + + {"buffer_pool_bytes_data", "buffer", + "Buffer bytes containing data (innodb_buffer_pool_bytes_data)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_BYTES_DATA}, + + {"buffer_pool_pages_dirty", "buffer", + "Buffer pages currently dirty (innodb_buffer_pool_pages_dirty)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_PAGES_DIRTY}, + + {"buffer_pool_bytes_dirty", "buffer", + "Buffer bytes currently dirty (innodb_buffer_pool_bytes_dirty)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_BYTES_DIRTY}, + + {"buffer_pool_pages_free", "buffer", + "Buffer pages currently free (innodb_buffer_pool_pages_free)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_POOL_PAGES_FREE}, + + {"buffer_pages_created", "buffer", + "Number of pages created (innodb_pages_created)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGE_CREATED}, + + {"buffer_pages_written", "buffer", + "Number of pages written (innodb_pages_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_WRITTEN}, + + {"buffer_index_pages_written", "buffer", + "Number of index pages written (innodb_index_pages_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_INDEX_PAGES_WRITTEN}, + + {"buffer_non_index_pages_written", "buffer", + "Number of non index pages written (innodb_non_index_pages_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_NON_INDEX_PAGES_WRITTEN}, + + {"buffer_pages_read", "buffer", + "Number of pages read (innodb_pages_read)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_READ}, + + {"buffer_index_sec_rec_cluster_reads", "buffer", + "Number of secondary record reads triggered cluster read", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_INDEX_SEC_REC_CLUSTER_READS}, + + {"buffer_index_sec_rec_cluster_reads_avoided", "buffer", + "Number of secondary record reads avoided triggering cluster read", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_INDEX_SEC_REC_CLUSTER_READS_AVOIDED}, + + {"buffer_data_reads", "buffer", + "Amount of data read in bytes (innodb_data_reads)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BYTE_READ}, + + {"buffer_data_written", "buffer", + "Amount of data written in bytes (innodb_data_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_BYTE_WRITTEN}, + + /* Cumulative counter for scanning in flush batches */ + {"buffer_flush_batch_scanned", "buffer", + "Total pages scanned as part of flush batch", + MONITOR_SET_OWNER, + MONITOR_FLUSH_BATCH_SCANNED_NUM_CALL, + MONITOR_FLUSH_BATCH_SCANNED}, + + {"buffer_flush_batch_num_scan", "buffer", + "Number of times buffer flush list flush is called", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BATCH_SCANNED, + MONITOR_FLUSH_BATCH_SCANNED_NUM_CALL}, + + {"buffer_flush_batch_scanned_per_call", "buffer", + "Pages scanned per flush batch scan", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BATCH_SCANNED, + MONITOR_FLUSH_BATCH_SCANNED_PER_CALL}, + + /* Cumulative counter for pages flushed in flush batches */ + {"buffer_flush_batch_total_pages", "buffer", + "Total pages flushed as part of flush batch", + MONITOR_SET_OWNER, MONITOR_FLUSH_BATCH_COUNT, + MONITOR_FLUSH_BATCH_TOTAL_PAGE}, + + {"buffer_flush_batches", "buffer", + "Number of flush batches", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BATCH_TOTAL_PAGE, + MONITOR_FLUSH_BATCH_COUNT}, + + {"buffer_flush_batch_pages", "buffer", + "Pages queued as a flush batch", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BATCH_TOTAL_PAGE, + MONITOR_FLUSH_BATCH_PAGES}, + + /* Cumulative counter for flush batches because of neighbor */ + {"buffer_flush_neighbor_total_pages", "buffer", + "Total neighbors flushed as part of neighbor flush", + MONITOR_SET_OWNER, MONITOR_FLUSH_NEIGHBOR_COUNT, + MONITOR_FLUSH_NEIGHBOR_TOTAL_PAGE}, + + {"buffer_flush_neighbor", "buffer", + "Number of times neighbors flushing is invoked", + MONITOR_SET_MEMBER, MONITOR_FLUSH_NEIGHBOR_TOTAL_PAGE, + MONITOR_FLUSH_NEIGHBOR_COUNT}, + + {"buffer_flush_neighbor_pages", "buffer", + "Pages queued as a neighbor batch", + MONITOR_SET_MEMBER, MONITOR_FLUSH_NEIGHBOR_TOTAL_PAGE, + MONITOR_FLUSH_NEIGHBOR_PAGES}, + + {"buffer_flush_n_to_flush_requested", "buffer", + "Number of pages requested for flushing.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_N_TO_FLUSH_REQUESTED}, + + {"buffer_flush_n_to_flush_by_age", "buffer", + "Number of pages target by LSN Age for flushing.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_N_TO_FLUSH_BY_AGE}, + + {"buffer_flush_adaptive_avg_time", "buffer", + "Avg time (ms) spent for adaptive flushing recently.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_ADAPTIVE_AVG_TIME}, + + {"buffer_flush_adaptive_avg_pass", "buffer", + "Number of adaptive flushes passed during the recent Avg period.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_ADAPTIVE_AVG_PASS}, + + {"buffer_LRU_get_free_loops", "buffer", + "Total loops in LRU get free.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_LRU_GET_FREE_LOOPS}, + + {"buffer_LRU_get_free_waits", "buffer", + "Total sleep waits in LRU get free.", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_LRU_GET_FREE_WAITS}, + + {"buffer_flush_avg_page_rate", "buffer", + "Average number of pages at which flushing is happening", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_AVG_PAGE_RATE}, + + {"buffer_flush_lsn_avg_rate", "buffer", + "Average redo generation rate", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_LSN_AVG_RATE}, + + {"buffer_flush_pct_for_dirty", "buffer", + "Percent of IO capacity used to avoid max dirty page limit", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_PCT_FOR_DIRTY}, + + {"buffer_flush_pct_for_lsn", "buffer", + "Percent of IO capacity used to avoid reusable redo space limit", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_PCT_FOR_LSN}, + + {"buffer_flush_sync_waits", "buffer", + "Number of times a wait happens due to sync flushing", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_FLUSH_SYNC_WAITS}, + + /* Cumulative counter for flush batches for adaptive flushing */ + {"buffer_flush_adaptive_total_pages", "buffer", + "Total pages flushed as part of adaptive flushing", + MONITOR_SET_OWNER, MONITOR_FLUSH_ADAPTIVE_COUNT, + MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE}, + + {"buffer_flush_adaptive", "buffer", + "Number of adaptive batches", + MONITOR_SET_MEMBER, MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE, + MONITOR_FLUSH_ADAPTIVE_COUNT}, + + {"buffer_flush_adaptive_pages", "buffer", + "Pages queued as an adaptive batch", + MONITOR_SET_MEMBER, MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE, + MONITOR_FLUSH_ADAPTIVE_PAGES}, + + /* Cumulative counter for flush batches because of sync */ + {"buffer_flush_sync_total_pages", "buffer", + "Total pages flushed as part of sync batches", + MONITOR_SET_OWNER, MONITOR_FLUSH_SYNC_COUNT, + MONITOR_FLUSH_SYNC_TOTAL_PAGE}, + + {"buffer_flush_sync", "buffer", + "Number of sync batches", + MONITOR_SET_MEMBER, MONITOR_FLUSH_SYNC_TOTAL_PAGE, + MONITOR_FLUSH_SYNC_COUNT}, + + {"buffer_flush_sync_pages", "buffer", + "Pages queued as a sync batch", + MONITOR_SET_MEMBER, MONITOR_FLUSH_SYNC_TOTAL_PAGE, + MONITOR_FLUSH_SYNC_PAGES}, + + /* Cumulative counter for flush batches because of background */ + {"buffer_flush_background_total_pages", "buffer", + "Total pages flushed as part of background batches", + MONITOR_SET_OWNER, MONITOR_FLUSH_BACKGROUND_COUNT, + MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE}, + + {"buffer_flush_background", "buffer", + "Number of background batches", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE, + MONITOR_FLUSH_BACKGROUND_COUNT}, + + {"buffer_flush_background_pages", "buffer", + "Pages queued as a background batch", + MONITOR_SET_MEMBER, MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE, + MONITOR_FLUSH_BACKGROUND_PAGES}, + + /* Cumulative counter for LRU batch scan */ + {"buffer_LRU_batch_scanned", "buffer", + "Total pages scanned as part of LRU batch", + MONITOR_SET_OWNER, MONITOR_LRU_BATCH_SCANNED_NUM_CALL, + MONITOR_LRU_BATCH_SCANNED}, + + {"buffer_LRU_batch_num_scan", "buffer", + "Number of times LRU batch is called", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_SCANNED, + MONITOR_LRU_BATCH_SCANNED_NUM_CALL}, + + {"buffer_LRU_batch_scanned_per_call", "buffer", + "Pages scanned per LRU batch call", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_SCANNED, + MONITOR_LRU_BATCH_SCANNED_PER_CALL}, + + /* Cumulative counter for LRU batch pages flushed */ + {"buffer_LRU_batch_flush_total_pages", "buffer", + "Total pages flushed as part of LRU batches", + MONITOR_SET_OWNER, MONITOR_LRU_BATCH_FLUSH_COUNT, + MONITOR_LRU_BATCH_FLUSH_TOTAL_PAGE}, + + {"buffer_LRU_batches_flush", "buffer", + "Number of LRU batches", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_FLUSH_TOTAL_PAGE, + MONITOR_LRU_BATCH_FLUSH_COUNT}, + + {"buffer_LRU_batch_flush_pages", "buffer", + "Pages queued as an LRU batch", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_FLUSH_TOTAL_PAGE, + MONITOR_LRU_BATCH_FLUSH_PAGES}, + + /* Cumulative counter for LRU batch pages flushed */ + {"buffer_LRU_batch_evict_total_pages", "buffer", + "Total pages evicted as part of LRU batches", + MONITOR_SET_OWNER, MONITOR_LRU_BATCH_EVICT_COUNT, + MONITOR_LRU_BATCH_EVICT_TOTAL_PAGE}, + + {"buffer_LRU_batches_evict", "buffer", + "Number of LRU batches", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_EVICT_TOTAL_PAGE, + MONITOR_LRU_BATCH_EVICT_COUNT}, + + {"buffer_LRU_batch_evict_pages", "buffer", + "Pages queued as an LRU batch", + MONITOR_SET_MEMBER, MONITOR_LRU_BATCH_EVICT_TOTAL_PAGE, + MONITOR_LRU_BATCH_EVICT_PAGES}, + + {"buffer_LRU_single_flush_failure_count", "Buffer", + "Number of times attempt to flush a single page from LRU failed", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_LRU_SINGLE_FLUSH_FAILURE_COUNT}, + + {"buffer_LRU_get_free_search", "Buffer", + "Number of searches performed for a clean page", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_LRU_GET_FREE_SEARCH}, + + /* Cumulative counter for LRU search scans */ + {"buffer_LRU_search_scanned", "buffer", + "Total pages scanned as part of LRU search", + MONITOR_SET_OWNER, + MONITOR_LRU_SEARCH_SCANNED_NUM_CALL, + MONITOR_LRU_SEARCH_SCANNED}, + + {"buffer_LRU_search_num_scan", "buffer", + "Number of times LRU search is performed", + MONITOR_SET_MEMBER, MONITOR_LRU_SEARCH_SCANNED, + MONITOR_LRU_SEARCH_SCANNED_NUM_CALL}, + + {"buffer_LRU_search_scanned_per_call", "buffer", + "Page scanned per single LRU search", + MONITOR_SET_MEMBER, MONITOR_LRU_SEARCH_SCANNED, + MONITOR_LRU_SEARCH_SCANNED_PER_CALL}, + + /* Cumulative counter for LRU unzip search scans */ + {"buffer_LRU_unzip_search_scanned", "buffer", + "Total pages scanned as part of LRU unzip search", + MONITOR_SET_OWNER, + MONITOR_LRU_UNZIP_SEARCH_SCANNED_NUM_CALL, + MONITOR_LRU_UNZIP_SEARCH_SCANNED}, + + {"buffer_LRU_unzip_search_num_scan", "buffer", + "Number of times LRU unzip search is performed", + MONITOR_SET_MEMBER, MONITOR_LRU_UNZIP_SEARCH_SCANNED, + MONITOR_LRU_UNZIP_SEARCH_SCANNED_NUM_CALL}, + + {"buffer_LRU_unzip_search_scanned_per_call", "buffer", + "Page scanned per single LRU unzip search", + MONITOR_SET_MEMBER, MONITOR_LRU_UNZIP_SEARCH_SCANNED, + MONITOR_LRU_UNZIP_SEARCH_SCANNED_PER_CALL}, + + /* ========== Counters for Buffer Page I/O ========== */ + {"module_buffer_page", "buffer_page_io", "Buffer Page I/O Module", + static_cast<monitor_type_t>( + MONITOR_MODULE | MONITOR_GROUP_MODULE), + MONITOR_DEFAULT_START, MONITOR_MODULE_BUF_PAGE}, + + MONITOR_BUF_PAGE_READ("index_leaf","Index Leaf", INDEX_LEAF), + + MONITOR_BUF_PAGE_READ("index_non_leaf","Index Non-leaf", + INDEX_NON_LEAF), + + MONITOR_BUF_PAGE_READ("index_ibuf_leaf", "Insert Buffer Index Leaf", + INDEX_IBUF_LEAF), + + MONITOR_BUF_PAGE_READ("index_ibuf_non_leaf", + "Insert Buffer Index Non-Leaf", + INDEX_IBUF_NON_LEAF), + + MONITOR_BUF_PAGE_READ("undo_log", "Undo Log", UNDO_LOG), + + MONITOR_BUF_PAGE_READ("index_inode", "Index Inode", INODE), + + MONITOR_BUF_PAGE_READ("ibuf_free_list", "Insert Buffer Free List", + IBUF_FREELIST), + + MONITOR_BUF_PAGE_READ("ibuf_bitmap", "Insert Buffer Bitmap", + IBUF_BITMAP), + + MONITOR_BUF_PAGE_READ("system_page", "System", SYSTEM), + + MONITOR_BUF_PAGE_READ("trx_system", "Transaction System", TRX_SYSTEM), + + MONITOR_BUF_PAGE_READ("fsp_hdr", "File Space Header", FSP_HDR), + + MONITOR_BUF_PAGE_READ("xdes", "Extent Descriptor", XDES), + + MONITOR_BUF_PAGE_READ("blob", "Uncompressed BLOB", BLOB), + + MONITOR_BUF_PAGE_READ("zblob", "First Compressed BLOB", ZBLOB), + + MONITOR_BUF_PAGE_READ("zblob2", "Subsequent Compressed BLOB", ZBLOB2), + + MONITOR_BUF_PAGE_READ("other", "other/unknown (old version of InnoDB)", + OTHER), + + MONITOR_BUF_PAGE_WRITTEN("index_leaf","Index Leaf", INDEX_LEAF), + + MONITOR_BUF_PAGE_WRITTEN("index_non_leaf","Index Non-leaf", + INDEX_NON_LEAF), + + MONITOR_BUF_PAGE_WRITTEN("index_ibuf_leaf", "Insert Buffer Index Leaf", + INDEX_IBUF_LEAF), + + MONITOR_BUF_PAGE_WRITTEN("index_ibuf_non_leaf", + "Insert Buffer Index Non-Leaf", + INDEX_IBUF_NON_LEAF), + + MONITOR_BUF_PAGE_WRITTEN("undo_log", "Undo Log", UNDO_LOG), + + MONITOR_BUF_PAGE_WRITTEN("index_inode", "Index Inode", INODE), + + MONITOR_BUF_PAGE_WRITTEN("ibuf_free_list", "Insert Buffer Free List", + IBUF_FREELIST), + + MONITOR_BUF_PAGE_WRITTEN("ibuf_bitmap", "Insert Buffer Bitmap", + IBUF_BITMAP), + + MONITOR_BUF_PAGE_WRITTEN("system_page", "System", SYSTEM), + + MONITOR_BUF_PAGE_WRITTEN("trx_system", "Transaction System", + TRX_SYSTEM), + + MONITOR_BUF_PAGE_WRITTEN("fsp_hdr", "File Space Header", FSP_HDR), + + MONITOR_BUF_PAGE_WRITTEN("xdes", "Extent Descriptor", XDES), + + MONITOR_BUF_PAGE_WRITTEN("blob", "Uncompressed BLOB", BLOB), + + MONITOR_BUF_PAGE_WRITTEN("zblob", "First Compressed BLOB", ZBLOB), + + MONITOR_BUF_PAGE_WRITTEN("zblob2", "Subsequent Compressed BLOB", + ZBLOB2), + + MONITOR_BUF_PAGE_WRITTEN("other", "other/unknown (old version InnoDB)", + OTHER), + + /* ========== Counters for OS level operations ========== */ + {"module_os", "os", "OS Level Operation", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_OS}, + + {"os_data_reads", "os", + "Number of reads initiated (innodb_data_reads)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_FILE_READ}, + + {"os_data_writes", "os", + "Number of writes initiated (innodb_data_writes)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_FILE_WRITE}, + + {"os_data_fsyncs", "os", + "Number of fsync() calls (innodb_data_fsyncs)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_FSYNC}, + + {"os_pending_reads", "os", "Number of reads pending", + MONITOR_DEFAULT_ON, + MONITOR_DEFAULT_START, MONITOR_OS_PENDING_READS}, + + {"os_pending_writes", "os", "Number of writes pending", + MONITOR_DEFAULT_ON, + MONITOR_DEFAULT_START, MONITOR_OS_PENDING_WRITES}, + + {"os_log_bytes_written", "os", + "Bytes of log written (innodb_os_log_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_LOG_WRITTEN}, + + {"os_log_fsyncs", "os", + "Number of fsync log writes (innodb_os_log_fsyncs)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_LOG_FSYNC}, + + {"os_log_pending_fsyncs", "os", + "Number of pending fsync write (innodb_os_log_pending_fsyncs)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_LOG_PENDING_FSYNC}, + + {"os_log_pending_writes", "os", + "Number of pending log file writes (innodb_os_log_pending_writes)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_OS_LOG_PENDING_WRITES}, + + /* ========== Counters for Transaction Module ========== */ + {"module_trx", "transaction", "Transaction Manager", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_TRX}, + + {"trx_rw_commits", "transaction", + "Number of read-write transactions committed", + MONITOR_NONE, MONITOR_DEFAULT_START, MONITOR_TRX_RW_COMMIT}, + + {"trx_ro_commits", "transaction", + "Number of read-only transactions committed", + MONITOR_NONE, MONITOR_DEFAULT_START, MONITOR_TRX_RO_COMMIT}, + + {"trx_nl_ro_commits", "transaction", + "Number of non-locking auto-commit read-only transactions committed", + MONITOR_NONE, MONITOR_DEFAULT_START, MONITOR_TRX_NL_RO_COMMIT}, + + {"trx_commits_insert_update", "transaction", + "Number of transactions committed with inserts and updates", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TRX_COMMIT_UNDO}, + + {"trx_rollbacks", "transaction", + "Number of transactions rolled back", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TRX_ROLLBACK}, + + {"trx_rollbacks_savepoint", "transaction", + "Number of transactions rolled back to savepoint", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TRX_ROLLBACK_SAVEPOINT}, + + {"trx_active_transactions", "transaction", + "Number of active transactions", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_TRX_ACTIVE}, + + {"trx_rseg_history_len", "transaction", + "Length of the TRX_RSEG_HISTORY list", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_RSEG_HISTORY_LEN}, + + {"trx_undo_slots_used", "transaction", "Number of undo slots used", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_UNDO_SLOT_USED}, + + {"trx_undo_slots_cached", "transaction", + "Number of undo slots cached", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_UNDO_SLOT_CACHED}, + + {"trx_rseg_current_size", "transaction", + "Current rollback segment size in pages", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_RSEG_CUR_SIZE}, + + /* ========== Counters for Purge Module ========== */ + {"module_purge", "purge", "Purge Module", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_PURGE}, + + {"purge_del_mark_records", "purge", + "Number of delete-marked rows purged", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_N_DEL_ROW_PURGE}, + + {"purge_upd_exist_or_extern_records", "purge", + "Number of purges on updates of existing records and" + " updates on delete marked record with externally stored field", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_N_UPD_EXIST_EXTERN}, + + {"purge_invoked", "purge", + "Number of times purge was invoked", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PURGE_INVOKED}, + + {"purge_undo_log_pages", "purge", + "Number of undo log pages handled by the purge", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PURGE_N_PAGE_HANDLED}, + + {"purge_dml_delay_usec", "purge", + "Microseconds DML to be delayed due to purge lagging", + MONITOR_DISPLAY_CURRENT, + MONITOR_DEFAULT_START, MONITOR_DML_PURGE_DELAY}, + + {"purge_stop_count", "purge", + "Number of times purge was stopped", + MONITOR_DISPLAY_CURRENT, + MONITOR_DEFAULT_START, MONITOR_PURGE_STOP_COUNT}, + + {"purge_resume_count", "purge", + "Number of times purge was resumed", + MONITOR_DISPLAY_CURRENT, + MONITOR_DEFAULT_START, MONITOR_PURGE_RESUME_COUNT}, + + /* ========== Counters for Recovery Module ========== */ + {"module_log", "recovery", "Recovery Module", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_RECOVERY}, + + {"log_checkpoints", "recovery", "Number of checkpoints", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_NUM_CHECKPOINT}, + + {"log_lsn_last_flush", "recovery", "LSN of Last flush", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_LSN_FLUSHDISK}, + + {"log_lsn_last_checkpoint", "recovery", "LSN at last checkpoint", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_LSN_CHECKPOINT}, + + {"log_lsn_current", "recovery", "Current LSN value", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_LSN_CURRENT}, + + {"log_lsn_checkpoint_age", "recovery", + "Current LSN value minus LSN at last checkpoint", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_LSN_CHECKPOINT_AGE}, + + {"log_lsn_buf_pool_oldest", "recovery", + "The oldest modified block LSN in the buffer pool", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_BUF_OLDEST_LSN}, + + {"log_max_modified_age_async", "recovery", + "Maximum LSN difference; when exceeded, start asynchronous preflush", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_MAX_AGE_ASYNC}, + + {"log_pending_log_flushes", "recovery", "Pending log flushes", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_PENDING_LOG_FLUSH}, + + {"log_pending_checkpoint_writes", "recovery", "Pending checkpoints", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_PENDING_CHECKPOINT_WRITE}, + + {"log_num_log_io", "recovery", "Number of log I/Os", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_LOG_IO}, + + {"log_waits", "recovery", + "Number of log waits due to small log buffer (innodb_log_waits)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOG_WAITS}, + + {"log_write_requests", "recovery", + "Number of log write requests (innodb_log_write_requests)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOG_WRITE_REQUEST}, + + {"log_writes", "recovery", + "Number of log writes (innodb_log_writes)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOG_WRITES}, + + {"log_padded", "recovery", + "Bytes of log padded for log write ahead", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_LOG_PADDED}, + + /* ========== Counters for Page Compression ========== */ + {"module_compress", "compression", "Page Compression Info", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_PAGE}, + + {"compress_pages_compressed", "compression", + "Number of pages compressed", MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PAGE_COMPRESS}, + + {"compress_pages_decompressed", "compression", + "Number of pages decompressed", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PAGE_DECOMPRESS}, + + {"compression_pad_increments", "compression", + "Number of times padding is incremented to avoid compression failures", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PAD_INCREMENTS}, + + {"compression_pad_decrements", "compression", + "Number of times padding is decremented due to good compressibility", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PAD_DECREMENTS}, + + {"compress_saved", "compression", + "Number of bytes saved by page compression", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGE_COMPRESS_SAVED}, + + {"compress_pages_page_compressed", "compression", + "Number of pages compressed by page compression", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_PAGE_COMPRESSED}, + + {"compress_page_compressed_trim_op", "compression", + "Number of TRIM operation performed by page compression", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGE_COMPRESSED_TRIM_OP}, + + {"compress_pages_page_decompressed", "compression", + "Number of pages decompressed by page compression", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_PAGE_DECOMPRESSED}, + + {"compress_pages_page_compression_error", "compression", + "Number of page compression errors", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_PAGE_COMPRESSION_ERROR}, + + {"compress_pages_encrypted", "compression", + "Number of pages encrypted", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_ENCRYPTED}, + + {"compress_pages_decrypted", "compression", + "Number of pages decrypted", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_OVLD_PAGES_DECRYPTED}, + + /* ========== Counters for Index ========== */ + {"module_index", "index", "Index Manager", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_INDEX}, + + {"index_page_splits", "index", "Number of index page splits", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_SPLIT}, + + {"index_page_merge_attempts", "index", + "Number of index page merge attempts", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_MERGE_ATTEMPTS}, + + {"index_page_merge_successful", "index", + "Number of successful index page merges", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_MERGE_SUCCESSFUL}, + + {"index_page_reorg_attempts", "index", + "Number of index page reorganization attempts", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_REORG_ATTEMPTS}, + + {"index_page_reorg_successful", "index", + "Number of successful index page reorganizations", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_REORG_SUCCESSFUL}, + + {"index_page_discards", "index", "Number of index pages discarded", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_INDEX_DISCARD}, + +#ifdef BTR_CUR_HASH_ADAPT + /* ========== Counters for Adaptive Hash Index ========== */ + {"module_adaptive_hash", "adaptive_hash_index", "Adaptive Hash Index", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_ADAPTIVE_HASH}, + + {"adaptive_hash_searches", "adaptive_hash_index", + "Number of successful searches using Adaptive Hash Index", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_ADAPTIVE_HASH_SEARCH}, +#endif /* BTR_CUR_HASH_ADAPT */ + + {"adaptive_hash_searches_btree", "adaptive_hash_index", + "Number of searches using B-tree on an index search", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_ADAPTIVE_HASH_SEARCH_BTREE}, + +#ifdef BTR_CUR_HASH_ADAPT + {"adaptive_hash_pages_added", "adaptive_hash_index", + "Number of index pages on which the Adaptive Hash Index is built", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_PAGE_ADDED}, + + {"adaptive_hash_pages_removed", "adaptive_hash_index", + "Number of index pages whose corresponding Adaptive Hash Index" + " entries were removed", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_PAGE_REMOVED}, + + {"adaptive_hash_rows_added", "adaptive_hash_index", + "Number of Adaptive Hash Index rows added", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_ROW_ADDED}, + + {"adaptive_hash_rows_removed", "adaptive_hash_index", + "Number of Adaptive Hash Index rows removed", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_ROW_REMOVED}, + + {"adaptive_hash_rows_deleted_no_hash_entry", "adaptive_hash_index", + "Number of rows deleted that did not have corresponding Adaptive Hash" + " Index entries", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_ROW_REMOVE_NOT_FOUND}, + + {"adaptive_hash_rows_updated", "adaptive_hash_index", + "Number of Adaptive Hash Index rows updated", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ADAPTIVE_HASH_ROW_UPDATED}, +#endif /* BTR_CUR_HASH_ADAPT */ + + /* ========== Counters for tablespace ========== */ + {"module_file", "file_system", "Tablespace and File System Manager", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_FIL_SYSTEM}, + + {"file_num_open_files", "file_system", + "Number of files currently open (innodb_num_open_files)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DISPLAY_CURRENT | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_N_FILE_OPENED}, + + /* ========== Counters for Change Buffer ========== */ + {"module_ibuf_system", "change_buffer", "InnoDB Change Buffer", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_IBUF_SYSTEM}, + + {"ibuf_merges_insert", "change_buffer", + "Number of inserted records merged by change buffering", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_INSERT}, + + {"ibuf_merges_delete_mark", "change_buffer", + "Number of deleted records merged by change buffering", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_DELETE}, + + {"ibuf_merges_delete", "change_buffer", + "Number of purge records merged by change buffering", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_PURGE}, + + {"ibuf_merges_discard_insert", "change_buffer", + "Number of insert merged operations discarded", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_DISCARD_INSERT}, + + {"ibuf_merges_discard_delete_mark", "change_buffer", + "Number of deleted merged operations discarded", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_DISCARD_DELETE}, + + {"ibuf_merges_discard_delete", "change_buffer", + "Number of purge merged operations discarded", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGE_DISCARD_PURGE}, + + {"ibuf_merges", "change_buffer", "Number of change buffer merges", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_MERGES}, + + {"ibuf_size", "change_buffer", "Change buffer size in pages", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_IBUF_SIZE}, + + /* ========== Counters for server operations ========== */ + {"module_innodb", "innodb", + "Counter for general InnoDB server wide operations and properties", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_SERVER}, + + {"innodb_master_thread_sleeps", "server", + "Number of times (seconds) master thread sleeps", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_MASTER_THREAD_SLEEP}, + + {"innodb_activity_count", "server", "Current server activity count", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_SERVER_ACTIVITY}, + + {"innodb_master_active_loops", "server", + "Number of times master thread performs its tasks when" + " server is active", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_MASTER_ACTIVE_LOOPS}, + + {"innodb_master_idle_loops", "server", + "Number of times master thread performs its tasks when server is idle", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_MASTER_IDLE_LOOPS}, + + {"innodb_background_drop_table_usec", "server", + "Time (in microseconds) spent to process drop table list", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND}, + + {"innodb_log_flush_usec", "server", + "Time (in microseconds) spent to flush log records", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_SRV_LOG_FLUSH_MICROSECOND}, + + {"innodb_dict_lru_usec", "server", + "Time (in microseconds) spent to process DICT LRU list", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_SRV_DICT_LRU_MICROSECOND}, + + {"innodb_dict_lru_count_active", "server", + "Number of tables evicted from DICT LRU list in the active loop", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_SRV_DICT_LRU_EVICT_COUNT_ACTIVE}, + + {"innodb_dict_lru_count_idle", "server", + "Number of tables evicted from DICT LRU list in the idle loop", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_SRV_DICT_LRU_EVICT_COUNT_IDLE}, + + {"innodb_dblwr_writes", "server", + "Number of doublewrite operations that have been performed" + " (innodb_dblwr_writes)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_SRV_DBLWR_WRITES}, + + {"innodb_dblwr_pages_written", "server", + "Number of pages that have been written for doublewrite operations" + " (innodb_dblwr_pages_written)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_SRV_DBLWR_PAGES_WRITTEN}, + + {"innodb_page_size", "server", + "InnoDB page size in bytes (innodb_page_size)", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON | MONITOR_DISPLAY_CURRENT), + MONITOR_DEFAULT_START, MONITOR_OVLD_SRV_PAGE_SIZE}, + + {"innodb_rwlock_s_spin_waits", "server", + "Number of rwlock spin waits due to shared latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_S_SPIN_WAITS}, + + {"innodb_rwlock_x_spin_waits", "server", + "Number of rwlock spin waits due to exclusive latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_X_SPIN_WAITS}, + + {"innodb_rwlock_sx_spin_waits", "server", + "Number of rwlock spin waits due to sx latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_SX_SPIN_WAITS}, + + {"innodb_rwlock_s_spin_rounds", "server", + "Number of rwlock spin loop rounds due to shared latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_S_SPIN_ROUNDS}, + + {"innodb_rwlock_x_spin_rounds", "server", + "Number of rwlock spin loop rounds due to exclusive latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_X_SPIN_ROUNDS}, + + {"innodb_rwlock_sx_spin_rounds", "server", + "Number of rwlock spin loop rounds due to sx latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_SX_SPIN_ROUNDS}, + + {"innodb_rwlock_s_os_waits", "server", + "Number of OS waits due to shared latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_S_OS_WAITS}, + + {"innodb_rwlock_x_os_waits", "server", + "Number of OS waits due to exclusive latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_X_OS_WAITS}, + + {"innodb_rwlock_sx_os_waits", "server", + "Number of OS waits due to sx latch request", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OVLD_RWLOCK_SX_OS_WAITS}, + + /* ========== Counters for DML operations ========== */ + {"module_dml", "dml", "Statistics for DMLs", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_DML_STATS}, + + {"dml_reads", "dml", "Number of rows read", + static_cast<monitor_type_t>(MONITOR_EXISTING), + MONITOR_DEFAULT_START, MONITOR_OLVD_ROW_READ}, + + {"dml_inserts", "dml", "Number of rows inserted", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_ROW_INSERTED}, + + {"dml_deletes", "dml", "Number of rows deleted", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_ROW_DELETED}, + + {"dml_updates", "dml", "Number of rows updated", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_ROW_UPDTATED}, + + {"dml_system_reads", "dml", "Number of system rows read", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_SYSTEM_ROW_READ}, + + {"dml_system_inserts", "dml", "Number of system rows inserted", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_SYSTEM_ROW_INSERTED}, + + {"dml_system_deletes", "dml", "Number of system rows deleted", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_SYSTEM_ROW_DELETED}, + + {"dml_system_updates", "dml", "Number of system rows updated", + static_cast<monitor_type_t>( + MONITOR_EXISTING | MONITOR_DEFAULT_ON), + MONITOR_DEFAULT_START, MONITOR_OLVD_SYSTEM_ROW_UPDATED}, + + /* ========== Counters for DDL operations ========== */ + {"module_ddl", "ddl", "Statistics for DDLs", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_DDL_STATS}, + + {"ddl_background_drop_indexes", "ddl", + "Number of indexes waiting to be dropped after failed index creation", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_BACKGROUND_DROP_INDEX}, + + {"ddl_background_drop_tables", "ddl", + "Number of tables in background drop table list", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_BACKGROUND_DROP_TABLE}, + + {"ddl_online_create_index", "ddl", + "Number of indexes being created online", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ONLINE_CREATE_INDEX}, + + {"ddl_pending_alter_table", "ddl", + "Number of ALTER TABLE, CREATE INDEX, DROP INDEX in progress", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_PENDING_ALTER_TABLE}, + + {"ddl_sort_file_alter_table", "ddl", + "Number of sort files created during alter table", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ALTER_TABLE_SORT_FILES}, + + {"ddl_log_file_alter_table", "ddl", + "Number of log files created during alter table", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ALTER_TABLE_LOG_FILES}, + + /* ===== Counters for ICP (Index Condition Pushdown) Module ===== */ + {"module_icp", "icp", "Index Condition Pushdown", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_ICP}, + + {"icp_attempts", "icp", + "Number of attempts for index push-down condition checks", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ICP_ATTEMPTS}, + + {"icp_no_match", "icp", "Index push-down condition does not match", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ICP_NO_MATCH}, + + {"icp_out_of_range", "icp", "Index push-down condition out of range", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ICP_OUT_OF_RANGE}, + + {"icp_match", "icp", "Index push-down condition matches", + MONITOR_NONE, + MONITOR_DEFAULT_START, MONITOR_ICP_MATCH}, + + /* ========== Mutex monitoring on/off ========== */ + {"latch_status", "Latch counters", + "Collect latch counters to display via SHOW ENGING INNODB MUTEX", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_MODULE_LATCHES}, + + {"latch", "sync", "Latch monitoring control", + MONITOR_HIDDEN, + MONITOR_DEFAULT_START, MONITOR_LATCHES}, + + /* ========== To turn on/off reset all counters ========== */ + {"all", "All Counters", "Turn on/off and reset all counters", + MONITOR_MODULE, + MONITOR_DEFAULT_START, MONITOR_ALL_COUNTER} +}; + +/* The "innodb_counter_value" array stores actual counter values */ +monitor_value_t innodb_counter_value[NUM_MONITOR]; + +/* monitor_set_tbl is used to record and determine whether a monitor +has been turned on/off. */ +Atomic_relaxed<ulint> + monitor_set_tbl[(NUM_MONITOR + NUM_BITS_ULINT - 1) / NUM_BITS_ULINT]; + +/****************************************************************//** +Get a monitor's "monitor_info" by its monitor id (index into the +innodb_counter_info array. +@return Point to corresponding monitor_info_t, or NULL if no such +monitor */ +monitor_info_t* +srv_mon_get_info( +/*=============*/ + monitor_id_t monitor_id) /*!< id indexing into the + innodb_counter_info array */ +{ + ut_a(monitor_id < NUM_MONITOR); + + return((monitor_id < NUM_MONITOR) + ? &innodb_counter_info[monitor_id] + : NULL); +} + +/****************************************************************//** +Get monitor's name by its monitor id (indexing into the +innodb_counter_info array. +@return corresponding monitor name, or NULL if no such +monitor */ +const char* +srv_mon_get_name( +/*=============*/ + monitor_id_t monitor_id) /*!< id index into the + innodb_counter_info array */ +{ + ut_a(monitor_id < NUM_MONITOR); + + return((monitor_id < NUM_MONITOR) + ? innodb_counter_info[monitor_id].monitor_name + : NULL); +} + +/****************************************************************//** +Turn on/off, reset monitor counters in a module. If module_id +is MONITOR_ALL_COUNTER then turn on all monitor counters. +turned on because it has already been turned on. */ +void +srv_mon_set_module_control( +/*=======================*/ + monitor_id_t module_id, /*!< in: Module ID as in + monitor_counter_id. If it is + set to MONITOR_ALL_COUNTER, this means + we shall turn on all the counters */ + mon_option_t set_option) /*!< in: Turn on/off reset the + counter */ +{ + lint ix; + lint start_id; + ibool set_current_module = FALSE; + + ut_a(module_id <= NUM_MONITOR); + compile_time_assert(array_elements(innodb_counter_info) + == NUM_MONITOR); + + /* The module_id must be an ID of MONITOR_MODULE type */ + ut_a(innodb_counter_info[module_id].monitor_type & MONITOR_MODULE); + + /* start with the first monitor in the module. If module_id + is MONITOR_ALL_COUNTER, this means we need to turn on all + monitor counters. */ + if (module_id == MONITOR_ALL_COUNTER) { + start_id = 1; + } else if (innodb_counter_info[module_id].monitor_type + & MONITOR_GROUP_MODULE) { + /* Counters in this module are set as a group together + and cannot be turned on/off individually. Need to set + the on/off bit in the module counter */ + start_id = module_id; + set_current_module = TRUE; + + } else { + start_id = module_id + 1; + } + + for (ix = start_id; ix < NUM_MONITOR; ix++) { + /* if we hit the next module counter, we will + continue if we want to turn on all monitor counters, + and break if just turn on the counters in the + current module. */ + if (innodb_counter_info[ix].monitor_type & MONITOR_MODULE) { + + if (set_current_module) { + /* Continue to set on/off bit on current + module */ + set_current_module = FALSE; + } else if (module_id == MONITOR_ALL_COUNTER) { + if (!(innodb_counter_info[ix].monitor_type + & MONITOR_GROUP_MODULE)) { + continue; + } + } else { + /* Hitting the next module, stop */ + break; + } + } + + /* Cannot turn on a monitor already been turned on. User + should be aware some counters are already on before + turn them on again (which could reset counter value) */ + if (MONITOR_IS_ON(ix) && (set_option == MONITOR_TURN_ON)) { + ib::info() << "Monitor '" + << srv_mon_get_name((monitor_id_t) ix) + << "' is already enabled."; + continue; + } + + /* For some existing counters (server status variables), + we will get its counter value at the start/stop time + to calculate the actual value during the time. */ + if (innodb_counter_info[ix].monitor_type & MONITOR_EXISTING) { + srv_mon_process_existing_counter( + static_cast<monitor_id_t>(ix), set_option); + } + + /* Currently support 4 operations on the monitor counters: + turn on, turn off, reset and reset all operations. */ + switch (set_option) { + case MONITOR_TURN_ON: + MONITOR_ON(ix); + MONITOR_INIT(ix); + MONITOR_SET_START(ix); + break; + + case MONITOR_TURN_OFF: + MONITOR_OFF(ix); + MONITOR_SET_OFF(ix); + break; + + case MONITOR_RESET_VALUE: + srv_mon_reset(static_cast<monitor_id_t>(ix)); + break; + + case MONITOR_RESET_ALL_VALUE: + srv_mon_reset_all(static_cast<monitor_id_t>(ix)); + break; + + default: + ut_error; + } + } +} + +/****************************************************************//** +Get transaction system's rollback segment size in pages +@return size in pages */ +static +ulint +srv_mon_get_rseg_size(void) +/*=======================*/ +{ + ulint i; + ulint value = 0; + + /* rseg_array is a static array, so we can go through it without + mutex protection. In addition, we provide an estimate of the + total rollback segment size and to avoid mutex contention we + don't acquire the rseg->mutex" */ + for (i = 0; i < TRX_SYS_N_RSEGS; ++i) { + const trx_rseg_t* rseg = trx_sys.rseg_array[i]; + + if (rseg != NULL) { + value += rseg->curr_size; + } + } + + return(value); +} + +/****************************************************************//** +This function consolidates some existing server counters used +by "system status variables". These existing system variables do not have +mechanism to start/stop and reset the counters, so we simulate these +controls by remembering the corresponding counter values when the +corresponding monitors are turned on/off/reset, and do appropriate +mathematics to deduct the actual value. Please also refer to +srv_export_innodb_status() for related global counters used by +the existing status variables.*/ +void +srv_mon_process_existing_counter( +/*=============================*/ + monitor_id_t monitor_id, /*!< in: the monitor's ID as in + monitor_counter_id */ + mon_option_t set_option) /*!< in: Turn on/off reset the + counter */ +{ + mon_type_t value; + monitor_info_t* monitor_info; + ibool update_min = FALSE; + + monitor_info = srv_mon_get_info(monitor_id); + + ut_a(monitor_info->monitor_type & MONITOR_EXISTING); + ut_a(monitor_id < NUM_MONITOR); + + /* Get the value from corresponding global variable */ + switch (monitor_id) { + /* export_vars.innodb_buffer_pool_reads. Num Reads from + disk (page not in buffer) */ + case MONITOR_OVLD_BUF_POOL_READS: + value = srv_stats.buf_pool_reads; + break; + + /* innodb_buffer_pool_read_requests, the number of logical + read requests */ + case MONITOR_OVLD_BUF_POOL_READ_REQUESTS: + value = buf_pool.stat.n_page_gets; + break; + + /* innodb_buffer_pool_write_requests, the number of + write request */ + case MONITOR_OVLD_BUF_POOL_WRITE_REQUEST: + value = srv_stats.buf_pool_write_requests; + break; + + /* innodb_buffer_pool_wait_free */ + case MONITOR_OVLD_BUF_POOL_WAIT_FREE: + value = buf_pool.stat.LRU_waits; + break; + + /* innodb_buffer_pool_read_ahead */ + case MONITOR_OVLD_BUF_POOL_READ_AHEAD: + value = buf_pool.stat.n_ra_pages_read; + break; + + /* innodb_buffer_pool_read_ahead_evicted */ + case MONITOR_OVLD_BUF_POOL_READ_AHEAD_EVICTED: + value = buf_pool.stat.n_ra_pages_evicted; + break; + + /* innodb_buffer_pool_pages_total */ + case MONITOR_OVLD_BUF_POOL_PAGE_TOTAL: + value = buf_pool.get_n_pages(); + break; + + /* innodb_buffer_pool_pages_misc */ + case MONITOR_OVLD_BUF_POOL_PAGE_MISC: + value = buf_pool.get_n_pages() + - UT_LIST_GET_LEN(buf_pool.LRU) + - UT_LIST_GET_LEN(buf_pool.free); + break; + + /* innodb_buffer_pool_pages_data */ + case MONITOR_OVLD_BUF_POOL_PAGES_DATA: + value = UT_LIST_GET_LEN(buf_pool.LRU); + break; + + /* innodb_buffer_pool_bytes_data */ + case MONITOR_OVLD_BUF_POOL_BYTES_DATA: + value = buf_pool.stat.LRU_bytes + + (UT_LIST_GET_LEN(buf_pool.unzip_LRU) + << srv_page_size_shift); + break; + + /* innodb_buffer_pool_pages_dirty */ + case MONITOR_OVLD_BUF_POOL_PAGES_DIRTY: + value = UT_LIST_GET_LEN(buf_pool.flush_list); + break; + + /* innodb_buffer_pool_bytes_dirty */ + case MONITOR_OVLD_BUF_POOL_BYTES_DIRTY: + value = buf_pool.stat.flush_list_bytes; + break; + + /* innodb_buffer_pool_pages_free */ + case MONITOR_OVLD_BUF_POOL_PAGES_FREE: + value = UT_LIST_GET_LEN(buf_pool.free); + break; + + /* innodb_pages_created, the number of pages created */ + case MONITOR_OVLD_PAGE_CREATED: + value = buf_pool.stat.n_pages_created; + break; + + /* innodb_pages_written, the number of page written */ + case MONITOR_OVLD_PAGES_WRITTEN: + value = buf_pool.stat.n_pages_written; + break; + + /* innodb_index_pages_written, the number of index pages written */ + case MONITOR_OVLD_INDEX_PAGES_WRITTEN: + value = srv_stats.index_pages_written; + break; + + /* innodb_non_index_pages_written, the number of non index pages written */ + case MONITOR_OVLD_NON_INDEX_PAGES_WRITTEN: + value = srv_stats.non_index_pages_written; + break; + + /* innodb_pages_read */ + case MONITOR_OVLD_PAGES_READ: + value = buf_pool.stat.n_pages_read; + break; + + /* Number of times secondary index lookup triggered cluster lookup */ + case MONITOR_OVLD_INDEX_SEC_REC_CLUSTER_READS: + value = srv_stats.n_sec_rec_cluster_reads; + break; + /* Number of times prefix optimization avoided triggering cluster + lookup */ + case MONITOR_OVLD_INDEX_SEC_REC_CLUSTER_READS_AVOIDED: + value = srv_stats.n_sec_rec_cluster_reads_avoided; + break; + + /* innodb_data_reads, the total number of data reads */ + case MONITOR_OVLD_BYTE_READ: + value = srv_stats.data_read; + break; + + /* innodb_data_writes, the total number of data writes. */ + case MONITOR_OVLD_BYTE_WRITTEN: + value = srv_stats.data_written; + break; + + /* innodb_data_reads, the total number of data reads. */ + case MONITOR_OVLD_OS_FILE_READ: + value = os_n_file_reads; + break; + + /* innodb_data_writes, the total number of data writes*/ + case MONITOR_OVLD_OS_FILE_WRITE: + value = os_n_file_writes; + break; + + /* innodb_data_fsyncs, number of fsync() operations so far. */ + case MONITOR_OVLD_OS_FSYNC: + value = os_n_fsyncs; + break; + + /* innodb_os_log_written */ + case MONITOR_OVLD_OS_LOG_WRITTEN: + value = (mon_type_t) srv_stats.os_log_written; + break; + + /* innodb_os_log_fsyncs */ + case MONITOR_OVLD_OS_LOG_FSYNC: + value = log_sys.get_flushes(); + break; + + /* innodb_os_log_pending_fsyncs */ + case MONITOR_OVLD_OS_LOG_PENDING_FSYNC: + value = log_sys.get_pending_flushes(); + update_min = TRUE; + break; + + /* innodb_os_log_pending_writes */ + case MONITOR_OVLD_OS_LOG_PENDING_WRITES: + value = srv_stats.os_log_pending_writes; + update_min = TRUE; + break; + + /* innodb_log_waits */ + case MONITOR_OVLD_LOG_WAITS: + value = srv_stats.log_waits; + break; + + /* innodb_log_write_requests */ + case MONITOR_OVLD_LOG_WRITE_REQUEST: + value = srv_stats.log_write_requests; + break; + + /* innodb_log_writes */ + case MONITOR_OVLD_LOG_WRITES: + value = srv_stats.log_writes; + break; + + case MONITOR_OVLD_LOG_PADDED: + value = srv_stats.log_padded; + break; + + /* innodb_dblwr_writes */ + case MONITOR_OVLD_SRV_DBLWR_WRITES: + buf_dblwr.lock(); + value = buf_dblwr.batches(); + buf_dblwr.unlock(); + break; + + /* innodb_dblwr_pages_written */ + case MONITOR_OVLD_SRV_DBLWR_PAGES_WRITTEN: + buf_dblwr.lock(); + value = buf_dblwr.written(); + buf_dblwr.unlock(); + break; + + /* innodb_page_size */ + case MONITOR_OVLD_SRV_PAGE_SIZE: + value = srv_page_size; + break; + + case MONITOR_OVLD_RWLOCK_S_SPIN_WAITS: + value = rw_lock_stats.rw_s_spin_wait_count; + break; + + case MONITOR_OVLD_RWLOCK_X_SPIN_WAITS: + value = rw_lock_stats.rw_x_spin_wait_count; + break; + + case MONITOR_OVLD_RWLOCK_SX_SPIN_WAITS: + value = rw_lock_stats.rw_sx_spin_wait_count; + break; + + case MONITOR_OVLD_RWLOCK_S_SPIN_ROUNDS: + value = rw_lock_stats.rw_s_spin_round_count; + break; + + case MONITOR_OVLD_RWLOCK_X_SPIN_ROUNDS: + value = rw_lock_stats.rw_x_spin_round_count; + break; + + case MONITOR_OVLD_RWLOCK_SX_SPIN_ROUNDS: + value = rw_lock_stats.rw_sx_spin_round_count; + break; + + case MONITOR_OVLD_RWLOCK_S_OS_WAITS: + value = rw_lock_stats.rw_s_os_wait_count; + break; + + case MONITOR_OVLD_RWLOCK_X_OS_WAITS: + value = rw_lock_stats.rw_x_os_wait_count; + break; + + case MONITOR_OVLD_RWLOCK_SX_OS_WAITS: + value = rw_lock_stats.rw_sx_os_wait_count; + break; + + case MONITOR_OVLD_BUFFER_POOL_SIZE: + value = srv_buf_pool_size; + break; + + /* innodb_rows_read */ + case MONITOR_OLVD_ROW_READ: + value = srv_stats.n_rows_read; + break; + + /* innodb_rows_inserted */ + case MONITOR_OLVD_ROW_INSERTED: + value = srv_stats.n_rows_inserted; + break; + + /* innodb_rows_deleted */ + case MONITOR_OLVD_ROW_DELETED: + value = srv_stats.n_rows_deleted; + break; + + /* innodb_rows_updated */ + case MONITOR_OLVD_ROW_UPDTATED: + value = srv_stats.n_rows_updated; + break; + + /* innodb_system_rows_read */ + case MONITOR_OLVD_SYSTEM_ROW_READ: + value = srv_stats.n_system_rows_read; + break; + + /* innodb_system_rows_inserted */ + case MONITOR_OLVD_SYSTEM_ROW_INSERTED: + value = srv_stats.n_system_rows_inserted; + break; + + /* innodb_system_rows_deleted */ + case MONITOR_OLVD_SYSTEM_ROW_DELETED: + value = srv_stats.n_system_rows_deleted; + break; + + /* innodb_system_rows_updated */ + case MONITOR_OLVD_SYSTEM_ROW_UPDATED: + value = srv_stats.n_system_rows_updated; + break; + + /* innodb_row_lock_current_waits */ + case MONITOR_OVLD_ROW_LOCK_CURRENT_WAIT: + value = srv_stats.n_lock_wait_current_count; + break; + + /* innodb_row_lock_time */ + case MONITOR_OVLD_LOCK_WAIT_TIME: + value = srv_stats.n_lock_wait_time / 1000; + break; + + /* innodb_row_lock_time_max */ + case MONITOR_OVLD_LOCK_MAX_WAIT_TIME: + value = lock_sys.n_lock_max_wait_time / 1000; + break; + + /* innodb_row_lock_time_avg */ + case MONITOR_OVLD_LOCK_AVG_WAIT_TIME: + if (srv_stats.n_lock_wait_count > 0) { + value = srv_stats.n_lock_wait_time / 1000 + / srv_stats.n_lock_wait_count; + } else { + value = 0; + } + break; + + /* innodb_row_lock_waits */ + case MONITOR_OVLD_ROW_LOCK_WAIT: + value = srv_stats.n_lock_wait_count; + break; + + case MONITOR_RSEG_HISTORY_LEN: + value = trx_sys.rseg_history_len; + break; + + case MONITOR_RSEG_CUR_SIZE: + value = srv_mon_get_rseg_size(); + break; + + case MONITOR_OVLD_N_FILE_OPENED: + value = fil_system.n_open; + break; + + case MONITOR_OVLD_IBUF_MERGE_INSERT: + value = ibuf.n_merged_ops[IBUF_OP_INSERT]; + break; + + case MONITOR_OVLD_IBUF_MERGE_DELETE: + value = ibuf.n_merged_ops[IBUF_OP_DELETE_MARK]; + break; + + case MONITOR_OVLD_IBUF_MERGE_PURGE: + value = ibuf.n_merged_ops[IBUF_OP_DELETE]; + break; + + case MONITOR_OVLD_IBUF_MERGE_DISCARD_INSERT: + value = ibuf.n_discarded_ops[IBUF_OP_INSERT]; + break; + + case MONITOR_OVLD_IBUF_MERGE_DISCARD_DELETE: + value = ibuf.n_discarded_ops[IBUF_OP_DELETE_MARK]; + break; + + case MONITOR_OVLD_IBUF_MERGE_DISCARD_PURGE: + value = ibuf.n_discarded_ops[IBUF_OP_DELETE]; + break; + + case MONITOR_OVLD_IBUF_MERGES: + value = ibuf.n_merges; + break; + + case MONITOR_OVLD_IBUF_SIZE: + value = ibuf.size; + break; + + case MONITOR_OVLD_SERVER_ACTIVITY: + value = srv_get_activity_count(); + break; + + case MONITOR_OVLD_LSN_FLUSHDISK: + value = log_sys.get_flushed_lsn(); + break; + + case MONITOR_OVLD_LSN_CURRENT: + value = log_sys.get_lsn(); + break; + + case MONITOR_PENDING_LOG_FLUSH: + value = static_cast<mon_type_t>(log_sys.pending_flushes); + + break; + + case MONITOR_PENDING_CHECKPOINT_WRITE: + mysql_mutex_lock(&log_sys.mutex); + value = static_cast<mon_type_t>( + log_sys.n_pending_checkpoint_writes); + mysql_mutex_unlock(&log_sys.mutex); + break; + + case MONITOR_LOG_IO: + mysql_mutex_lock(&log_sys.mutex); + value = static_cast<mon_type_t>(log_sys.n_log_ios); + mysql_mutex_unlock(&log_sys.mutex); + break; + + case MONITOR_LSN_CHECKPOINT_AGE: + mysql_mutex_lock(&log_sys.mutex); + value = static_cast<mon_type_t>(log_sys.get_lsn() + - log_sys.last_checkpoint_lsn); + mysql_mutex_unlock(&log_sys.mutex); + break; + + case MONITOR_OVLD_BUF_OLDEST_LSN: + mysql_mutex_lock(&buf_pool.flush_list_mutex); + value = (mon_type_t) buf_pool.get_oldest_modification(0); + mysql_mutex_unlock(&buf_pool.flush_list_mutex); + break; + + case MONITOR_OVLD_LSN_CHECKPOINT: + value = (mon_type_t) log_sys.last_checkpoint_lsn; + break; + + case MONITOR_OVLD_MAX_AGE_ASYNC: + value = log_sys.max_modified_age_async; + break; + +#ifdef BTR_CUR_HASH_ADAPT + case MONITOR_OVLD_ADAPTIVE_HASH_SEARCH: + value = btr_cur_n_sea; + break; +#endif /* BTR_CUR_HASH_ADAPT */ + + case MONITOR_OVLD_ADAPTIVE_HASH_SEARCH_BTREE: + value = btr_cur_n_non_sea; + break; + + case MONITOR_OVLD_PAGE_COMPRESS_SAVED: + value = srv_stats.page_compression_saved; + break; + case MONITOR_OVLD_PAGES_PAGE_COMPRESSED: + value = srv_stats.pages_page_compressed; + break; + case MONITOR_OVLD_PAGE_COMPRESSED_TRIM_OP: + value = srv_stats.page_compressed_trim_op; + break; + case MONITOR_OVLD_PAGES_PAGE_DECOMPRESSED: + value = srv_stats.pages_page_decompressed; + break; + case MONITOR_OVLD_PAGES_PAGE_COMPRESSION_ERROR: + value = srv_stats.pages_page_compression_error; + break; + case MONITOR_OVLD_PAGES_ENCRYPTED: + value = srv_stats.pages_encrypted; + break; + case MONITOR_OVLD_PAGES_DECRYPTED: + value = srv_stats.pages_decrypted; + break; + + default: + ut_error; + } + + switch (set_option) { + case MONITOR_TURN_ON: + /* Save the initial counter value in mon_start_value + field */ + MONITOR_SAVE_START(monitor_id, value); + return; + + case MONITOR_TURN_OFF: + /* Save the counter value to mon_last_value when we + turn off the monitor but not yet reset. Note the + counter has not yet been set to off in the bitmap + table for normal turn off. We need to check the + count status (on/off) to avoid reset the value + for an already off conte */ + if (MONITOR_IS_ON(monitor_id)) { + srv_mon_process_existing_counter(monitor_id, + MONITOR_GET_VALUE); + MONITOR_SAVE_LAST(monitor_id); + } + return; + + case MONITOR_GET_VALUE: + if (MONITOR_IS_ON(monitor_id)) { + + /* If MONITOR_DISPLAY_CURRENT bit is on, we + only record the current value, rather than + incremental value over a period. Most of +` this type of counters are resource related + counters such as number of buffer pages etc. */ + if (monitor_info->monitor_type + & MONITOR_DISPLAY_CURRENT) { + MONITOR_SET(monitor_id, value); + } else { + /* Most status counters are montonically + increasing, no need to update their + minimum values. Only do so + if "update_min" set to TRUE */ + MONITOR_SET_DIFF(monitor_id, value); + + if (update_min + && (MONITOR_VALUE(monitor_id) + < MONITOR_MIN_VALUE(monitor_id))) { + MONITOR_MIN_VALUE(monitor_id) = + MONITOR_VALUE(monitor_id); + } + } + } + return; + + case MONITOR_RESET_VALUE: + if (!MONITOR_IS_ON(monitor_id)) { + MONITOR_LAST_VALUE(monitor_id) = 0; + } + return; + + /* Nothing special for reset all operation for these existing + counters */ + case MONITOR_RESET_ALL_VALUE: + return; + } +} + +/*************************************************************//** +Reset a monitor, create a new base line with the current monitor +value. This baseline is recorded by MONITOR_VALUE_RESET(monitor) */ +void +srv_mon_reset( +/*==========*/ + monitor_id_t monitor) /*!< in: monitor id */ +{ + ibool monitor_was_on; + + monitor_was_on = MONITOR_IS_ON(monitor); + + if (monitor_was_on) { + /* Temporarily turn off the counter for the resetting + operation */ + MONITOR_OFF(monitor); + } + + /* Before resetting the current monitor value, first + calculate and set the max/min value since monitor + start */ + srv_mon_calc_max_since_start(monitor); + srv_mon_calc_min_since_start(monitor); + + /* Monitors with MONITOR_DISPLAY_CURRENT bit + are not incremental, no need to remember + the reset value. */ + if (innodb_counter_info[monitor].monitor_type + & MONITOR_DISPLAY_CURRENT) { + MONITOR_VALUE_RESET(monitor) = 0; + } else { + /* Remember the new baseline */ + MONITOR_VALUE_RESET(monitor) = MONITOR_VALUE_RESET(monitor) + + MONITOR_VALUE(monitor); + } + + /* Reset the counter value */ + MONITOR_VALUE(monitor) = 0; + MONITOR_MAX_VALUE(monitor) = MAX_RESERVED; + MONITOR_MIN_VALUE(monitor) = MIN_RESERVED; + + MONITOR_FIELD((monitor), mon_reset_time) = time(NULL); + + if (monitor_was_on) { + MONITOR_ON(monitor); + } +} + +/*************************************************************//** +Turn on monitor counters that are marked as default ON. */ +void +srv_mon_default_on(void) +/*====================*/ +{ + ulint ix; + + for (ix = 0; ix < NUM_MONITOR; ix++) { + if (innodb_counter_info[ix].monitor_type + & MONITOR_DEFAULT_ON) { + /* Turn on monitor counters that are default on */ + MONITOR_ON(ix); + MONITOR_INIT(ix); + MONITOR_SET_START(ix); + } + } +} diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc new file mode 100644 index 00000000..ad221dc2 --- /dev/null +++ b/storage/innobase/srv/srv0srv.cc @@ -0,0 +1,2135 @@ +/***************************************************************************** + +Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2008, 2009 Google Inc. +Copyright (c) 2009, Percona Inc. +Copyright (c) 2013, 2021, MariaDB Corporation. + +Portions of this file contain modifications contributed and copyrighted by +Google, Inc. Those modifications are gratefully acknowledged and are described +briefly in the InnoDB documentation. The contributions by Google are +incorporated with their permission, and subject to the conditions contained in +the file COPYING.Google. + +Portions of this file contain modifications contributed and copyrighted +by Percona Inc.. Those modifications are +gratefully acknowledged and are described briefly in the InnoDB +documentation. The contributions by Percona Inc. are incorporated with +their permission, and subject to the conditions contained in the file +COPYING.Percona. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file srv/srv0srv.cc +The database server main program + +Created 10/8/1995 Heikki Tuuri +*******************************************************/ + +#include "my_global.h" +// JAN: TODO: MySQL 5.7 missing header +//#include "my_thread.h" +// +#include "mysql/psi/mysql_stage.h" +#include "mysql/psi/psi.h" + +#include "btr0sea.h" +#include "buf0flu.h" +#include "buf0lru.h" +#include "dict0boot.h" +#include "dict0load.h" +#include "ibuf0ibuf.h" +#include "lock0lock.h" +#include "log0recv.h" +#include "mem0mem.h" +#include "pars0pars.h" +#include "que0que.h" +#include "row0mysql.h" +#include "row0log.h" +#include "srv0mon.h" +#include "srv0srv.h" +#include "srv0start.h" +#include "sync0sync.h" +#include "trx0i_s.h" +#include "trx0purge.h" +#include "ut0crc32.h" +#include "btr0defragment.h" +#include "ut0mem.h" +#include "fil0fil.h" +#include "fil0crypt.h" +#include "fil0pagecompress.h" +#include "trx0types.h" +#include <list> + +#include <my_service_manager.h> +/* The following is the maximum allowed duration of a lock wait. */ +UNIV_INTERN ulong srv_fatal_semaphore_wait_threshold = DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT; + +/* How much data manipulation language (DML) statements need to be delayed, +in microseconds, in order to reduce the lagging of the purge thread. */ +ulint srv_dml_needed_delay; + +const char* srv_main_thread_op_info = ""; + +/** Prefix used by MySQL to indicate pre-5.1 table name encoding */ +const char srv_mysql50_table_name_prefix[10] = "#mysql50#"; + +/* Server parameters which are read from the initfile */ + +/* The following three are dir paths which are catenated before file +names, where the file name itself may also contain a path */ + +char* srv_data_home; + +/** Rollback files directory, can be absolute. */ +char* srv_undo_dir; + +/** The number of tablespaces to use for rollback segments. */ +ulong srv_undo_tablespaces; + +/** The number of UNDO tablespaces that are open and ready to use. */ +ulint srv_undo_tablespaces_open; + +/** The number of UNDO tablespaces that are active (hosting some rollback +segment). It is quite possible that some of the tablespaces doesn't host +any of the rollback-segment based on configuration used. */ +ulint srv_undo_tablespaces_active; + +/** Rate at which UNDO records should be purged. */ +ulong srv_purge_rseg_truncate_frequency; + +/** Enable or Disable Truncate of UNDO tablespace. +Note: If enabled then UNDO tablespace will be selected for truncate. +While Server waits for undo-tablespace to truncate if user disables +it, truncate action is completed but no new tablespace is marked +for truncate (action is never aborted). */ +my_bool srv_undo_log_truncate; + +/** Maximum size of undo tablespace. */ +unsigned long long srv_max_undo_log_size; + +/** Set if InnoDB must operate in read-only mode. We don't do any +recovery and open all tables in RO mode instead of RW mode. We don't +sync the max trx id to disk either. */ +my_bool srv_read_only_mode; +/** store to its own file each table created by an user; data +dictionary tables are in the system tablespace 0 */ +my_bool srv_file_per_table; +/** Set if InnoDB operates in read-only mode or innodb-force-recovery +is greater than SRV_FORCE_NO_TRX_UNDO. */ +my_bool high_level_read_only; + +/** Sort buffer size in index creation */ +ulong srv_sort_buf_size; +/** Maximum modification log file size for online index creation */ +unsigned long long srv_online_max_size; + +/* If this flag is TRUE, then we will use the native aio of the +OS (provided we compiled Innobase with it in), otherwise we will +use simulated aio we build below with threads. +Currently we support native aio on windows and linux */ +my_bool srv_use_native_aio; +my_bool srv_numa_interleave; +/** copy of innodb_use_atomic_writes; @see innodb_init_params() */ +my_bool srv_use_atomic_writes; +/** innodb_compression_algorithm; used with page compression */ +ulong innodb_compression_algorithm; + +#ifdef UNIV_DEBUG +/** Used by SET GLOBAL innodb_master_thread_disabled_debug = X. */ +my_bool srv_master_thread_disabled_debug; +/** Event used to inform that master thread is disabled. */ +static os_event_t srv_master_thread_disabled_event; +#endif /* UNIV_DEBUG */ + +/*------------------------- LOG FILES ------------------------ */ +char* srv_log_group_home_dir; + +/** The InnoDB redo log file size, or 0 when changing the redo log format +at startup (while disallowing writes to the redo log). */ +ulonglong srv_log_file_size; +/** innodb_log_buffer_size, in bytes */ +ulong srv_log_buffer_size; +/** innodb_flush_log_at_trx_commit */ +ulong srv_flush_log_at_trx_commit; +/** innodb_flush_log_at_timeout */ +uint srv_flush_log_at_timeout; +/** innodb_page_size */ +ulong srv_page_size; +/** log2 of innodb_page_size; @see innodb_init_params() */ +ulong srv_page_size_shift; +/** innodb_log_write_ahead_size */ +ulong srv_log_write_ahead_size; + +/** innodb_adaptive_flushing; try to flush dirty pages so as to avoid +IO bursts at the checkpoints. */ +my_bool srv_adaptive_flushing; + +/** innodb_flush_sync; whether to ignore io_capacity at log checkpoints */ +my_bool srv_flush_sync; + +/** common thread pool*/ +tpool::thread_pool* srv_thread_pool; + +/** Maximum number of times allowed to conditionally acquire +mutex before switching to blocking wait on the mutex */ +#define MAX_MUTEX_NOWAIT 2 + +/** Check whether the number of failed nonblocking mutex +acquisition attempts exceeds maximum allowed value. If so, +srv_printf_innodb_monitor() will request mutex acquisition +with mutex_enter(), which will wait until it gets the mutex. */ +#define MUTEX_NOWAIT(mutex_skipped) ((mutex_skipped) < MAX_MUTEX_NOWAIT) + +#ifdef WITH_INNODB_DISALLOW_WRITES +UNIV_INTERN os_event_t srv_allow_writes_event; +#endif /* WITH_INNODB_DISALLOW_WRITES */ + +/** copy of innodb_buffer_pool_size */ +ulint srv_buf_pool_size; +const ulint srv_buf_pool_min_size = 5 * 1024 * 1024; +/** Default pool size in bytes */ +const ulint srv_buf_pool_def_size = 128 * 1024 * 1024; +/** Requested buffer pool chunk size */ +ulong srv_buf_pool_chunk_unit; +/** innodb_lru_scan_depth; number of blocks scanned in LRU flush batch */ +ulong srv_LRU_scan_depth; +/** innodb_flush_neighbors; whether or not to flush neighbors of a block */ +ulong srv_flush_neighbors; +/** Previously requested size */ +ulint srv_buf_pool_old_size; +/** Current size as scaling factor for the other components */ +ulint srv_buf_pool_base_size; +/** Current size in bytes */ +ulint srv_buf_pool_curr_size; +/** Dump this % of each buffer pool during BP dump */ +ulong srv_buf_pool_dump_pct; +/** Abort load after this amount of pages */ +#ifdef UNIV_DEBUG +ulong srv_buf_pool_load_pages_abort = LONG_MAX; +#endif +/** Lock table size in bytes */ +ulint srv_lock_table_size = ULINT_MAX; + +/** innodb_read_io_threads */ +uint srv_n_read_io_threads; +/** innodb_write_io_threads */ +uint srv_n_write_io_threads; + +/** innodb_random_read_ahead */ +my_bool srv_random_read_ahead; +/** innodb_read_ahead_threshold; the number of pages that must be present +in the buffer cache and accessed sequentially for InnoDB to trigger a +readahead request. */ +ulong srv_read_ahead_threshold; + +/** innodb_change_buffer_max_size; maximum on-disk size of change +buffer in terms of percentage of the buffer pool. */ +uint srv_change_buffer_max_size; + +ulong srv_file_flush_method; + + +/** copy of innodb_open_files; @see innodb_init_params() */ +ulint srv_max_n_open_files; + +/** innodb_io_capacity */ +ulong srv_io_capacity; +/** innodb_io_capacity_max */ +ulong srv_max_io_capacity; + +/* The InnoDB main thread tries to keep the ratio of modified pages +in the buffer pool to all database pages in the buffer pool smaller than +the following number. But it is not guaranteed that the value stays below +that during a time of heavy update/insert activity. */ + +/** innodb_max_dirty_pages_pct */ +double srv_max_buf_pool_modified_pct; +/** innodb_max_dirty_pages_pct_lwm */ +double srv_max_dirty_pages_pct_lwm; + +/** innodb_adaptive_flushing_lwm; the percentage of log capacity at +which adaptive flushing, if enabled, will kick in. */ +double srv_adaptive_flushing_lwm; + +/** innodb_flushing_avg_loops; number of iterations over which +adaptive flushing is averaged */ +ulong srv_flushing_avg_loops; + +/** innodb_purge_threads; the number of purge tasks to use */ +uint srv_n_purge_threads; + +/** innodb_purge_batch_size, in pages */ +ulong srv_purge_batch_size; + +/** innodb_stats_method decides how InnoDB treats +NULL value when collecting statistics. By default, it is set to +SRV_STATS_NULLS_EQUAL(0), ie. all NULL value are treated equal */ +ulong srv_innodb_stats_method; + +srv_stats_t srv_stats; + +/* structure to pass status variables to MySQL */ +export_var_t export_vars; + +/** Normally 0. When nonzero, skip some phases of crash recovery, +starting from SRV_FORCE_IGNORE_CORRUPT, so that data can be recovered +by SELECT or mysqldump. When this is nonzero, we do not allow any user +modifications to the data. */ +ulong srv_force_recovery; + +/** innodb_print_all_deadlocks; whether to print all user-level +transactions deadlocks to the error log */ +my_bool srv_print_all_deadlocks; + +/** innodb_cmp_per_index_enabled; enable +INFORMATION_SCHEMA.innodb_cmp_per_index */ +my_bool srv_cmp_per_index_enabled; + +/** innodb_fast_shutdown=1 skips purge and change buffer merge. +innodb_fast_shutdown=2 effectively crashes the server (no log checkpoint). +innodb_fast_shutdown=3 is a clean shutdown that skips the rollback +of active transaction (to be done on restart). */ +uint srv_fast_shutdown; + +/** copy of innodb_status_file; generate a innodb_status.<pid> file */ +ibool srv_innodb_status; + +/** innodb_prefix_index_cluster_optimization; whether to optimize +prefix index queries to skip cluster index lookup when possible */ +my_bool srv_prefix_index_cluster_optimization; + +/** innodb_stats_transient_sample_pages; +When estimating number of different key values in an index, sample +this many index pages, there are 2 ways to calculate statistics: +* persistent stats that are calculated by ANALYZE TABLE and saved + in the innodb database. +* quick transient stats, that are used if persistent stats for the given + table/index are not found in the innodb database */ +unsigned long long srv_stats_transient_sample_pages; +/** innodb_stats_persistent */ +my_bool srv_stats_persistent; +/** innodb_stats_include_delete_marked */ +my_bool srv_stats_include_delete_marked; +/** innodb_stats_persistent_sample_pages */ +unsigned long long srv_stats_persistent_sample_pages; +/** innodb_stats_auto_recalc */ +my_bool srv_stats_auto_recalc; + +/** innodb_stats_modified_counter; The number of rows modified before +we calculate new statistics (default 0 = current limits) */ +unsigned long long srv_stats_modified_counter; + +/** innodb_stats_traditional; enable traditional statistic calculation +based on number of configured pages */ +my_bool srv_stats_sample_traditional; + +my_bool srv_use_doublewrite_buf; + +/** innodb_sync_spin_loops */ +ulong srv_n_spin_wait_rounds; +/** innodb_spin_wait_delay */ +uint srv_spin_wait_delay; + +static ulint srv_n_rows_inserted_old; +static ulint srv_n_rows_updated_old; +static ulint srv_n_rows_deleted_old; +static ulint srv_n_rows_read_old; +static ulint srv_n_system_rows_inserted_old; +static ulint srv_n_system_rows_updated_old; +static ulint srv_n_system_rows_deleted_old; +static ulint srv_n_system_rows_read_old; + +ulint srv_truncated_status_writes; +/** Number of initialized rollback segments for persistent undo log */ +ulong srv_available_undo_logs; + +/* Defragmentation */ +UNIV_INTERN my_bool srv_defragment; +/** innodb_defragment_n_pages */ +UNIV_INTERN uint srv_defragment_n_pages; +UNIV_INTERN uint srv_defragment_stats_accuracy; +/** innodb_defragment_fill_factor_n_recs */ +UNIV_INTERN uint srv_defragment_fill_factor_n_recs; +/** innodb_defragment_fill_factor */ +UNIV_INTERN double srv_defragment_fill_factor; +/** innodb_defragment_frequency */ +UNIV_INTERN uint srv_defragment_frequency; +/** derived from innodb_defragment_frequency; +@see innodb_defragment_frequency_update() */ +UNIV_INTERN ulonglong srv_defragment_interval; + +/** Current mode of operation */ +UNIV_INTERN enum srv_operation_mode srv_operation; + +/* Set the following to 0 if you want InnoDB to write messages on +stderr on startup/shutdown. Not enabled on the embedded server. */ +ibool srv_print_verbose_log; +my_bool srv_print_innodb_monitor; +my_bool srv_print_innodb_lock_monitor; +/** innodb_force_primary_key; whether to disallow CREATE TABLE without +PRIMARY KEY */ +my_bool srv_force_primary_key; + +/** Key version to encrypt the temporary tablespace */ +my_bool innodb_encrypt_temporary_tables; + +my_bool srv_immediate_scrub_data_uncompressed; + +static time_t srv_last_monitor_time; + +static ib_mutex_t srv_innodb_monitor_mutex; + +/** Mutex protecting page_zip_stat_per_index */ +ib_mutex_t page_zip_stat_per_index_mutex; + +/* Mutex for locking srv_monitor_file. Not created if srv_read_only_mode */ +ib_mutex_t srv_monitor_file_mutex; + +/** Temporary file for innodb monitor output */ +FILE* srv_monitor_file; +/** Mutex for locking srv_misc_tmpfile. Not created if srv_read_only_mode. +This mutex has a very low rank; threads reserving it should not +acquire any further latches or sleep before releasing this one. */ +ib_mutex_t srv_misc_tmpfile_mutex; +/** Temporary file for miscellanous diagnostic output */ +FILE* srv_misc_tmpfile; + +static ulint srv_main_thread_process_no; +static ulint srv_main_thread_id; + +/* The following counts are used by the srv_master_callback. */ + +/** Iterations of the loop bounded by 'srv_active' label. */ +ulint srv_main_active_loops; +/** Iterations of the loop bounded by the 'srv_idle' label. */ +ulint srv_main_idle_loops; +/** Iterations of the loop bounded by the 'srv_shutdown' label. */ +static ulint srv_main_shutdown_loops; +/** Log writes involving flush. */ +ulint srv_log_writes_and_flush; + +/* This is only ever touched by the master thread. It records the +time when the last flush of log file has happened. The master +thread ensures that we flush the log files at least once per +second. */ +static time_t srv_last_log_flush_time; + +/* Interval in seconds at which various tasks are performed by the +master thread when server is active. In order to balance the workload, +we should try to keep intervals such that they are not multiple of +each other. For example, if we have intervals for various tasks +defined as 5, 10, 15, 60 then all tasks will be performed when +current_time % 60 == 0 and no tasks will be performed when +current_time % 5 != 0. */ + +# define SRV_MASTER_CHECKPOINT_INTERVAL (7) +# define SRV_MASTER_DICT_LRU_INTERVAL (47) + +/** Buffer pool dump status frequence in percentages */ +UNIV_INTERN ulong srv_buf_dump_status_frequency; + +/* + IMPLEMENTATION OF THE SERVER MAIN PROGRAM + ========================================= + +There is the following analogue between this database +server and an operating system kernel: + +DB concept equivalent OS concept +---------- --------------------- +transaction -- process; + +query thread -- thread; + +lock -- semaphore; + +kernel -- kernel; + +query thread execution: +(a) without lock mutex +reserved -- process executing in user mode; +(b) with lock mutex reserved + -- process executing in kernel mode; + +The server has several backgroind threads all running at the same +priority as user threads. It periodically checks if here is anything +happening in the server which requires intervention of the master +thread. Such situations may be, for example, when flushing of dirty +blocks is needed in the buffer pool or old version of database rows +have to be cleaned away (purged). The user can configure a separate +dedicated purge thread(s) too, in which case the master thread does not +do any purging. + +The threads which we call user threads serve the queries of the MySQL +server. They run at normal priority. + +When there is no activity in the system, also the master thread +suspends itself to wait for an event making the server totally silent. + +There is still one complication in our server design. If a +background utility thread obtains a resource (e.g., mutex) needed by a user +thread, and there is also some other user activity in the system, +the user thread may have to wait indefinitely long for the +resource, as the OS does not schedule a background thread if +there is some other runnable user thread. This problem is called +priority inversion in real-time programming. + +One solution to the priority inversion problem would be to keep record +of which thread owns which resource and in the above case boost the +priority of the background thread so that it will be scheduled and it +can release the resource. This solution is called priority inheritance +in real-time programming. A drawback of this solution is that the overhead +of acquiring a mutex increases slightly, maybe 0.2 microseconds on a 100 +MHz Pentium, because the thread has to call os_thread_get_curr_id. This may +be compared to 0.5 microsecond overhead for a mutex lock-unlock pair. Note +that the thread cannot store the information in the resource , say mutex, +itself, because competing threads could wipe out the information if it is +stored before acquiring the mutex, and if it stored afterwards, the +information is outdated for the time of one machine instruction, at least. +(To be precise, the information could be stored to lock_word in mutex if +the machine supports atomic swap.) + +The above solution with priority inheritance may become actual in the +future, currently we do not implement any priority twiddling solution. +Our general aim is to reduce the contention of all mutexes by making +them more fine grained. + +The thread table contains information of the current status of each +thread existing in the system, and also the event semaphores used in +suspending the master thread and utility threads when they have nothing +to do. The thread table can be seen as an analogue to the process table +in a traditional Unix implementation. */ + +/** The server system struct */ +struct srv_sys_t{ + ib_mutex_t tasks_mutex; /*!< variable protecting the + tasks queue */ + UT_LIST_BASE_NODE_T(que_thr_t) + tasks; /*!< task queue */ + + srv_stats_t::ulint_ctr_1_t + activity_count; /*!< For tracking server + activity */ +}; + +static srv_sys_t srv_sys; + +/* + Structure shared by timer and coordinator_callback. + No protection necessary since timer and task never run + in parallel (being in the same task group of size 1). +*/ +struct purge_coordinator_state +{ + /** Snapshot of the last history length before the purge call.*/ + uint32 m_history_length; + Atomic_counter<int> m_running; + purge_coordinator_state() : m_history_length(), m_running(0) {} +}; + +static purge_coordinator_state purge_state; + +/** threadpool timer for srv_monitor_task() */ +std::unique_ptr<tpool::timer> srv_monitor_timer; + + +/** The buffer pool dump/load file name */ +char* srv_buf_dump_filename; + +/** Boolean config knobs that tell InnoDB to dump the buffer pool at shutdown +and/or load it during startup. */ +char srv_buffer_pool_dump_at_shutdown = TRUE; +char srv_buffer_pool_load_at_startup = TRUE; + +#ifdef HAVE_PSI_STAGE_INTERFACE +/** Performance schema stage event for monitoring ALTER TABLE progress +everything after flush log_make_checkpoint(). */ +PSI_stage_info srv_stage_alter_table_end + = {0, "alter table (end)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring ALTER TABLE progress +row_merge_insert_index_tuples(). */ +PSI_stage_info srv_stage_alter_table_insert + = {0, "alter table (insert)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring ALTER TABLE progress +row_log_apply(). */ +PSI_stage_info srv_stage_alter_table_log_index + = {0, "alter table (log apply index)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring ALTER TABLE progress +row_log_table_apply(). */ +PSI_stage_info srv_stage_alter_table_log_table + = {0, "alter table (log apply table)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring ALTER TABLE progress +row_merge_sort(). */ +PSI_stage_info srv_stage_alter_table_merge_sort + = {0, "alter table (merge sort)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring ALTER TABLE progress +row_merge_read_clustered_index(). */ +PSI_stage_info srv_stage_alter_table_read_pk_internal_sort + = {0, "alter table (read PK and internal sort)", PSI_FLAG_STAGE_PROGRESS}; + +/** Performance schema stage event for monitoring buffer pool load progress. */ +PSI_stage_info srv_stage_buffer_pool_load + = {0, "buffer pool load", PSI_FLAG_STAGE_PROGRESS}; +#endif /* HAVE_PSI_STAGE_INTERFACE */ + +/*********************************************************************//** +Prints counters for work done by srv_master_thread. */ +static +void +srv_print_master_thread_info( +/*=========================*/ + FILE *file) /* in: output stream */ +{ + fprintf(file, "srv_master_thread loops: " ULINTPF " srv_active, " + ULINTPF " srv_shutdown, " ULINTPF " srv_idle\n" + "srv_master_thread log flush and writes: " ULINTPF "\n", + srv_main_active_loops, + srv_main_shutdown_loops, + srv_main_idle_loops, + srv_log_writes_and_flush); +} + +static void thread_pool_thread_init() +{ + my_thread_init(); + pfs_register_thread(thread_pool_thread_key); +} +static void thread_pool_thread_end() +{ + pfs_delete_thread(); + my_thread_end(); +} + + +#ifndef DBUG_OFF +static void dbug_after_task_callback() +{ + ut_ad(!sync_check_iterate(sync_check())); +} +#endif + +void srv_thread_pool_init() +{ + DBUG_ASSERT(!srv_thread_pool); + +#if defined (_WIN32) + srv_thread_pool= tpool::create_thread_pool_win(); +#else + srv_thread_pool= tpool::create_thread_pool_generic(); +#endif + srv_thread_pool->set_thread_callbacks(thread_pool_thread_init, + thread_pool_thread_end); +#ifndef DBUG_OFF + tpool::set_after_task_callback(dbug_after_task_callback); +#endif +} + + +void srv_thread_pool_end() +{ + ut_ad(!srv_master_timer); + delete srv_thread_pool; + srv_thread_pool= nullptr; +} + +static bool need_srv_free; + +/** Initialize the server. */ +static void srv_init() +{ + mutex_create(LATCH_ID_SRV_INNODB_MONITOR, &srv_innodb_monitor_mutex); + + if (!srv_read_only_mode) { + mutex_create(LATCH_ID_SRV_SYS_TASKS, &srv_sys.tasks_mutex); + + UT_LIST_INIT(srv_sys.tasks, &que_thr_t::queue); + } + + need_srv_free = true; + ut_d(srv_master_thread_disabled_event = os_event_create(0)); + + /* page_zip_stat_per_index_mutex is acquired from: + 1. page_zip_compress() (after SYNC_FSP) + 2. page_zip_decompress() + 3. i_s_cmp_per_index_fill_low() (where SYNC_DICT is acquired) + 4. innodb_cmp_per_index_update(), no other latches + since we do not acquire any other latches while holding this mutex, + it can have very low level. We pick SYNC_ANY_LATCH for it. */ + mutex_create(LATCH_ID_PAGE_ZIP_STAT_PER_INDEX, + &page_zip_stat_per_index_mutex); + +#ifdef WITH_INNODB_DISALLOW_WRITES + /* Writes have to be enabled on init or else we hang. Thus, we + always set the event here regardless of innobase_disallow_writes. + That flag will always be 0 at this point because it isn't settable + via my.cnf or command line arg. */ + srv_allow_writes_event = os_event_create(0); + os_event_set(srv_allow_writes_event); +#endif /* WITH_INNODB_DISALLOW_WRITES */ + + /* Initialize some INFORMATION SCHEMA internal structures */ + trx_i_s_cache_init(trx_i_s_cache); + +} + +/*********************************************************************//** +Frees the data structures created in srv_init(). */ +void +srv_free(void) +/*==========*/ +{ + if (!need_srv_free) { + return; + } + + mutex_free(&srv_innodb_monitor_mutex); + mutex_free(&page_zip_stat_per_index_mutex); + + if (!srv_read_only_mode) { + mutex_free(&srv_sys.tasks_mutex); + } + + ut_d(os_event_destroy(srv_master_thread_disabled_event)); + + trx_i_s_cache_free(trx_i_s_cache); + srv_thread_pool_end(); +} + +/*********************************************************************//** +Boots the InnoDB server. */ +void +srv_boot(void) +/*==========*/ +{ + srv_thread_pool_init(); + sync_check_init(); + trx_pool_init(); + row_mysql_init(); + srv_init(); +} + +/******************************************************************//** +Refreshes the values used to calculate per-second averages. */ +static void srv_refresh_innodb_monitor_stats(time_t current_time) +{ + mutex_enter(&srv_innodb_monitor_mutex); + + if (difftime(current_time, srv_last_monitor_time) < 60) { + /* We referesh InnoDB Monitor values so that averages are + printed from at most 60 last seconds */ + mutex_exit(&srv_innodb_monitor_mutex); + return; + } + + srv_last_monitor_time = current_time; + + os_aio_refresh_stats(); + +#ifdef BTR_CUR_HASH_ADAPT + btr_cur_n_sea_old = btr_cur_n_sea; +#endif /* BTR_CUR_HASH_ADAPT */ + btr_cur_n_non_sea_old = btr_cur_n_non_sea; + + log_refresh_stats(); + + buf_refresh_io_stats(); + + srv_n_rows_inserted_old = srv_stats.n_rows_inserted; + srv_n_rows_updated_old = srv_stats.n_rows_updated; + srv_n_rows_deleted_old = srv_stats.n_rows_deleted; + srv_n_rows_read_old = srv_stats.n_rows_read; + + srv_n_system_rows_inserted_old = srv_stats.n_system_rows_inserted; + srv_n_system_rows_updated_old = srv_stats.n_system_rows_updated; + srv_n_system_rows_deleted_old = srv_stats.n_system_rows_deleted; + srv_n_system_rows_read_old = srv_stats.n_system_rows_read; + + mutex_exit(&srv_innodb_monitor_mutex); +} + +/******************************************************************//** +Outputs to a file the output of the InnoDB Monitor. +@return FALSE if not all information printed +due to failure to obtain necessary mutex */ +ibool +srv_printf_innodb_monitor( +/*======================*/ + FILE* file, /*!< in: output stream */ + ibool nowait, /*!< in: whether to wait for the + lock_sys_t:: mutex */ + ulint* trx_start_pos, /*!< out: file position of the start of + the list of active transactions */ + ulint* trx_end) /*!< out: file position of the end of + the list of active transactions */ +{ + double time_elapsed; + time_t current_time; + ibool ret; + + mutex_enter(&srv_innodb_monitor_mutex); + + current_time = time(NULL); + + /* We add 0.001 seconds to time_elapsed to prevent division + by zero if two users happen to call SHOW ENGINE INNODB STATUS at the + same time */ + + time_elapsed = difftime(current_time, srv_last_monitor_time) + + 0.001; + + srv_last_monitor_time = time(NULL); + + fputs("\n=====================================\n", file); + + ut_print_timestamp(file); + fprintf(file, + " INNODB MONITOR OUTPUT\n" + "=====================================\n" + "Per second averages calculated from the last %lu seconds\n", + (ulong) time_elapsed); + + fputs("-----------------\n" + "BACKGROUND THREAD\n" + "-----------------\n", file); + srv_print_master_thread_info(file); + + fputs("----------\n" + "SEMAPHORES\n" + "----------\n", file); + + sync_print(file); + + /* Conceptually, srv_innodb_monitor_mutex has a very high latching + order level in sync0sync.h, while dict_foreign_err_mutex has a very + low level 135. Therefore we can reserve the latter mutex here without + a danger of a deadlock of threads. */ + + mutex_enter(&dict_foreign_err_mutex); + + if (!srv_read_only_mode && ftell(dict_foreign_err_file) != 0L) { + fputs("------------------------\n" + "LATEST FOREIGN KEY ERROR\n" + "------------------------\n", file); + ut_copy_file(file, dict_foreign_err_file); + } + + mutex_exit(&dict_foreign_err_mutex); + + /* Only if lock_print_info_summary proceeds correctly, + before we call the lock_print_info_all_transactions + to print all the lock information. IMPORTANT NOTE: This + function acquires the lock mutex on success. */ + ret = lock_print_info_summary(file, nowait); + + if (ret) { + if (trx_start_pos) { + long t = ftell(file); + if (t < 0) { + *trx_start_pos = ULINT_UNDEFINED; + } else { + *trx_start_pos = (ulint) t; + } + } + + /* NOTE: If we get here then we have the lock mutex. This + function will release the lock mutex that we acquired when + we called the lock_print_info_summary() function earlier. */ + + lock_print_info_all_transactions(file); + + if (trx_end) { + long t = ftell(file); + if (t < 0) { + *trx_end = ULINT_UNDEFINED; + } else { + *trx_end = (ulint) t; + } + } + } + + fputs("--------\n" + "FILE I/O\n" + "--------\n", file); + os_aio_print(file); + + fputs("-------------------------------------\n" + "INSERT BUFFER AND ADAPTIVE HASH INDEX\n" + "-------------------------------------\n", file); + ibuf_print(file); + +#ifdef BTR_CUR_HASH_ADAPT + for (ulint i = 0; i < btr_ahi_parts && btr_search_enabled; ++i) { + const auto part= &btr_search_sys.parts[i]; + rw_lock_s_lock(&part->latch); + ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH); + fprintf(file, "Hash table size " ULINTPF + ", node heap has " ULINTPF " buffer(s)\n", + part->table.n_cells, + part->heap->base.count - !part->heap->free_block); + rw_lock_s_unlock(&part->latch); + } + + fprintf(file, + "%.2f hash searches/s, %.2f non-hash searches/s\n", + static_cast<double>(btr_cur_n_sea - btr_cur_n_sea_old) + / time_elapsed, + static_cast<double>(btr_cur_n_non_sea - btr_cur_n_non_sea_old) + / time_elapsed); + btr_cur_n_sea_old = btr_cur_n_sea; +#else /* BTR_CUR_HASH_ADAPT */ + fprintf(file, + "%.2f non-hash searches/s\n", + static_cast<double>(btr_cur_n_non_sea - btr_cur_n_non_sea_old) + / time_elapsed); +#endif /* BTR_CUR_HASH_ADAPT */ + btr_cur_n_non_sea_old = btr_cur_n_non_sea; + + fputs("---\n" + "LOG\n" + "---\n", file); + log_print(file); + + fputs("----------------------\n" + "BUFFER POOL AND MEMORY\n" + "----------------------\n", file); + fprintf(file, + "Total large memory allocated " ULINTPF "\n" + "Dictionary memory allocated " ULINTPF "\n", + ulint{os_total_large_mem_allocated}, + dict_sys.rough_size()); + + buf_print_io(file); + + fputs("--------------\n" + "ROW OPERATIONS\n" + "--------------\n", file); + fprintf(file, ULINTPF " read views open inside InnoDB\n", + trx_sys.view_count()); + + if (ulint n_reserved = fil_system.sys_space->n_reserved_extents) { + fprintf(file, + ULINTPF " tablespace extents now reserved for" + " B-tree split operations\n", + n_reserved); + } + + fprintf(file, + "Process ID=" ULINTPF + ", Main thread ID=" ULINTPF + ", state: %s\n", + srv_main_thread_process_no, + srv_main_thread_id, + srv_main_thread_op_info); + fprintf(file, + "Number of rows inserted " ULINTPF + ", updated " ULINTPF + ", deleted " ULINTPF + ", read " ULINTPF "\n", + (ulint) srv_stats.n_rows_inserted, + (ulint) srv_stats.n_rows_updated, + (ulint) srv_stats.n_rows_deleted, + (ulint) srv_stats.n_rows_read); + fprintf(file, + "%.2f inserts/s, %.2f updates/s," + " %.2f deletes/s, %.2f reads/s\n", + static_cast<double>(srv_stats.n_rows_inserted + - srv_n_rows_inserted_old) + / time_elapsed, + static_cast<double>(srv_stats.n_rows_updated + - srv_n_rows_updated_old) + / time_elapsed, + static_cast<double>(srv_stats.n_rows_deleted + - srv_n_rows_deleted_old) + / time_elapsed, + static_cast<double>(srv_stats.n_rows_read + - srv_n_rows_read_old) + / time_elapsed); + fprintf(file, + "Number of system rows inserted " ULINTPF + ", updated " ULINTPF ", deleted " ULINTPF + ", read " ULINTPF "\n", + (ulint) srv_stats.n_system_rows_inserted, + (ulint) srv_stats.n_system_rows_updated, + (ulint) srv_stats.n_system_rows_deleted, + (ulint) srv_stats.n_system_rows_read); + fprintf(file, + "%.2f inserts/s, %.2f updates/s," + " %.2f deletes/s, %.2f reads/s\n", + static_cast<double>(srv_stats.n_system_rows_inserted + - srv_n_system_rows_inserted_old) + / time_elapsed, + static_cast<double>(srv_stats.n_system_rows_updated + - srv_n_system_rows_updated_old) + / time_elapsed, + static_cast<double>(srv_stats.n_system_rows_deleted + - srv_n_system_rows_deleted_old) + / time_elapsed, + static_cast<double>(srv_stats.n_system_rows_read + - srv_n_system_rows_read_old) + / time_elapsed); + srv_n_rows_inserted_old = srv_stats.n_rows_inserted; + srv_n_rows_updated_old = srv_stats.n_rows_updated; + srv_n_rows_deleted_old = srv_stats.n_rows_deleted; + srv_n_rows_read_old = srv_stats.n_rows_read; + srv_n_system_rows_inserted_old = srv_stats.n_system_rows_inserted; + srv_n_system_rows_updated_old = srv_stats.n_system_rows_updated; + srv_n_system_rows_deleted_old = srv_stats.n_system_rows_deleted; + srv_n_system_rows_read_old = srv_stats.n_system_rows_read; + + fputs("----------------------------\n" + "END OF INNODB MONITOR OUTPUT\n" + "============================\n", file); + mutex_exit(&srv_innodb_monitor_mutex); + fflush(file); + + return(ret); +} + +/******************************************************************//** +Function to pass InnoDB status variables to MySQL */ +void +srv_export_innodb_status(void) +/*==========================*/ +{ + fil_crypt_stat_t crypt_stat; + + if (!srv_read_only_mode) { + fil_crypt_total_stat(&crypt_stat); + } + +#ifdef BTR_CUR_HASH_ADAPT + ulint mem_adaptive_hash = 0; + for (ulong i = 0; i < btr_ahi_parts; i++) { + const auto part= &btr_search_sys.parts[i]; + rw_lock_s_lock(&part->latch); + if (part->heap) { + ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH); + + mem_adaptive_hash += mem_heap_get_size(part->heap) + + part->table.n_cells * sizeof(hash_cell_t); + } + rw_lock_s_unlock(&part->latch); + } + export_vars.innodb_mem_adaptive_hash = mem_adaptive_hash; +#endif + + export_vars.innodb_mem_dictionary = dict_sys.rough_size(); + + mutex_enter(&srv_innodb_monitor_mutex); + + export_vars.innodb_data_pending_reads = + ulint(MONITOR_VALUE(MONITOR_OS_PENDING_READS)); + + export_vars.innodb_data_pending_writes = + ulint(MONITOR_VALUE(MONITOR_OS_PENDING_WRITES)); + + export_vars.innodb_data_pending_fsyncs = + log_sys.get_pending_flushes() + + fil_n_pending_tablespace_flushes; + + export_vars.innodb_data_fsyncs = os_n_fsyncs; + + export_vars.innodb_data_read = srv_stats.data_read; + + export_vars.innodb_data_reads = os_n_file_reads; + + export_vars.innodb_data_writes = os_n_file_writes; + + ulint dblwr = 0; + + if (buf_dblwr.is_initialised()) { + buf_dblwr.lock(); + dblwr = buf_dblwr.submitted(); + export_vars.innodb_dblwr_pages_written = buf_dblwr.written(); + export_vars.innodb_dblwr_writes = buf_dblwr.batches(); + buf_dblwr.unlock(); + } + + export_vars.innodb_data_written = srv_stats.data_written + dblwr; + + export_vars.innodb_buffer_pool_read_requests + = buf_pool.stat.n_page_gets; + + export_vars.innodb_buffer_pool_write_requests = + srv_stats.buf_pool_write_requests; + + export_vars.innodb_buffer_pool_reads = srv_stats.buf_pool_reads; + + export_vars.innodb_buffer_pool_read_ahead_rnd = + buf_pool.stat.n_ra_pages_read_rnd; + + export_vars.innodb_buffer_pool_read_ahead = + buf_pool.stat.n_ra_pages_read; + + export_vars.innodb_buffer_pool_read_ahead_evicted = + buf_pool.stat.n_ra_pages_evicted; + + export_vars.innodb_buffer_pool_pages_data = + UT_LIST_GET_LEN(buf_pool.LRU); + + export_vars.innodb_buffer_pool_bytes_data = + buf_pool.stat.LRU_bytes + + (UT_LIST_GET_LEN(buf_pool.unzip_LRU) + << srv_page_size_shift); + + export_vars.innodb_buffer_pool_pages_dirty = + UT_LIST_GET_LEN(buf_pool.flush_list); + + export_vars.innodb_buffer_pool_pages_made_young + = buf_pool.stat.n_pages_made_young; + export_vars.innodb_buffer_pool_pages_made_not_young + = buf_pool.stat.n_pages_not_made_young; + + export_vars.innodb_buffer_pool_pages_old = buf_pool.LRU_old_len; + + export_vars.innodb_buffer_pool_bytes_dirty = + buf_pool.stat.flush_list_bytes; + + export_vars.innodb_buffer_pool_pages_free = + UT_LIST_GET_LEN(buf_pool.free); + +#ifdef UNIV_DEBUG + export_vars.innodb_buffer_pool_pages_latched = + buf_get_latched_pages_number(); +#endif /* UNIV_DEBUG */ + export_vars.innodb_buffer_pool_pages_total = buf_pool.get_n_pages(); + + export_vars.innodb_buffer_pool_pages_misc = + buf_pool.get_n_pages() + - UT_LIST_GET_LEN(buf_pool.LRU) + - UT_LIST_GET_LEN(buf_pool.free); + + export_vars.innodb_max_trx_id = trx_sys.get_max_trx_id(); + export_vars.innodb_history_list_length = trx_sys.rseg_history_len; + + export_vars.innodb_log_waits = srv_stats.log_waits; + + export_vars.innodb_os_log_written = srv_stats.os_log_written; + + export_vars.innodb_os_log_fsyncs = log_sys.get_flushes(); + + export_vars.innodb_os_log_pending_fsyncs + = log_sys.get_pending_flushes(); + + export_vars.innodb_os_log_pending_writes = + srv_stats.os_log_pending_writes; + + export_vars.innodb_log_write_requests = srv_stats.log_write_requests; + + export_vars.innodb_log_writes = srv_stats.log_writes; + + export_vars.innodb_row_lock_waits = srv_stats.n_lock_wait_count; + + export_vars.innodb_row_lock_current_waits = + srv_stats.n_lock_wait_current_count; + + export_vars.innodb_row_lock_time = srv_stats.n_lock_wait_time / 1000; + + if (srv_stats.n_lock_wait_count > 0) { + + export_vars.innodb_row_lock_time_avg = (ulint) + (srv_stats.n_lock_wait_time + / 1000 / srv_stats.n_lock_wait_count); + + } else { + export_vars.innodb_row_lock_time_avg = 0; + } + + export_vars.innodb_row_lock_time_max = + lock_sys.n_lock_max_wait_time / 1000; + + export_vars.innodb_rows_read = srv_stats.n_rows_read; + + export_vars.innodb_rows_inserted = srv_stats.n_rows_inserted; + + export_vars.innodb_rows_updated = srv_stats.n_rows_updated; + + export_vars.innodb_rows_deleted = srv_stats.n_rows_deleted; + + export_vars.innodb_system_rows_read = srv_stats.n_system_rows_read; + + export_vars.innodb_system_rows_inserted = + srv_stats.n_system_rows_inserted; + + export_vars.innodb_system_rows_updated = + srv_stats.n_system_rows_updated; + + export_vars.innodb_system_rows_deleted = + srv_stats.n_system_rows_deleted; + + export_vars.innodb_truncated_status_writes = + srv_truncated_status_writes; + + export_vars.innodb_page_compression_saved = srv_stats.page_compression_saved; + export_vars.innodb_index_pages_written = srv_stats.index_pages_written; + export_vars.innodb_non_index_pages_written = srv_stats.non_index_pages_written; + export_vars.innodb_pages_page_compressed = srv_stats.pages_page_compressed; + export_vars.innodb_page_compressed_trim_op = srv_stats.page_compressed_trim_op; + export_vars.innodb_pages_page_decompressed = srv_stats.pages_page_decompressed; + export_vars.innodb_pages_page_compression_error = srv_stats.pages_page_compression_error; + export_vars.innodb_pages_decrypted = srv_stats.pages_decrypted; + export_vars.innodb_pages_encrypted = srv_stats.pages_encrypted; + export_vars.innodb_n_merge_blocks_encrypted = srv_stats.n_merge_blocks_encrypted; + export_vars.innodb_n_merge_blocks_decrypted = srv_stats.n_merge_blocks_decrypted; + export_vars.innodb_n_rowlog_blocks_encrypted = srv_stats.n_rowlog_blocks_encrypted; + export_vars.innodb_n_rowlog_blocks_decrypted = srv_stats.n_rowlog_blocks_decrypted; + + export_vars.innodb_n_temp_blocks_encrypted = + srv_stats.n_temp_blocks_encrypted; + + export_vars.innodb_n_temp_blocks_decrypted = + srv_stats.n_temp_blocks_decrypted; + + export_vars.innodb_defragment_compression_failures = + btr_defragment_compression_failures; + export_vars.innodb_defragment_failures = btr_defragment_failures; + export_vars.innodb_defragment_count = btr_defragment_count; + + export_vars.innodb_onlineddl_rowlog_rows = onlineddl_rowlog_rows; + export_vars.innodb_onlineddl_rowlog_pct_used = onlineddl_rowlog_pct_used; + export_vars.innodb_onlineddl_pct_progress = onlineddl_pct_progress; + + export_vars.innodb_sec_rec_cluster_reads = + srv_stats.n_sec_rec_cluster_reads; + export_vars.innodb_sec_rec_cluster_reads_avoided = + srv_stats.n_sec_rec_cluster_reads_avoided; + + if (!srv_read_only_mode) { + export_vars.innodb_encryption_rotation_pages_read_from_cache = + crypt_stat.pages_read_from_cache; + export_vars.innodb_encryption_rotation_pages_read_from_disk = + crypt_stat.pages_read_from_disk; + export_vars.innodb_encryption_rotation_pages_modified = + crypt_stat.pages_modified; + export_vars.innodb_encryption_rotation_pages_flushed = + crypt_stat.pages_flushed; + export_vars.innodb_encryption_rotation_estimated_iops = + crypt_stat.estimated_iops; + export_vars.innodb_encryption_key_requests = + srv_stats.n_key_requests; + export_vars.innodb_key_rotation_list_length = + srv_stats.key_rotation_list_length; + } + + mutex_exit(&srv_innodb_monitor_mutex); + + mysql_mutex_lock(&log_sys.mutex); + export_vars.innodb_lsn_current = log_sys.get_lsn(); + export_vars.innodb_lsn_flushed = log_sys.get_flushed_lsn(); + export_vars.innodb_lsn_last_checkpoint = log_sys.last_checkpoint_lsn; + export_vars.innodb_checkpoint_max_age = static_cast<ulint>( + log_sys.max_checkpoint_age); + mysql_mutex_unlock(&log_sys.mutex); + + export_vars.innodb_checkpoint_age = static_cast<ulint>( + export_vars.innodb_lsn_current + - export_vars.innodb_lsn_last_checkpoint); +} + +struct srv_monitor_state_t +{ + time_t last_monitor_time; + ulint mutex_skipped; + bool last_srv_print_monitor; + srv_monitor_state_t() : mutex_skipped(0), last_srv_print_monitor(false) + { + srv_last_monitor_time = time(NULL); + last_monitor_time= srv_last_monitor_time; + } +}; + +static srv_monitor_state_t monitor_state; + +/** A task which prints the info output by various InnoDB monitors.*/ +static void srv_monitor() +{ + time_t current_time = time(NULL); + + if (difftime(current_time, monitor_state.last_monitor_time) >= 15) { + monitor_state.last_monitor_time = current_time; + + if (srv_print_innodb_monitor) { + /* Reset mutex_skipped counter everytime + srv_print_innodb_monitor changes. This is to + ensure we will not be blocked by lock_sys.mutex + for short duration information printing */ + if (!monitor_state.last_srv_print_monitor) { + monitor_state.mutex_skipped = 0; + monitor_state.last_srv_print_monitor = true; + } + + if (!srv_printf_innodb_monitor(stderr, + MUTEX_NOWAIT(monitor_state.mutex_skipped), + NULL, NULL)) { + monitor_state.mutex_skipped++; + } else { + /* Reset the counter */ + monitor_state.mutex_skipped = 0; + } + } else { + monitor_state.last_monitor_time = 0; + } + + + /* We don't create the temp files or associated + mutexes in read-only-mode */ + + if (!srv_read_only_mode && srv_innodb_status) { + mutex_enter(&srv_monitor_file_mutex); + rewind(srv_monitor_file); + if (!srv_printf_innodb_monitor(srv_monitor_file, + MUTEX_NOWAIT(monitor_state.mutex_skipped), + NULL, NULL)) { + monitor_state.mutex_skipped++; + } else { + monitor_state.mutex_skipped = 0; + } + + os_file_set_eof(srv_monitor_file); + mutex_exit(&srv_monitor_file_mutex); + } + } + + srv_refresh_innodb_monitor_stats(current_time); +} + +/*********************************************************************//** +A task which prints warnings about semaphore waits which have lasted +too long. These can be used to track bugs which cause hangs. +*/ +void srv_monitor_task(void*) +{ + /* number of successive fatal timeouts observed */ + static ulint fatal_cnt; + static lsn_t old_lsn = recv_sys.recovered_lsn; + /* longest waiting thread for a semaphore */ + os_thread_id_t waiter; + static os_thread_id_t old_waiter = os_thread_get_curr_id(); + /* the semaphore that is being waited for */ + const void* sema = NULL; + static const void* old_sema = NULL; + + ut_ad(!srv_read_only_mode); + + /* Try to track a strange bug reported by Harald Fuchs and others, + where the lsn seems to decrease at times */ + + lsn_t new_lsn = log_sys.get_lsn(); + ut_a(new_lsn >= old_lsn); + old_lsn = new_lsn; + + /* Update the statistics collected for deciding LRU + eviction policy. */ + buf_LRU_stat_update(); + + if (sync_array_print_long_waits(&waiter, &sema) + && sema == old_sema && os_thread_eq(waiter, old_waiter)) { +#if defined(WITH_WSREP) && defined(WITH_INNODB_DISALLOW_WRITES) + if (!os_event_is_set(srv_allow_writes_event)) { + fprintf(stderr, + "WSREP: avoiding InnoDB self crash due to " + "long semaphore wait of > %lu seconds\n" + "Server is processing SST donor operation, " + "fatal_cnt now: " ULINTPF, + srv_fatal_semaphore_wait_threshold, fatal_cnt); + return; + } +#endif /* WITH_WSREP */ + if (fatal_cnt++) { + ib::fatal() << "Semaphore wait has lasted > " + << srv_fatal_semaphore_wait_threshold + << " seconds. We intentionally crash the" + " server because it appears to be hung."; + } + } else { + fatal_cnt = 0; + old_waiter = waiter; + old_sema = sema; + } + + srv_monitor(); +} + +/******************************************************************//** +Increment the server activity count. */ +void +srv_inc_activity_count(void) +/*========================*/ +{ + srv_sys.activity_count.inc(); +} + +#ifdef UNIV_DEBUG +/** @return whether purge or master task is active */ +bool srv_any_background_activity() +{ + if (purge_sys.enabled() || srv_master_timer.get()) + { + ut_ad(!srv_read_only_mode); + return true; + } + return false; +} +#endif /* UNIV_DEBUG */ + +static void purge_worker_callback(void*); +static void purge_coordinator_callback(void*); +static void purge_coordinator_timer_callback(void*); + +static tpool::task_group purge_task_group; +tpool::waitable_task purge_worker_task(purge_worker_callback, nullptr, + &purge_task_group); +static tpool::task_group purge_coordinator_task_group(1); +static tpool::waitable_task purge_coordinator_task + (purge_coordinator_callback, nullptr, &purge_coordinator_task_group); + +static tpool::timer *purge_coordinator_timer; + +/** Wake up the purge threads if there is work to do. */ +void +srv_wake_purge_thread_if_not_active() +{ + ut_ad(!srv_read_only_mode); + + if (purge_sys.enabled() && !purge_sys.paused() + && trx_sys.rseg_history_len) { + if(++purge_state.m_running == 1) { + srv_thread_pool->submit_task(&purge_coordinator_task); + } + } +} + +/** @return whether the purge tasks are active */ +bool purge_sys_t::running() const +{ + return purge_coordinator_task.is_running(); +} + +/** Stop purge during FLUSH TABLES FOR EXPORT */ +void purge_sys_t::stop() +{ + rw_lock_x_lock(&latch); + + if (!enabled()) + { + /* Shutdown must have been initiated during FLUSH TABLES FOR EXPORT. */ + ut_ad(!srv_undo_sources); + rw_lock_x_unlock(&latch); + return; + } + + ut_ad(srv_n_purge_threads > 0); + + const auto paused= m_paused++; + + rw_lock_x_unlock(&latch); + + if (!paused) + { + ib::info() << "Stopping purge"; + MONITOR_ATOMIC_INC(MONITOR_PURGE_STOP_COUNT); + purge_coordinator_task.disable(); + } +} + +/** Resume purge at UNLOCK TABLES after FLUSH TABLES FOR EXPORT */ +void purge_sys_t::resume() +{ + if (!enabled()) + { + /* Shutdown must have been initiated during FLUSH TABLES FOR EXPORT. */ + ut_ad(!srv_undo_sources); + return; + } + ut_ad(!srv_read_only_mode); + ut_ad(srv_force_recovery < SRV_FORCE_NO_BACKGROUND); + ut_ad(!sync_check_iterate(sync_check())); + purge_coordinator_task.enable(); + rw_lock_x_lock(&latch); + int32_t paused= m_paused--; + ut_a(paused); + + if (paused == 1) + { + ib::info() << "Resuming purge"; + purge_state.m_running = 0; + srv_wake_purge_thread_if_not_active(); + MONITOR_ATOMIC_INC(MONITOR_PURGE_RESUME_COUNT); + } + rw_lock_x_unlock(&latch); +} + +/*******************************************************************//** +Get current server activity count. +@return activity count. */ +ulint +srv_get_activity_count(void) +/*========================*/ +{ + return(srv_sys.activity_count); +} + +/** Check if srv_inc_activity_count() has been called. +@param activity_count copy of srv_sys.activity_count +@return whether the activity_count had changed */ +static bool srv_check_activity(ulint *activity_count) +{ + ulint new_activity_count= srv_sys.activity_count; + if (new_activity_count != *activity_count) + { + *activity_count= new_activity_count; + return true; + } + + return false; +} + +/********************************************************************//** +The master thread is tasked to ensure that flush of log file happens +once every second in the background. This is to ensure that not more +than one second of trxs are lost in case of crash when +innodb_flush_logs_at_trx_commit != 1 */ +static +void +srv_sync_log_buffer_in_background(void) +/*===================================*/ +{ + time_t current_time = time(NULL); + + srv_main_thread_op_info = "flushing log"; + if (difftime(current_time, srv_last_log_flush_time) + >= srv_flush_log_at_timeout) { + log_buffer_flush_to_disk(); + srv_last_log_flush_time = current_time; + srv_log_writes_and_flush++; + } +} + +/********************************************************************//** +Make room in the table cache by evicting an unused table. +@return number of tables evicted. */ +static +ulint +srv_master_evict_from_table_cache( +/*==============================*/ + ulint pct_check) /*!< in: max percent to check */ +{ + ulint n_tables_evicted = 0; + + dict_sys_lock(); + + n_tables_evicted = dict_make_room_in_cache( + innobase_get_table_cache_size(), pct_check); + + dict_sys_unlock(); + + return(n_tables_evicted); +} + +/*********************************************************************//** +This function prints progress message every 60 seconds during server +shutdown, for any activities that master thread is pending on. */ +static +void +srv_shutdown_print_master_pending( +/*==============================*/ + time_t* last_print_time, /*!< last time the function + print the message */ + ulint n_tables_to_drop, /*!< number of tables to + be dropped */ + ulint n_bytes_merged) /*!< number of change buffer + just merged */ +{ + time_t current_time = time(NULL); + + if (difftime(current_time, *last_print_time) > 60) { + *last_print_time = current_time; + + if (n_tables_to_drop) { + ib::info() << "Waiting for " << n_tables_to_drop + << " table(s) to be dropped"; + } + + /* Check change buffer merge, we only wait for change buffer + merge if it is a slow shutdown */ + if (!srv_fast_shutdown && n_bytes_merged) { + ib::info() << "Waiting for change buffer merge to" + " complete number of bytes of change buffer" + " just merged: " << n_bytes_merged; + } + } +} + +#ifdef UNIV_DEBUG +/** Waits in loop as long as master thread is disabled (debug) */ +static +void +srv_master_do_disabled_loop(void) +{ + if (!srv_master_thread_disabled_debug) { + /* We return here to avoid changing op_info. */ + return; + } + + srv_main_thread_op_info = "disabled"; + + while (srv_master_thread_disabled_debug) { + os_event_set(srv_master_thread_disabled_event); + if (srv_shutdown_state != SRV_SHUTDOWN_NONE) { + break; + } + os_thread_sleep(100000); + } + + srv_main_thread_op_info = ""; +} + +/** Disables master thread. It's used by: + SET GLOBAL innodb_master_thread_disabled_debug = 1 (0). +@param[in] save immediate result from check function */ +void +srv_master_thread_disabled_debug_update(THD*, st_mysql_sys_var*, void*, + const void* save) +{ + /* This method is protected by mutex, as every SET GLOBAL .. */ + ut_ad(srv_master_thread_disabled_event != NULL); + + const bool disable = *static_cast<const my_bool*>(save); + + const int64_t sig_count = os_event_reset( + srv_master_thread_disabled_event); + + srv_master_thread_disabled_debug = disable; + + if (disable) { + os_event_wait_low( + srv_master_thread_disabled_event, sig_count); + } +} +#endif /* UNIV_DEBUG */ + +/*********************************************************************//** +Perform the tasks that the master thread is supposed to do when the +server is active. There are two types of tasks. The first category is +of such tasks which are performed at each inovcation of this function. +We assume that this function is called roughly every second when the +server is active. The second category is of such tasks which are +performed at some interval e.g.: purge, dict_LRU cleanup etc. */ +static +void +srv_master_do_active_tasks(void) +/*============================*/ +{ + time_t cur_time = time(NULL); + ulonglong counter_time = microsecond_interval_timer(); + + /* First do the tasks that we are suppose to do at each + invocation of this function. */ + + ++srv_main_active_loops; + + MONITOR_INC(MONITOR_MASTER_ACTIVE_LOOPS); + + /* ALTER TABLE in MySQL requires on Unix that the table handler + can drop tables lazily after there no longer are SELECT + queries to them. */ + srv_main_thread_op_info = "doing background drop tables"; + row_drop_tables_for_mysql_in_background(); + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, counter_time); + + ut_d(srv_master_do_disabled_loop()); + + if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { + return; + } + + /* make sure that there is enough reusable space in the redo + log files */ + srv_main_thread_op_info = "checking free log space"; + log_free_check(); + + /* Flush logs if needed */ + srv_main_thread_op_info = "flushing log"; + srv_sync_log_buffer_in_background(); + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time); + + /* Now see if various tasks that are performed at defined + intervals need to be performed. */ + + if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { + return; + } + + if (cur_time % SRV_MASTER_DICT_LRU_INTERVAL == 0) { + srv_main_thread_op_info = "enforcing dict cache limit"; + ulint n_evicted = srv_master_evict_from_table_cache(50); + if (n_evicted != 0) { + MONITOR_INC_VALUE( + MONITOR_SRV_DICT_LRU_EVICT_COUNT_ACTIVE, n_evicted); + } + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time); + } +} + +/*********************************************************************//** +Perform the tasks that the master thread is supposed to do whenever the +server is idle. We do check for the server state during this function +and if the server has entered the shutdown phase we may return from +the function without completing the required tasks. +Note that the server can move to active state when we are executing this +function but we don't check for that as we are suppose to perform more +or less same tasks when server is active. */ +static +void +srv_master_do_idle_tasks(void) +/*==========================*/ +{ + ++srv_main_idle_loops; + + MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS); + + + /* ALTER TABLE in MySQL requires on Unix that the table handler + can drop tables lazily after there no longer are SELECT + queries to them. */ + ulonglong counter_time = microsecond_interval_timer(); + srv_main_thread_op_info = "doing background drop tables"; + row_drop_tables_for_mysql_in_background(); + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, + counter_time); + + ut_d(srv_master_do_disabled_loop()); + + if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { + return; + } + + /* make sure that there is enough reusable space in the redo + log files */ + srv_main_thread_op_info = "checking free log space"; + log_free_check(); + + if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { + return; + } + + srv_main_thread_op_info = "enforcing dict cache limit"; + ulint n_evicted = srv_master_evict_from_table_cache(100); + if (n_evicted != 0) { + MONITOR_INC_VALUE( + MONITOR_SRV_DICT_LRU_EVICT_COUNT_IDLE, n_evicted); + } + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time); + + /* Flush logs if needed */ + srv_sync_log_buffer_in_background(); + MONITOR_INC_TIME_IN_MICRO_SECS( + MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time); +} + +/** +Complete the shutdown tasks such as background DROP TABLE, +and optionally change buffer merge (on innodb_fast_shutdown=0). */ +void srv_shutdown(bool ibuf_merge) +{ + ulint n_bytes_merged = 0; + ulint n_tables_to_drop; + time_t now = time(NULL); + + do { + ut_ad(!srv_read_only_mode); + ut_ad(srv_shutdown_state == SRV_SHUTDOWN_CLEANUP); + ++srv_main_shutdown_loops; + + /* FIXME: Remove the background DROP TABLE queue; it is not + crash-safe and breaks ACID. */ + srv_main_thread_op_info = "doing background drop tables"; + n_tables_to_drop = row_drop_tables_for_mysql_in_background(); + + if (ibuf_merge) { + srv_main_thread_op_info = "checking free log space"; + log_free_check(); + srv_main_thread_op_info = "doing insert buffer merge"; + n_bytes_merged = ibuf_merge_all(); + + /* Flush logs if needed */ + srv_sync_log_buffer_in_background(); + } + + /* Print progress message every 60 seconds during shutdown */ + if (srv_print_verbose_log) { + srv_shutdown_print_master_pending( + &now, n_tables_to_drop, n_bytes_merged); + } + } while (n_bytes_merged || n_tables_to_drop); +} + +/** The periodic master task controlling the server. */ +void srv_master_callback(void*) +{ + static ulint old_activity_count; + + ut_a(srv_shutdown_state <= SRV_SHUTDOWN_INITIATED); + + srv_main_thread_op_info = ""; + MONITOR_INC(MONITOR_MASTER_THREAD_SLEEP); + if (srv_check_activity(&old_activity_count)) { + srv_master_do_active_tasks(); + } else { + srv_master_do_idle_tasks(); + } + srv_main_thread_op_info = "sleeping"; +} + +/** @return whether purge should exit due to shutdown */ +static bool srv_purge_should_exit() +{ + ut_ad(srv_shutdown_state <= SRV_SHUTDOWN_CLEANUP); + + if (srv_undo_sources) + return false; + + if (srv_fast_shutdown) + return true; + + /* Slow shutdown was requested. */ + if (const uint32_t history_size= trx_sys.rseg_history_len) + { + static time_t progress_time; + time_t now= time(NULL); + if (now - progress_time >= 15) + { + progress_time= now; +#if defined HAVE_SYSTEMD && !defined EMBEDDED_LIBRARY + service_manager_extend_timeout(INNODB_EXTEND_TIMEOUT_INTERVAL, + "InnoDB: to purge %u transactions", + history_size); + ib::info() << "to purge " << history_size << " transactions"; +#endif + } + return false; + } + + return !trx_sys.any_active_transactions(); +} + +/*********************************************************************//** +Fetch and execute a task from the work queue. +@param [in,out] slot purge worker thread slot +@return true if a task was executed */ +static bool srv_task_execute() +{ + ut_ad(!srv_read_only_mode); + ut_ad(srv_force_recovery < SRV_FORCE_NO_BACKGROUND); + + mutex_enter(&srv_sys.tasks_mutex); + + if (que_thr_t* thr = UT_LIST_GET_FIRST(srv_sys.tasks)) { + ut_a(que_node_get_type(thr->child) == QUE_NODE_PURGE); + UT_LIST_REMOVE(srv_sys.tasks, thr); + mutex_exit(&srv_sys.tasks_mutex); + que_run_threads(thr); + return true; + } + + ut_ad(UT_LIST_GET_LEN(srv_sys.tasks) == 0); + mutex_exit(&srv_sys.tasks_mutex); + return false; +} + +std::mutex purge_thread_count_mtx; +void srv_update_purge_thread_count(uint n) +{ + std::lock_guard<std::mutex> lk(purge_thread_count_mtx); + srv_n_purge_threads = n; + srv_purge_thread_count_changed = 1; +} + +Atomic_counter<int> srv_purge_thread_count_changed; + +/** Do the actual purge operation. +@param[in,out] n_total_purged total number of purged pages +@return length of history list before the last purge batch. */ +static uint32_t srv_do_purge(ulint* n_total_purged) +{ + ulint n_pages_purged; + + static ulint count = 0; + static ulint n_use_threads = 0; + static uint32_t rseg_history_len = 0; + ulint old_activity_count = srv_get_activity_count(); + static ulint n_threads = srv_n_purge_threads; + + ut_a(n_threads > 0); + ut_ad(!srv_read_only_mode); + + /* Purge until there are no more records to purge and there is + no change in configuration or server state. If the user has + configured more than one purge thread then we treat that as a + pool of threads and only use the extra threads if purge can't + keep up with updates. */ + + if (n_use_threads == 0) { + n_use_threads = n_threads; + } + + do { + if (UNIV_UNLIKELY(srv_purge_thread_count_changed)) { + /* Read the fresh value of srv_n_purge_threads, reset + the changed flag. Both variables are protected by + purge_thread_count_mtx. + + This code does not run concurrently, it is executed + by a single purge_coordinator thread, and no races + involving srv_purge_thread_count_changed are possible. + */ + + std::lock_guard<std::mutex> lk(purge_thread_count_mtx); + n_threads = n_use_threads = srv_n_purge_threads; + srv_purge_thread_count_changed = 0; + } else if (trx_sys.rseg_history_len > rseg_history_len + || (srv_max_purge_lag > 0 + && rseg_history_len > srv_max_purge_lag)) { + + /* History length is now longer than what it was + when we took the last snapshot. Use more threads. */ + + if (n_use_threads < n_threads) { + ++n_use_threads; + } + + } else if (srv_check_activity(&old_activity_count) + && n_use_threads > 1) { + + /* History length same or smaller since last snapshot, + use fewer threads. */ + + --n_use_threads; + } + + /* Ensure that the purge threads are less than what + was configured. */ + + ut_a(n_use_threads > 0); + ut_a(n_use_threads <= n_threads); + + /* Take a snapshot of the history list before purge. */ + if (!(rseg_history_len = trx_sys.rseg_history_len)) { + break; + } + + n_pages_purged = trx_purge( + n_use_threads, + !(++count % srv_purge_rseg_truncate_frequency) + || purge_sys.truncate.current); + + *n_total_purged += n_pages_purged; + } while (n_pages_purged > 0 && !purge_sys.paused() + && !srv_purge_should_exit()); + + return(rseg_history_len); +} + + +static std::list<THD*> purge_thds; +static std::mutex purge_thd_mutex; +extern void* thd_attach_thd(THD*); +extern void thd_detach_thd(void *); + +THD* acquire_thd(void **ctx) +{ + std::unique_lock<std::mutex> lk(purge_thd_mutex); + if (purge_thds.empty()) { + THD* thd = current_thd; + purge_thds.push_back(innobase_create_background_thd("InnoDB purge worker")); + set_current_thd(thd); + } + THD* thd = purge_thds.front(); + purge_thds.pop_front(); + lk.unlock(); + + /* Set current thd, and thd->mysys_var as well, + it might be used by something in the server.*/ + *ctx = thd_attach_thd(thd); + return thd; +} + +void release_thd(THD *thd, void *ctx) +{ + thd_detach_thd(ctx); + std::unique_lock<std::mutex> lk(purge_thd_mutex); + purge_thds.push_back(thd); + lk.unlock(); + set_current_thd(0); +} + + +/* + Called by timer when purge coordinator decides + to delay processing of purge records. +*/ +static void purge_coordinator_timer_callback(void *) +{ + if (!purge_sys.enabled() || purge_sys.paused() || + purge_state.m_running || !trx_sys.rseg_history_len) + return; + + if (purge_state.m_history_length < 5000 && + purge_state.m_history_length == trx_sys.rseg_history_len) + /* No new records were added since wait started. + Simply wait for new records. The magic number 5000 is an + approximation for the case where we have cached UNDO + log records which prevent truncate of the UNDO segments.*/ + return; + srv_wake_purge_thread_if_not_active(); +} + +static void purge_worker_callback(void*) +{ + ut_ad(!current_thd); + ut_ad(!srv_read_only_mode); + ut_ad(srv_force_recovery < SRV_FORCE_NO_BACKGROUND); + void *ctx; + THD *thd= acquire_thd(&ctx); + while (srv_task_execute()) + ut_ad(purge_sys.running()); + release_thd(thd,ctx); +} + +static void purge_coordinator_callback_low() +{ + ulint n_total_purged= ULINT_UNDEFINED; + purge_state.m_history_length= 0; + + if (!purge_sys.enabled() || purge_sys.paused()) + return; + do + { + n_total_purged = 0; + int sigcount= purge_state.m_running; + + purge_state.m_history_length= srv_do_purge(&n_total_purged); + + /* Check if purge was woken by srv_wake_purge_thread_if_not_active() */ + + bool woken_during_purge= purge_state.m_running > sigcount; + + /* If last purge batch processed less than 1 page and there is + still work to do, delay the next batch by 10ms. Unless + someone added work and woke us up. */ + if (n_total_purged == 0) + { + if (trx_sys.rseg_history_len == 0) + return; + if (!woken_during_purge) + { + /* Delay next purge round*/ + purge_coordinator_timer->set_time(10, 0); + return; + } + } + } + while ((purge_sys.enabled() && !purge_sys.paused()) || + !srv_purge_should_exit()); +} + +static void purge_coordinator_callback(void*) +{ + void *ctx; + THD *thd= acquire_thd(&ctx); + purge_coordinator_callback_low(); + release_thd(thd,ctx); + purge_state.m_running= 0; +} + +void srv_init_purge_tasks() +{ + purge_coordinator_timer= srv_thread_pool->create_timer + (purge_coordinator_timer_callback, nullptr); +} + +static void srv_shutdown_purge_tasks() +{ + purge_coordinator_task.wait(); + delete purge_coordinator_timer; + purge_coordinator_timer= nullptr; + purge_worker_task.wait(); + while (!purge_thds.empty()) + { + innobase_destroy_background_thd(purge_thds.front()); + purge_thds.pop_front(); + } +} + +/**********************************************************************//** +Enqueues a task to server task queue and releases a worker thread, if there +is a suspended one. */ +void +srv_que_task_enqueue_low( +/*=====================*/ + que_thr_t* thr) /*!< in: query thread */ +{ + ut_ad(!srv_read_only_mode); + mutex_enter(&srv_sys.tasks_mutex); + + UT_LIST_ADD_LAST(srv_sys.tasks, thr); + + mutex_exit(&srv_sys.tasks_mutex); +} + +#ifdef UNIV_DEBUG +/** @return number of tasks in queue */ +ulint srv_get_task_queue_length() +{ + ulint n_tasks; + + ut_ad(!srv_read_only_mode); + + mutex_enter(&srv_sys.tasks_mutex); + + n_tasks = UT_LIST_GET_LEN(srv_sys.tasks); + + mutex_exit(&srv_sys.tasks_mutex); + + return(n_tasks); +} +#endif + +/** Shut down the purge threads. */ +void srv_purge_shutdown() +{ + if (purge_sys.enabled()) { + srv_update_purge_thread_count(innodb_purge_threads_MAX); + while(!srv_purge_should_exit()) { + ut_a(!purge_sys.paused()); + srv_wake_purge_thread_if_not_active(); + os_thread_sleep(1000); + } + purge_sys.coordinator_shutdown(); + srv_shutdown_purge_tasks(); + } +} diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc new file mode 100644 index 00000000..aa6e7ce1 --- /dev/null +++ b/storage/innobase/srv/srv0start.cc @@ -0,0 +1,2168 @@ +/***************************************************************************** + +Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2008, Google Inc. +Copyright (c) 2009, Percona Inc. +Copyright (c) 2013, 2021, MariaDB Corporation. + +Portions of this file contain modifications contributed and copyrighted by +Google, Inc. Those modifications are gratefully acknowledged and are described +briefly in the InnoDB documentation. The contributions by Google are +incorporated with their permission, and subject to the conditions contained in +the file COPYING.Google. + +Portions of this file contain modifications contributed and copyrighted +by Percona Inc.. Those modifications are +gratefully acknowledged and are described briefly in the InnoDB +documentation. The contributions by Percona Inc. are incorporated with +their permission, and subject to the conditions contained in the file +COPYING.Percona. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/********************************************************************//** +@file srv/srv0start.cc +Starts the InnoDB database server + +Created 2/16/1996 Heikki Tuuri +*************************************************************************/ + +#include "my_global.h" + +#include "mysqld.h" +#include "mysql/psi/mysql_stage.h" +#include "mysql/psi/psi.h" + +#include "row0ftsort.h" +#include "ut0mem.h" +#include "mem0mem.h" +#include "data0data.h" +#include "data0type.h" +#include "dict0dict.h" +#include "buf0buf.h" +#include "buf0dblwr.h" +#include "buf0dump.h" +#include "os0file.h" +#include "os0thread.h" +#include "fil0fil.h" +#include "fil0crypt.h" +#include "fsp0fsp.h" +#include "rem0rec.h" +#include "mtr0mtr.h" +#include "log0crypt.h" +#include "log0recv.h" +#include "page0page.h" +#include "page0cur.h" +#include "trx0trx.h" +#include "trx0sys.h" +#include "btr0btr.h" +#include "btr0cur.h" +#include "rem0rec.h" +#include "ibuf0ibuf.h" +#include "srv0start.h" +#include "srv0srv.h" +#include "btr0defragment.h" +#include "mysql/service_wsrep.h" /* wsrep_recovery */ +#include "trx0rseg.h" +#include "buf0flu.h" +#include "buf0rea.h" +#include "dict0boot.h" +#include "dict0load.h" +#include "dict0stats_bg.h" +#include "que0que.h" +#include "lock0lock.h" +#include "trx0roll.h" +#include "trx0purge.h" +#include "lock0lock.h" +#include "pars0pars.h" +#include "btr0sea.h" +#include "rem0cmp.h" +#include "dict0crea.h" +#include "row0ins.h" +#include "row0sel.h" +#include "row0upd.h" +#include "row0row.h" +#include "row0mysql.h" +#include "btr0pcur.h" +#include "os0event.h" +#include "zlib.h" +#include "ut0crc32.h" + +/** We are prepared for a situation that we have this many threads waiting for +a semaphore inside InnoDB. srv_start() sets the value. */ +ulint srv_max_n_threads; + +/** Log sequence number at shutdown */ +lsn_t srv_shutdown_lsn; + +/** TRUE if a raw partition is in use */ +ibool srv_start_raw_disk_in_use; + +/** Number of IO threads to use */ +uint srv_n_file_io_threads; + +/** UNDO tablespaces starts with space id. */ +ulint srv_undo_space_id_start; + +/** TRUE if the server is being started, before rolling back any +incomplete transactions */ +bool srv_startup_is_before_trx_rollback_phase; +/** TRUE if the server is being started */ +bool srv_is_being_started; +/** TRUE if SYS_TABLESPACES is available for lookups */ +bool srv_sys_tablespaces_open; +/** TRUE if the server was successfully started */ +bool srv_was_started; +/** The original value of srv_log_file_size (innodb_log_file_size) */ +static ulonglong srv_log_file_size_requested; +/** whether srv_start() has been called */ +static bool srv_start_has_been_called; + +/** Whether any undo log records can be generated */ +UNIV_INTERN bool srv_undo_sources; + +#ifdef UNIV_DEBUG +/** InnoDB system tablespace to set during recovery */ +UNIV_INTERN uint srv_sys_space_size_debug; +/** whether redo log file have been created at startup */ +UNIV_INTERN bool srv_log_file_created; +#endif /* UNIV_DEBUG */ + +/** whether some background threads that create redo log have been started */ +static bool srv_started_redo; + +/** At a shutdown this value climbs from SRV_SHUTDOWN_NONE to +SRV_SHUTDOWN_CLEANUP and then to SRV_SHUTDOWN_LAST_PHASE, and so on */ +enum srv_shutdown_t srv_shutdown_state = SRV_SHUTDOWN_NONE; + +/** Name of srv_monitor_file */ +static char* srv_monitor_file_name; +std::unique_ptr<tpool::timer> srv_master_timer; + +/** */ +#define SRV_MAX_N_PENDING_SYNC_IOS 100 + +#ifdef UNIV_PFS_THREAD +/* Keys to register InnoDB threads with performance schema */ +mysql_pfs_key_t thread_pool_thread_key; +#endif /* UNIV_PFS_THREAD */ + +#ifdef HAVE_PSI_STAGE_INTERFACE +/** Array of all InnoDB stage events for monitoring activities via +performance schema. */ +static PSI_stage_info* srv_stages[] = +{ + &srv_stage_alter_table_end, + &srv_stage_alter_table_insert, + &srv_stage_alter_table_log_index, + &srv_stage_alter_table_log_table, + &srv_stage_alter_table_merge_sort, + &srv_stage_alter_table_read_pk_internal_sort, + &srv_stage_buffer_pool_load, +}; +#endif /* HAVE_PSI_STAGE_INTERFACE */ + +/*********************************************************************//** +Check if a file can be opened in read-write mode. +@return true if it doesn't exist or can be opened in rw mode. */ +static +bool +srv_file_check_mode( +/*================*/ + const char* name) /*!< in: filename to check */ +{ + os_file_stat_t stat; + + memset(&stat, 0x0, sizeof(stat)); + + dberr_t err = os_file_get_status( + name, &stat, true, srv_read_only_mode); + + if (err == DB_FAIL) { + ib::error() << "os_file_get_status() failed on '" << name + << "'. Can't determine file permissions."; + return(false); + + } else if (err == DB_SUCCESS) { + + /* Note: stat.rw_perm is only valid of files */ + + if (stat.type == OS_FILE_TYPE_FILE) { + + if (!stat.rw_perm) { + const char* mode = srv_read_only_mode + ? "read" : "read-write"; + ib::error() << name << " can't be opened in " + << mode << " mode."; + return(false); + } + } else { + /* Not a regular file, bail out. */ + ib::error() << "'" << name << "' not a regular file."; + + return(false); + } + } else { + + /* This is OK. If the file create fails on RO media, there + is nothing we can do. */ + + ut_a(err == DB_NOT_FOUND); + } + + return(true); +} + +/** Initial number of the redo log file */ +static const char INIT_LOG_FILE0[]= "101"; + +/** Creates log file. +@param[in] create_new_db whether the database is being initialized +@param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value +@param[out] logfile0 name of the log file +@return DB_SUCCESS or error code */ +static dberr_t create_log_file(bool create_new_db, lsn_t lsn, + std::string& logfile0) +{ + if (srv_read_only_mode) { + ib::error() << "Cannot create log file in read-only mode"; + return DB_READ_ONLY; + } + + /* Crashing after deleting the first file should be + recoverable. The buffer pool was clean, and we can simply + create log file from the scratch. */ + DBUG_EXECUTE_IF("innodb_log_abort_6", delete_log_file("0"); + return DB_ERROR;); + + for (size_t i = 0; i < 102; i++) { + delete_log_file(std::to_string(i).c_str()); + } + + DBUG_PRINT("ib_log", ("After innodb_log_abort_6")); + DBUG_ASSERT(!buf_pool.any_io_pending()); + + DBUG_EXECUTE_IF("innodb_log_abort_7", return DB_ERROR;); + DBUG_PRINT("ib_log", ("After innodb_log_abort_7")); + + logfile0 = get_log_file_path(LOG_FILE_NAME_PREFIX) + .append(INIT_LOG_FILE0); + + bool ret; + pfs_os_file_t file = os_file_create( + innodb_log_file_key, logfile0.c_str(), + OS_FILE_CREATE|OS_FILE_ON_ERROR_NO_EXIT, OS_FILE_NORMAL, + OS_LOG_FILE, srv_read_only_mode, &ret); + + if (!ret) { + ib::error() << "Cannot create " << logfile0; + return DB_ERROR; + } + + ib::info() << "Setting log file " << logfile0 << " size to " + << srv_log_file_size << " bytes"; + + ret = os_file_set_size(logfile0.c_str(), file, srv_log_file_size); + if (!ret) { + os_file_close(file); + ib::error() << "Cannot set log file " << logfile0 + << " size to " << srv_log_file_size << " bytes"; + return DB_ERROR; + } + + ret = os_file_close(file); + ut_a(ret); + + DBUG_EXECUTE_IF("innodb_log_abort_8", return(DB_ERROR);); + DBUG_PRINT("ib_log", ("After innodb_log_abort_8")); + + /* We did not create the first log file initially as LOG_FILE_NAME, so + that crash recovery cannot find it until it has been completed and + renamed. */ + + log_sys.log.create(); + if (!log_set_capacity(srv_log_file_size_requested)) { + return DB_ERROR; + } + + log_sys.log.open_file(logfile0); + if (!fil_system.sys_space->open(create_new_db)) { + return DB_ERROR; + } + + /* Create a log checkpoint. */ + mysql_mutex_lock(&log_sys.mutex); + if (log_sys.is_encrypted() && !log_crypt_init()) { + return DB_ERROR; + } + ut_d(recv_no_log_write = false); + lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE); + log_sys.set_lsn(lsn + LOG_BLOCK_HDR_SIZE); + log_sys.log.set_lsn(lsn); + log_sys.log.set_lsn_offset(LOG_FILE_HDR_SIZE); + + log_sys.buf_next_to_write = 0; + log_sys.write_lsn = lsn; + + log_sys.next_checkpoint_no = 0; + log_sys.last_checkpoint_lsn = 0; + + memset(log_sys.buf, 0, srv_log_buffer_size); + log_block_init(log_sys.buf, lsn); + log_block_set_first_rec_group(log_sys.buf, LOG_BLOCK_HDR_SIZE); + memset(log_sys.flush_buf, 0, srv_log_buffer_size); + + log_sys.buf_free = LOG_BLOCK_HDR_SIZE; + + log_sys.log.write_header_durable(lsn); + + mysql_mutex_unlock(&log_sys.mutex); + + log_make_checkpoint(); + log_buffer_flush_to_disk(); + + return DB_SUCCESS; +} + +/** Rename the first redo log file. +@param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value +@param[in,out] logfile0 name of the first log file +@return error code +@retval DB_SUCCESS on successful operation */ +MY_ATTRIBUTE((warn_unused_result)) +static dberr_t create_log_file_rename(lsn_t lsn, std::string &logfile0) +{ + ut_ad(!srv_log_file_created); + ut_d(srv_log_file_created= true); + + DBUG_EXECUTE_IF("innodb_log_abort_9", return (DB_ERROR);); + DBUG_PRINT("ib_log", ("After innodb_log_abort_9")); + + /* Rename the first log file, now that a log checkpoint has been created. */ + auto new_name = get_log_file_path(); + + ib::info() << "Renaming log file " << logfile0 << " to " << new_name; + + mysql_mutex_lock(&log_sys.mutex); + ut_ad(logfile0.size() == 2 + new_name.size()); + logfile0= new_name; + dberr_t err= log_sys.log.rename(std::move(new_name)); + + mysql_mutex_unlock(&log_sys.mutex); + + DBUG_EXECUTE_IF("innodb_log_abort_10", err= DB_ERROR;); + + if (err == DB_SUCCESS) + ib::info() << "New log file created, LSN=" << lsn; + + return err; +} + +/** Create an undo tablespace file +@param[in] name file name +@return DB_SUCCESS or error code */ +static dberr_t srv_undo_tablespace_create(const char* name) +{ + pfs_os_file_t fh; + bool ret; + dberr_t err = DB_SUCCESS; + + os_file_create_subdirs_if_needed(name); + + fh = os_file_create( + innodb_data_file_key, + name, + srv_read_only_mode ? OS_FILE_OPEN : OS_FILE_CREATE, + OS_FILE_NORMAL, OS_DATA_FILE, srv_read_only_mode, &ret); + + if (!ret) { + if (os_file_get_last_error(false) != OS_FILE_ALREADY_EXISTS +#ifdef UNIV_AIX + /* AIX 5.1 after security patch ML7 may have + errno set to 0 here, which causes our function + to return 100; work around that AIX problem */ + && os_file_get_last_error(false) != 100 +#endif /* UNIV_AIX */ + ) { + ib::error() << "Can't create UNDO tablespace " + << name; + } + err = DB_ERROR; + } else if (srv_read_only_mode) { + ib::info() << name << " opened in read-only mode"; + } else { + /* We created the data file and now write it full of zeros */ + + ib::info() << "Data file " << name << " did not exist: new to" + " be created"; + + ib::info() << "Setting file " << name << " size to " + << (SRV_UNDO_TABLESPACE_SIZE_IN_PAGES >> (20 - srv_page_size_shift)) << " MB"; + + ib::info() << "Database physically writes the file full: " + << "wait..."; + + if (!os_file_set_size(name, fh, os_offset_t + {SRV_UNDO_TABLESPACE_SIZE_IN_PAGES} + << srv_page_size_shift)) { + ib::error() << "Unable to allocate " << name; + err = DB_ERROR; + } + + os_file_close(fh); + } + + return(err); +} + +/* Validate the number of undo opened undo tablespace and user given +undo tablespace +@return DB_SUCCESS if it is valid */ +static dberr_t srv_validate_undo_tablespaces() +{ + /* If the user says that there are fewer than what we find we + tolerate that discrepancy but not the inverse. Because there could + be unused undo tablespaces for future use. */ + + if (srv_undo_tablespaces > srv_undo_tablespaces_open) + { + ib::error() << "Expected to open innodb_undo_tablespaces=" + << srv_undo_tablespaces + << " but was able to find only " + << srv_undo_tablespaces_open; + + return DB_ERROR; + } + else if (srv_undo_tablespaces_open > 0) + { + ib::info() << "Opened " << srv_undo_tablespaces_open + << " undo tablespaces"; + + if (srv_undo_tablespaces == 0) + ib::warn() << "innodb_undo_tablespaces=0 disables" + " dedicated undo log tablespaces"; + } + return DB_SUCCESS; +} + +/** @return the number of active undo tablespaces (except system tablespace) */ +static ulint trx_rseg_get_n_undo_tablespaces() +{ + std::set<uint32_t> space_ids; + mtr_t mtr; + mtr.start(); + + if (const buf_block_t *sys_header= trx_sysf_get(&mtr, false)) + for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) + if (trx_sysf_rseg_get_page_no(sys_header, rseg_id) != FIL_NULL) + if (uint32_t space= trx_sysf_rseg_get_space(sys_header, rseg_id)) + space_ids.insert(space); + mtr.commit(); + return space_ids.size(); +} + +/** Open an undo tablespace. +@param[in] create whether undo tablespaces are being created +@param[in] name tablespace file name +@param[in] i undo tablespace count +@return undo tablespace identifier +@retval 0 on failure */ +static ulint srv_undo_tablespace_open(bool create, const char* name, ulint i) +{ + bool success; + char undo_name[sizeof "innodb_undo000"]; + ulint space_id= 0; + ulint fsp_flags= 0; + + if (create) + { + space_id= srv_undo_space_id_start + i; + snprintf(undo_name, sizeof(undo_name), + "innodb_undo%03u", static_cast<unsigned>(space_id)); + switch (srv_checksum_algorithm) { + case SRV_CHECKSUM_ALGORITHM_FULL_CRC32: + case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32: + fsp_flags= FSP_FLAGS_FCRC32_MASK_MARKER | FSP_FLAGS_FCRC32_PAGE_SSIZE(); + break; + default: + fsp_flags= FSP_FLAGS_PAGE_SSIZE(); + } + } + + pfs_os_file_t fh= os_file_create(innodb_data_file_key, name, OS_FILE_OPEN | + OS_FILE_ON_ERROR_NO_EXIT | + OS_FILE_ON_ERROR_SILENT, + OS_FILE_AIO, OS_DATA_FILE, + srv_read_only_mode, &success); + + if (!success) + return 0; + + os_offset_t size= os_file_get_size(fh); + ut_a(size != os_offset_t(-1)); + + if (!create) + { + page_t *page= static_cast<byte*>(aligned_malloc(srv_page_size, + srv_page_size)); + dberr_t err= os_file_read(IORequestRead, fh, page, 0, srv_page_size); + if (err != DB_SUCCESS) + { +err_exit: + ib::error() << "Unable to read first page of file " << name; + aligned_free(page); + return err; + } + + uint32_t id= mach_read_from_4(FIL_PAGE_SPACE_ID + page); + if (id == 0 || id >= SRV_SPACE_ID_UPPER_BOUND || + memcmp_aligned<2>(FIL_PAGE_SPACE_ID + page, + FSP_HEADER_OFFSET + FSP_SPACE_ID + page, 4)) + { + ib::error() << "Inconsistent tablespace ID in file " << name; + err= DB_CORRUPTION; + goto err_exit; + } + + fsp_flags= mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page); + if (buf_page_is_corrupted(false, page, fsp_flags)) + { + ib::error() << "Checksum mismatch in the first page of file " << name; + err= DB_CORRUPTION; + goto err_exit; + } + + space_id= id; + snprintf(undo_name, sizeof undo_name, "innodb_undo%03u", id); + aligned_free(page); + } + + /* Load the tablespace into InnoDB's internal data structures. */ + + /* We set the biggest space id to the undo tablespace + because InnoDB hasn't opened any other tablespace apart + from the system tablespace. */ + + fil_set_max_space_id_if_bigger(space_id); + + fil_space_t *space= fil_space_t::create(undo_name, space_id, fsp_flags, + FIL_TYPE_TABLESPACE, NULL); + ut_a(fil_validate()); + ut_a(space); + + fil_node_t *file= space->add(name, fh, 0, false, true); + mutex_enter(&fil_system.mutex); + + if (create) + { + space->set_sizes(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES); + space->size= file->size= uint32_t(size >> srv_page_size_shift); + } + else if (!file->read_page0()) + { + os_file_close(file->handle); + file->handle= OS_FILE_CLOSED; + ut_a(fil_system.n_open > 0); + fil_system.n_open--; + } + + mutex_exit(&fil_system.mutex); + return space_id; +} + +/** Check if undo tablespaces and redo log files exist before creating a +new system tablespace +@retval DB_SUCCESS if all undo and redo logs are not found +@retval DB_ERROR if any undo and redo logs are found */ +static +dberr_t +srv_check_undo_redo_logs_exists() +{ + bool ret; + pfs_os_file_t fh; + char name[OS_FILE_MAX_PATH]; + + /* Check if any undo tablespaces exist */ + for (ulint i = 1; i <= srv_undo_tablespaces; ++i) { + + snprintf( + name, sizeof(name), + "%s%cundo%03zu", + srv_undo_dir, OS_PATH_SEPARATOR, + i); + + fh = os_file_create( + innodb_data_file_key, name, + OS_FILE_OPEN_RETRY + | OS_FILE_ON_ERROR_NO_EXIT + | OS_FILE_ON_ERROR_SILENT, + OS_FILE_NORMAL, + OS_DATA_FILE, + srv_read_only_mode, + &ret); + + if (ret) { + os_file_close(fh); + ib::error() + << "undo tablespace '" << name << "' exists." + " Creating system tablespace with existing undo" + " tablespaces is not supported. Please delete" + " all undo tablespaces before creating new" + " system tablespace."; + return(DB_ERROR); + } + } + + /* Check if redo log file exists */ + auto logfilename = get_log_file_path(); + + fh = os_file_create(innodb_log_file_key, logfilename.c_str(), + OS_FILE_OPEN_RETRY | OS_FILE_ON_ERROR_NO_EXIT + | OS_FILE_ON_ERROR_SILENT, + OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, + &ret); + + if (ret) { + os_file_close(fh); + ib::error() << "redo log file '" << logfilename + << "' exists. Creating system tablespace with" + " existing redo log file is not recommended." + " Please delete redo log file before" + " creating new system tablespace."; + return DB_ERROR; + } + + return(DB_SUCCESS); +} + +static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, ulint n_undo) +{ + /* Open all the undo tablespaces that are currently in use. If we + fail to open any of these it is a fatal error. The tablespace ids + should be contiguous. It is a fatal error because they are required + for recovery and are referenced by the UNDO logs (a.k.a RBS). */ + + ulint prev_id= create_new_db ? srv_undo_space_id_start - 1 : 0; + + for (ulint i= 0; i < n_undo; ++i) + { + char name[OS_FILE_MAX_PATH]; + snprintf(name, sizeof name, "%s%cundo%03zu", srv_undo_dir, + OS_PATH_SEPARATOR, i + 1); + ulint space_id= srv_undo_tablespace_open(create_new_db, name, i); + if (!space_id) + { + if (!create_new_db) + break; + ib::error() << "Unable to open create tablespace '" << name << "'."; + return DB_ERROR; + } + + /* Should be no gaps in undo tablespace ids. */ + ut_a(!i || prev_id + 1 == space_id); + + prev_id= space_id; + + /* Note the first undo tablespace id in case of + no active undo tablespace. */ + if (0 == srv_undo_tablespaces_open++) + srv_undo_space_id_start= space_id; + } + + /* Open any extra unused undo tablespaces. These must be contiguous. + We stop at the first failure. These are undo tablespaces that are + not in use and therefore not required by recovery. We only check + that there are no gaps. */ + + for (ulint i= prev_id + 1; i < srv_undo_space_id_start + TRX_SYS_N_RSEGS; + ++i) + { + char name[OS_FILE_MAX_PATH]; + snprintf(name, sizeof(name), + "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i); + if (!srv_undo_tablespace_open(create_new_db, name, i)) + break; + ++srv_undo_tablespaces_open; + } + + return srv_validate_undo_tablespaces(); +} + +/** Open the configured number of dedicated undo tablespaces. +@param[in] create_new_db whether the database is being initialized +@return DB_SUCCESS or error code */ +dberr_t +srv_undo_tablespaces_init(bool create_new_db) +{ + srv_undo_tablespaces_open= 0; + + ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); + ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL); + + if (srv_undo_tablespaces == 1) + srv_undo_tablespaces= 0; + + /* Create the undo spaces only if we are creating a new + instance. We don't allow creating of new undo tablespaces + in an existing instance (yet). */ + if (create_new_db) + { + srv_undo_space_id_start= 1; + DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;); + + for (ulint i= 0; i < srv_undo_tablespaces; ++i) + { + char name[OS_FILE_MAX_PATH]; + snprintf(name, sizeof name, "%s%cundo%03zu", + srv_undo_dir, OS_PATH_SEPARATOR, i + 1); + if (dberr_t err= srv_undo_tablespace_create(name)) + { + ib::error() << "Could not create undo tablespace '" << name << "'."; + return err; + } + } + } + + /* Get the tablespace ids of all the undo segments excluding + the system tablespace (0). If we are creating a new instance then + we build the undo_tablespace_ids ourselves since they don't + already exist. */ + srv_undo_tablespaces_active= srv_undo_tablespaces; + + ulint n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP || + srv_operation == SRV_OPERATION_RESTORE_DELTA) + ? srv_undo_tablespaces : TRX_SYS_N_RSEGS; + + if (dberr_t err= srv_all_undo_tablespaces_open(create_new_db, n_undo)) + return err; + + /* Initialize srv_undo_space_id_start=0 when there are no + dedicated undo tablespaces. */ + if (srv_undo_tablespaces_open == 0) + srv_undo_space_id_start= 0; + + if (create_new_db) + { + mtr_t mtr; + for (ulint i= 0; i < srv_undo_tablespaces; ++i) + { + mtr.start(); + fsp_header_init(fil_space_get(srv_undo_space_id_start + i), + SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); + mtr.commit(); + } + } + + return DB_SUCCESS; +} + +/** Create the temporary file tablespace. +@param[in] create_new_db whether we are creating a new database +@return DB_SUCCESS or error code. */ +static +dberr_t +srv_open_tmp_tablespace(bool create_new_db) +{ + ulint sum_of_new_sizes; + + /* Will try to remove if there is existing file left-over by last + unclean shutdown */ + srv_tmp_space.set_sanity_check_status(true); + srv_tmp_space.delete_files(); + srv_tmp_space.set_ignore_read_only(true); + + ib::info() << "Creating shared tablespace for temporary tables"; + + bool create_new_temp_space; + + srv_tmp_space.set_space_id(SRV_TMP_SPACE_ID); + + dberr_t err = srv_tmp_space.check_file_spec( + &create_new_temp_space, 12 * 1024 * 1024); + + if (err == DB_FAIL) { + ib::error() << "The innodb_temporary" + " data file must be writable!"; + err = DB_ERROR; + } else if (err != DB_SUCCESS) { + ib::error() << "Could not create the shared innodb_temporary."; + } else if ((err = srv_tmp_space.open_or_create( + true, create_new_db, &sum_of_new_sizes, NULL)) + != DB_SUCCESS) { + ib::error() << "Unable to create the shared innodb_temporary"; + } else if (fil_system.temp_space->open(true)) { + /* Initialize the header page */ + mtr_t mtr; + mtr.start(); + mtr.set_log_mode(MTR_LOG_NO_REDO); + fsp_header_init(fil_system.temp_space, + srv_tmp_space.get_sum_of_sizes(), + &mtr); + mtr.commit(); + } else { + /* This file was just opened in the code above! */ + ib::error() << "The innodb_temporary" + " data file cannot be re-opened" + " after check_file_spec() succeeded!"; + err = DB_ERROR; + } + + return(err); +} + +/** Shutdown background threads, except the page cleaner. */ +static void srv_shutdown_threads() +{ + ut_ad(!srv_undo_sources); + srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; + + lock_sys.timeout_timer.reset(); + srv_master_timer.reset(); + + if (purge_sys.enabled()) { + srv_purge_shutdown(); + } + + if (srv_n_fil_crypt_threads) { + fil_crypt_set_thread_cnt(0); + } +} + +#ifdef UNIV_DEBUG +# define srv_init_abort(_db_err) \ + srv_init_abort_low(create_new_db, __FILE__, __LINE__, _db_err) +#else +# define srv_init_abort(_db_err) \ + srv_init_abort_low(create_new_db, _db_err) +#endif /* UNIV_DEBUG */ + +/** Innobase start-up aborted. Perform cleanup actions. +@param[in] create_new_db TRUE if new db is being created +@param[in] file File name +@param[in] line Line number +@param[in] err Reason for aborting InnoDB startup +@return DB_SUCCESS or error code. */ +MY_ATTRIBUTE((warn_unused_result, nonnull)) +static +dberr_t +srv_init_abort_low( + bool create_new_db, +#ifdef UNIV_DEBUG + const char* file, + unsigned line, +#endif /* UNIV_DEBUG */ + dberr_t err) +{ + ut_ad(srv_is_being_started); + + if (create_new_db) { + ib::error() << "Database creation was aborted" +#ifdef UNIV_DEBUG + " at " << innobase_basename(file) << "[" << line << "]" +#endif /* UNIV_DEBUG */ + " with error " << err << ". You may need" + " to delete the ibdata1 file before trying to start" + " up again."; + } else { + ib::error() << "Plugin initialization aborted" +#ifdef UNIV_DEBUG + " at " << innobase_basename(file) << "[" << line << "]" +#endif /* UNIV_DEBUG */ + " with error " << err; + } + + srv_shutdown_bg_undo_sources(); + srv_shutdown_threads(); + return(err); +} + +/** Prepare to delete the redo log file. Flush the dirty pages from all the +buffer pools. Flush the redo log buffer to the redo log file. +@param[in] old_exists old redo log file exists +@return lsn upto which data pages have been flushed. */ +static lsn_t srv_prepare_to_delete_redo_log_file(bool old_exists) +{ + DBUG_ENTER("srv_prepare_to_delete_redo_log_file"); + + lsn_t flushed_lsn; + ulint count = 0; + + if (log_sys.log.subformat != 2) { + srv_log_file_size = 0; + } + + for (;;) { + /* Clean the buffer pool. */ + buf_flush_sync(); + + DBUG_EXECUTE_IF("innodb_log_abort_1", DBUG_RETURN(0);); + DBUG_PRINT("ib_log", ("After innodb_log_abort_1")); + + mysql_mutex_lock(&log_sys.mutex); + + fil_names_clear(log_sys.get_lsn(), false); + + flushed_lsn = log_sys.get_lsn(); + + { + ib::info info; + if (srv_log_file_size == 0 + || (log_sys.log.format & ~log_t::FORMAT_ENCRYPTED) + != log_t::FORMAT_10_5) { + info << "Upgrading redo log: "; + } else if (!old_exists + || srv_log_file_size + != srv_log_file_size_requested) { + if (srv_encrypt_log + == (my_bool)log_sys.is_encrypted()) { + info << (srv_encrypt_log + ? "Resizing encrypted" + : "Resizing"); + } else if (srv_encrypt_log) { + info << "Encrypting and resizing"; + } else { + info << "Removing encryption" + " and resizing"; + } + + info << " redo log from " << srv_log_file_size + << " to "; + } else if (srv_encrypt_log) { + info << "Encrypting redo log: "; + } else { + info << "Removing redo log encryption: "; + } + + info << srv_log_file_size_requested + << " bytes; LSN=" << flushed_lsn; + } + + mysql_mutex_unlock(&log_sys.mutex); + + if (flushed_lsn != log_sys.get_flushed_lsn()) { + log_write_up_to(flushed_lsn, false); + log_sys.log.flush(); + } + + ut_ad(flushed_lsn == log_sys.get_lsn()); + + /* Check if the buffer pools are clean. If not + retry till it is clean. */ + if (ulint pending_io = buf_pool.io_pending()) { + count++; + /* Print a message every 60 seconds if we + are waiting to clean the buffer pools */ + if (srv_print_verbose_log && count > 600) { + ib::info() << "Waiting for " + << pending_io << " buffer " + << "page I/Os to complete"; + count = 0; + } + + os_thread_sleep(100000); + continue; + } + + break; + } + + DBUG_RETURN(flushed_lsn); +} + +/** Tries to locate LOG_FILE_NAME and check it's size, etc +@param[out] log_file_found returns true here if correct file was found +@return dberr_t with DB_SUCCESS or some error */ +static dberr_t find_and_check_log_file(bool &log_file_found) +{ + log_file_found= false; + + auto logfile0= get_log_file_path(); + os_file_stat_t stat_info; + const dberr_t err= os_file_get_status(logfile0.c_str(), &stat_info, false, + srv_read_only_mode); + + auto is_operation_restore= []() -> bool { + return srv_operation == SRV_OPERATION_RESTORE || + srv_operation == SRV_OPERATION_RESTORE_EXPORT; + }; + + if (err == DB_NOT_FOUND) + { + if (is_operation_restore()) + return DB_NOT_FOUND; + + return DB_SUCCESS; + } + + if (stat_info.type != OS_FILE_TYPE_FILE) + return DB_SUCCESS; + + if (!srv_file_check_mode(logfile0.c_str())) + return DB_ERROR; + + const os_offset_t size= stat_info.size; + ut_a(size != (os_offset_t) -1); + + if (size % OS_FILE_LOG_BLOCK_SIZE) + { + ib::error() << "Log file " << logfile0 << " size " << size + << " is not a multiple of " << OS_FILE_LOG_BLOCK_SIZE + << " bytes"; + return DB_ERROR; + } + + if (size == 0 && is_operation_restore()) + { + /* Tolerate an empty LOG_FILE_NAME from a previous run of + mariabackup --prepare. */ + return DB_NOT_FOUND; + } + /* The first log file must consist of at least the following 512-byte pages: + header, checkpoint page 1, empty, checkpoint page 2, redo log page(s). + + Mariabackup --prepare would create an empty LOG_FILE_NAME. Tolerate it. */ + if (size != 0 && size <= OS_FILE_LOG_BLOCK_SIZE * 4) + { + ib::error() << "Log file " << logfile0 << " size " << size + << " is too small"; + return DB_ERROR; + } + srv_log_file_size= size; + + log_file_found= true; + return DB_SUCCESS; +} + +/** Start InnoDB. +@param[in] create_new_db whether to create a new database +@return DB_SUCCESS or error code */ +dberr_t srv_start(bool create_new_db) +{ + lsn_t flushed_lsn; + dberr_t err = DB_SUCCESS; + bool srv_log_file_found = true; + mtr_t mtr; + + ut_ad(srv_operation == SRV_OPERATION_NORMAL + || srv_operation == SRV_OPERATION_RESTORE + || srv_operation == SRV_OPERATION_RESTORE_EXPORT); + + if (srv_force_recovery) { + ib::info() << "!!! innodb_force_recovery is set to " + << srv_force_recovery << " !!!"; + } + + if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) { + srv_read_only_mode = true; + } + + high_level_read_only = srv_read_only_mode + || srv_force_recovery > SRV_FORCE_NO_IBUF_MERGE + || srv_sys_space.created_new_raw(); + + srv_started_redo = false; + + compile_time_assert(sizeof(ulint) == sizeof(void*)); + +#ifdef UNIV_DEBUG + ib::info() << "!!!!!!!! UNIV_DEBUG switched on !!!!!!!!!"; +#endif + +#ifdef UNIV_IBUF_DEBUG + ib::info() << "!!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!"; +#endif + + ib::info() << MUTEX_TYPE; + + ib::info() << "Compressed tables use zlib " ZLIB_VERSION +#ifdef UNIV_ZIP_DEBUG + " with validation" +#endif /* UNIV_ZIP_DEBUG */ + ; +#ifdef UNIV_ZIP_COPY + ib::info() << "and extra copying"; +#endif /* UNIV_ZIP_COPY */ + + /* Since InnoDB does not currently clean up all its internal data + structures in MySQL Embedded Server Library server_end(), we + print an error message if someone tries to start up InnoDB a + second time during the process lifetime. */ + + if (srv_start_has_been_called) { + ib::error() << "Startup called second time" + " during the process lifetime." + " In the MySQL Embedded Server Library" + " you cannot call server_init() more than" + " once during the process lifetime."; + } + + srv_start_has_been_called = true; + + srv_is_being_started = true; + + /* Register performance schema stages before any real work has been + started which may need to be instrumented. */ + mysql_stage_register("innodb", srv_stages, + static_cast<int>(UT_ARR_SIZE(srv_stages))); + + /* Set the maximum number of threads which can wait for a semaphore + inside InnoDB: this is the 'sync wait array' size */ + + srv_max_n_threads = 1 /* io_ibuf_thread */ + + 1 /* io_log_thread */ + + 1 /* srv_print_monitor_task */ + + 1 /* srv_purge_coordinator_thread */ + + 1 /* buf_dump_thread */ + + 1 /* dict_stats_thread */ + + 1 /* fts_optimize_thread */ + + 1 /* trx_rollback_all_recovered */ + + 128 /* added as margin, for use of + InnoDB Memcached etc. */ + + 1/* buf_flush_page_cleaner */ + + max_connections + + srv_n_read_io_threads + + srv_n_write_io_threads + + srv_n_purge_threads + /* FTS Parallel Sort */ + + fts_sort_pll_degree * FTS_NUM_AUX_INDEX + * max_connections; + + srv_boot(); + + ib::info() << my_crc32c_implementation(); + + if (!srv_read_only_mode) { + + mutex_create(LATCH_ID_SRV_MONITOR_FILE, + &srv_monitor_file_mutex); + + if (srv_innodb_status) { + + srv_monitor_file_name = static_cast<char*>( + ut_malloc_nokey( + strlen(fil_path_to_mysql_datadir) + + 20 + sizeof "/innodb_status.")); + + sprintf(srv_monitor_file_name, + "%s/innodb_status." ULINTPF, + fil_path_to_mysql_datadir, + static_cast<ulint> + (IF_WIN(GetCurrentProcessId(), getpid()))); + + srv_monitor_file = my_fopen(srv_monitor_file_name, + O_RDWR|O_TRUNC|O_CREAT, + MYF(MY_WME)); + + if (!srv_monitor_file) { + ib::error() << "Unable to create " + << srv_monitor_file_name << ": " + << strerror(errno); + if (err == DB_SUCCESS) { + err = DB_ERROR; + } + } + } else { + + srv_monitor_file_name = NULL; + srv_monitor_file = os_file_create_tmpfile(); + + if (!srv_monitor_file && err == DB_SUCCESS) { + err = DB_ERROR; + } + } + + mutex_create(LATCH_ID_SRV_MISC_TMPFILE, + &srv_misc_tmpfile_mutex); + + srv_misc_tmpfile = os_file_create_tmpfile(); + + if (!srv_misc_tmpfile && err == DB_SUCCESS) { + err = DB_ERROR; + } + } + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + srv_n_file_io_threads = srv_n_read_io_threads + srv_n_write_io_threads; + + if (!srv_read_only_mode) { + /* Add the log and ibuf IO threads. */ + srv_n_file_io_threads += 2; + } else { + ib::info() << "Disabling background log and ibuf IO write" + << " threads."; + } + + ut_a(srv_n_file_io_threads <= SRV_MAX_N_IO_THREADS); + + if (os_aio_init()) { + ib::error() << "Cannot initialize AIO sub-system"; + + return(srv_init_abort(DB_ERROR)); + } + +#ifdef LINUX_NATIVE_AIO + if (srv_use_native_aio) { + ib::info() << "Using Linux native AIO"; + } +#endif + + fil_system.create(srv_file_per_table ? 50000 : 5000); + + ib::info() << "Initializing buffer pool, total size = " + << srv_buf_pool_size + << ", chunk size = " << srv_buf_pool_chunk_unit; + + if (buf_pool.create()) { + ib::error() << "Cannot allocate memory for the buffer pool"; + + return(srv_init_abort(DB_ERROR)); + } + + ib::info() << "Completed initialization of buffer pool"; + +#ifdef UNIV_DEBUG + /* We have observed deadlocks with a 5MB buffer pool but + the actual lower limit could very well be a little higher. */ + + if (srv_buf_pool_size <= 5 * 1024 * 1024) { + + ib::info() << "Small buffer pool size (" + << srv_buf_pool_size / 1024 / 1024 + << "M), the flst_validate() debug function can cause a" + << " deadlock if the buffer pool fills up."; + } +#endif /* UNIV_DEBUG */ + + log_sys.create(); + recv_sys.create(); + lock_sys.create(srv_lock_table_size); + + + if (!srv_read_only_mode) { + buf_flush_page_cleaner_init(); + ut_ad(buf_page_cleaner_is_active); + } + + srv_startup_is_before_trx_rollback_phase = !create_new_db; + + /* Check if undo tablespaces and redo log files exist before creating + a new system tablespace */ + if (create_new_db) { + err = srv_check_undo_redo_logs_exists(); + if (err != DB_SUCCESS) { + return(srv_init_abort(DB_ERROR)); + } + recv_sys.debug_free(); + } + + /* Open or create the data files. */ + ulint sum_of_new_sizes; + + err = srv_sys_space.open_or_create( + false, create_new_db, &sum_of_new_sizes, &flushed_lsn); + + switch (err) { + case DB_SUCCESS: + break; + case DB_CANNOT_OPEN_FILE: + ib::error() + << "Could not open or create the system tablespace. If" + " you tried to add new data files to the system" + " tablespace, and it failed here, you should now" + " edit innodb_data_file_path in my.cnf back to what" + " it was, and remove the new ibdata files InnoDB" + " created in this failed attempt. InnoDB only wrote" + " those files full of zeros, but did not yet use" + " them in any way. But be careful: do not remove" + " old data files which contain your precious data!"; + /* fall through */ + default: + /* Other errors might come from Datafile::validate_first_page() */ + return(srv_init_abort(err)); + } + + srv_log_file_size_requested = srv_log_file_size; + + if (innodb_encrypt_temporary_tables && !log_crypt_init()) { + return srv_init_abort(DB_ERROR); + } + + std::string logfile0; + bool create_new_log = create_new_db; + if (create_new_db) { + flushed_lsn = log_sys.get_lsn(); + log_sys.set_flushed_lsn(flushed_lsn); + buf_flush_sync(); + + err = create_log_file(true, flushed_lsn, logfile0); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + } else { + srv_log_file_size = 0; + + bool log_file_found; + if (dberr_t err = find_and_check_log_file(log_file_found)) { + if (err == DB_NOT_FOUND) { + return DB_SUCCESS; + } + return srv_init_abort(err); + } + + create_new_log = srv_log_file_size == 0; + if (create_new_log) { + if (flushed_lsn < lsn_t(1000)) { + ib::error() + << "Cannot create log file because" + " data files are corrupt or the" + " database was not shut down cleanly" + " after creating the data files."; + return srv_init_abort(DB_ERROR); + } + + srv_log_file_size = srv_log_file_size_requested; + + err = create_log_file(false, flushed_lsn, logfile0); + + if (err == DB_SUCCESS) { + err = create_log_file_rename(flushed_lsn, + logfile0); + } + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + /* Suppress the message about + crash recovery. */ + flushed_lsn = log_sys.get_lsn(); + goto file_checked; + } + + srv_log_file_found = log_file_found; + + log_sys.log.open_file(get_log_file_path()); + + log_sys.log.create(); + + if (!log_set_capacity(srv_log_file_size_requested)) { + return(srv_init_abort(DB_ERROR)); + } + } + +file_checked: + /* Open log file and data files in the systemtablespace: we keep + them open until database shutdown */ + ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); + + err = fil_system.sys_space->open(create_new_db) + ? srv_undo_tablespaces_init(create_new_db) + : DB_ERROR; + + /* If the force recovery is set very high then we carry on regardless + of all errors. Basically this is fingers crossed mode. */ + + if (err != DB_SUCCESS + && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { + + return(srv_init_abort(err)); + } + + /* Initialize objects used by dict stats gathering thread, which + can also be used by recovery if it tries to drop some table */ + if (!srv_read_only_mode) { + dict_stats_init(); + } + + trx_sys.create(); + + if (create_new_db) { + ut_ad(!srv_read_only_mode); + + mtr_start(&mtr); + ut_ad(fil_system.sys_space->id == 0); + compile_time_assert(TRX_SYS_SPACE == 0); + compile_time_assert(IBUF_SPACE_ID == 0); + fsp_header_init(fil_system.sys_space, + uint32_t(sum_of_new_sizes), &mtr); + + ulint ibuf_root = btr_create( + DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space, + DICT_IBUF_ID_MIN, nullptr, &mtr); + + mtr_commit(&mtr); + + if (ibuf_root == FIL_NULL) { + return(srv_init_abort(DB_ERROR)); + } + + ut_ad(ibuf_root == IBUF_TREE_ROOT_PAGE_NO); + + /* To maintain backward compatibility we create only + the first rollback segment before the double write buffer. + All the remaining rollback segments will be created later, + after the double write buffer has been created. */ + trx_sys_create_sys_pages(); + err = trx_lists_init_at_db_start(); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + err = dict_create(); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + buf_flush_sync(); + + flushed_lsn = log_sys.get_lsn(); + + err = fil_write_flushed_lsn(flushed_lsn); + + if (err == DB_SUCCESS) { + err = create_log_file_rename(flushed_lsn, logfile0); + } + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + } else { + /* Suppress warnings in fil_space_t::create() for files + that are being read before dict_boot() has recovered + DICT_HDR_MAX_SPACE_ID. */ + fil_system.space_id_reuse_warned = true; + + /* We always try to do a recovery, even if the database had + been shut down normally: this is the normal startup path */ + + err = create_new_log + ? DB_SUCCESS + : recv_recovery_from_checkpoint_start(flushed_lsn); + recv_sys.close_files(); + + recv_sys.dblwr.pages.clear(); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + switch (srv_operation) { + case SRV_OPERATION_NORMAL: + case SRV_OPERATION_RESTORE_EXPORT: + /* Initialize the change buffer. */ + err = dict_boot(); + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + /* fall through */ + case SRV_OPERATION_RESTORE: + /* This must precede + recv_apply_hashed_log_recs(true). */ + srv_undo_tablespaces_active + = trx_rseg_get_n_undo_tablespaces(); + err = srv_validate_undo_tablespaces(); + if (err != DB_SUCCESS) { + return srv_init_abort(err); + } + if (srv_operation == SRV_OPERATION_RESTORE) { + break; + } + err = trx_lists_init_at_db_start(); + if (err != DB_SUCCESS) { + return srv_init_abort(err); + } + break; + case SRV_OPERATION_RESTORE_DELTA: + case SRV_OPERATION_BACKUP: + ut_ad("wrong mariabackup mode" == 0); + } + + if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { + /* Apply the hashed log records to the + respective file pages, for the last batch of + recv_group_scan_log_recs(). */ + + recv_sys.apply(true); + + if (recv_sys.found_corrupt_log + || recv_sys.found_corrupt_fs) { + return(srv_init_abort(DB_CORRUPTION)); + } + + DBUG_PRINT("ib_log", ("apply completed")); + + if (recv_needed_recovery) { + trx_sys_print_mysql_binlog_offset(); + } + } + + fil_system.space_id_reuse_warned = false; + + if (!srv_read_only_mode) { + const ulint flags = FSP_FLAGS_PAGE_SSIZE(); + for (ulint id = 0; id <= srv_undo_tablespaces; id++) { + if (fil_space_t* space = fil_space_get(id)) { + fsp_flags_try_adjust(space, flags); + } + } + + if (sum_of_new_sizes > 0) { + /* New data file(s) were added */ + mtr.start(); + mtr.x_lock_space(fil_system.sys_space, + __FILE__, __LINE__); + buf_block_t* block = buf_page_get( + page_id_t(0, 0), 0, + RW_SX_LATCH, &mtr); + ulint size = mach_read_from_4( + FSP_HEADER_OFFSET + FSP_SIZE + + block->frame); + ut_ad(size == fil_system.sys_space + ->size_in_header); + size += sum_of_new_sizes; + mtr.write<4>(*block, + FSP_HEADER_OFFSET + FSP_SIZE + + block->frame, size); + fil_system.sys_space->size_in_header + = uint32_t(size); + mtr.commit(); + /* Immediately write the log record about + increased tablespace size to disk, so that it + is durable even if mysqld would crash + quickly */ + log_buffer_flush_to_disk(); + } + } + +#ifdef UNIV_DEBUG + { + mtr.start(); + buf_block_t* block = buf_page_get(page_id_t(0, 0), 0, + RW_S_LATCH, &mtr); + ut_ad(mach_read_from_4(FSP_SIZE + FSP_HEADER_OFFSET + + block->frame) + == fil_system.sys_space->size_in_header); + mtr.commit(); + } +#endif + const ulint tablespace_size_in_header + = fil_system.sys_space->size_in_header; + const ulint sum_of_data_file_sizes + = srv_sys_space.get_sum_of_sizes(); + /* Compare the system tablespace file size to what is + stored in FSP_SIZE. In srv_sys_space.open_or_create() + we already checked that the file sizes match the + innodb_data_file_path specification. */ + if (srv_read_only_mode + || sum_of_data_file_sizes == tablespace_size_in_header) { + /* Do not complain about the size. */ + } else if (!srv_sys_space.can_auto_extend_last_file() + || sum_of_data_file_sizes + < tablespace_size_in_header) { + ib::error() << "Tablespace size stored in header is " + << tablespace_size_in_header + << " pages, but the sum of data file sizes is " + << sum_of_data_file_sizes << " pages"; + + if (srv_force_recovery == 0 + && sum_of_data_file_sizes + < tablespace_size_in_header) { + ib::error() << + "Cannot start InnoDB. The tail of" + " the system tablespace is" + " missing. Have you edited" + " innodb_data_file_path in my.cnf" + " in an inappropriate way, removing" + " data files from there?" + " You can set innodb_force_recovery=1" + " in my.cnf to force" + " a startup if you are trying to" + " recover a badly corrupt database."; + + return(srv_init_abort(DB_ERROR)); + } + } + + recv_sys.debug_free(); + + if (srv_operation == SRV_OPERATION_RESTORE + || srv_operation == SRV_OPERATION_RESTORE_EXPORT) { + /* After applying the redo log from + SRV_OPERATION_BACKUP, flush the changes + to the data files and truncate or delete the log. + Unless --export is specified, no further change to + InnoDB files is needed. */ + ut_ad(srv_force_recovery <= SRV_FORCE_IGNORE_CORRUPT); + ut_ad(recv_no_log_write); + err = fil_write_flushed_lsn(log_sys.get_lsn()); + DBUG_ASSERT(!buf_pool.any_io_pending()); + log_sys.log.close_file(); + if (err == DB_SUCCESS) { + bool trunc = srv_operation + == SRV_OPERATION_RESTORE; + if (!trunc) { + delete_log_file("0"); + } else { + auto logfile0 = get_log_file_path(); + /* Truncate the first log file. */ + fclose(fopen(logfile0.c_str(), "w")); + } + } + return(err); + } + + /* Upgrade or resize or rebuild the redo logs before + generating any dirty pages, so that the old redo log + file will not be written to. */ + + if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) { + /* Completely ignore the redo log. */ + } else if (srv_read_only_mode) { + /* Leave the redo log alone. */ + } else if (srv_log_file_size_requested == srv_log_file_size + && srv_log_file_found + && log_sys.log.format + == (srv_encrypt_log + ? log_t::FORMAT_ENC_10_5 + : log_t::FORMAT_10_5) + && log_sys.log.subformat == 2) { + /* No need to add or remove encryption, + upgrade, downgrade, or resize. */ + } else { + /* Prepare to delete the old redo log file */ + flushed_lsn = srv_prepare_to_delete_redo_log_file( + srv_log_file_found); + + DBUG_EXECUTE_IF("innodb_log_abort_1", + return(srv_init_abort(DB_ERROR));); + /* Prohibit redo log writes from any other + threads until creating a log checkpoint at the + end of create_log_file(). */ + ut_d(recv_no_log_write = true); + DBUG_ASSERT(!buf_pool.any_io_pending()); + + DBUG_EXECUTE_IF("innodb_log_abort_3", + return(srv_init_abort(DB_ERROR));); + DBUG_PRINT("ib_log", ("After innodb_log_abort_3")); + + /* Stamp the LSN to the data files. */ + err = fil_write_flushed_lsn(flushed_lsn); + + DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;); + DBUG_PRINT("ib_log", ("After innodb_log_abort_4")); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + /* Close the redo log file, so that we can replace it */ + log_sys.log.close_file(); + + DBUG_EXECUTE_IF("innodb_log_abort_5", + return(srv_init_abort(DB_ERROR));); + DBUG_PRINT("ib_log", ("After innodb_log_abort_5")); + + ib::info() + << "Starting to delete and rewrite log file."; + + srv_log_file_size = srv_log_file_size_requested; + + err = create_log_file(false, flushed_lsn, logfile0); + + if (err == DB_SUCCESS) { + err = create_log_file_rename(flushed_lsn, + logfile0); + } + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + } + } + + ut_ad(err == DB_SUCCESS); + ut_a(sum_of_new_sizes != ULINT_UNDEFINED); + + /* Create the doublewrite buffer to a new tablespace */ + if (!srv_read_only_mode && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO + && !buf_dblwr.create()) { + return(srv_init_abort(DB_ERROR)); + } + + /* Here the double write buffer has already been created and so + any new rollback segments will be allocated after the double + write buffer. The default segment should already exist. + We create the new segments only if it's a new database or + the database was shutdown cleanly. */ + + /* Note: When creating the extra rollback segments during an upgrade + we violate the latching order, even if the change buffer is empty. + We make an exception in sync0sync.cc and check srv_is_being_started + for that violation. It cannot create a deadlock because we are still + running in single threaded mode essentially. Only the IO threads + should be running at this stage. */ + + if (!trx_sys_create_rsegs()) { + return(srv_init_abort(DB_ERROR)); + } + + if (!create_new_db) { + ut_ad(high_level_read_only + || srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE); + + /* Validate a few system page types that were left + uninitialized before MySQL or MariaDB 5.5. */ + if (!high_level_read_only + && !fil_system.sys_space->full_crc32()) { + buf_block_t* block; + mtr.start(); + /* Bitmap page types will be reset in + buf_dblwr_check_block() without redo logging. */ + block = buf_page_get( + page_id_t(IBUF_SPACE_ID, + FSP_IBUF_HEADER_PAGE_NO), + 0, RW_X_LATCH, &mtr); + fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); + /* Already MySQL 3.23.53 initialized + FSP_IBUF_TREE_ROOT_PAGE_NO to + FIL_PAGE_INDEX. No need to reset that one. */ + block = buf_page_get( + page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO), + 0, RW_X_LATCH, &mtr); + fil_block_check_type(*block, FIL_PAGE_TYPE_TRX_SYS, + &mtr); + block = buf_page_get( + page_id_t(TRX_SYS_SPACE, + FSP_FIRST_RSEG_PAGE_NO), + 0, RW_X_LATCH, &mtr); + fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); + block = buf_page_get( + page_id_t(TRX_SYS_SPACE, FSP_DICT_HDR_PAGE_NO), + 0, RW_X_LATCH, &mtr); + fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); + mtr.commit(); + } + + /* Roll back any recovered data dictionary + transactions, so that the data dictionary tables will + be free of any locks. The data dictionary latch + should guarantee that there is at most one data + dictionary transaction active at a time. */ + if (!high_level_read_only + && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) { + /* If the following call is ever removed, the + first-time ha_innobase::open() must hold (or + acquire and release) a table lock that + conflicts with trx_resurrect_table_locks(), to + ensure that any recovered incomplete ALTER + TABLE will have been rolled back. Otherwise, + dict_table_t::instant could be cleared by + rollback invoking + dict_index_t::clear_instant_alter() while open + table handles exist in client connections. */ + trx_rollback_recovered(false); + } + + /* FIXME: Skip the following if srv_read_only_mode, + while avoiding "Allocated tablespace ID" warnings. */ + if (srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE) { + /* Open or Create SYS_TABLESPACES and SYS_DATAFILES + so that tablespace names and other metadata can be + found. */ + err = dict_create_or_check_sys_tablespace(); + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + /* The following call is necessary for the insert + buffer to work with multiple tablespaces. We must + know the mapping between space id's and .ibd file + names. + + In a crash recovery, we check that the info in data + dictionary is consistent with what we already know + about space id's from the calls to fil_ibd_load(). + + In a normal startup, we create the space objects for + every table in the InnoDB data dictionary that has + an .ibd file. + + We also determine the maximum tablespace id used. */ + dict_check_tablespaces_and_store_max_id(); + } + + if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO + && !srv_read_only_mode) { + /* Drop partially created indexes. */ + row_merge_drop_temp_indexes(); + /* Drop garbage tables. */ + row_mysql_drop_garbage_tables(); + + /* Drop any auxiliary tables that were not + dropped when the parent table was + dropped. This can happen if the parent table + was dropped but the server crashed before the + auxiliary tables were dropped. */ + fts_drop_orphaned_tables(); + + /* Rollback incomplete non-DDL transactions */ + trx_rollback_is_active = true; + os_thread_create(trx_rollback_all_recovered); + } + } + + srv_startup_is_before_trx_rollback_phase = false; + + if (!srv_read_only_mode) { + /* timer task which watches the timeouts + for lock waits */ + lock_sys.timeout_timer.reset(srv_thread_pool->create_timer( + lock_wait_timeout_task)); + + DBUG_EXECUTE_IF("innodb_skip_monitors", goto skip_monitors;); + /* Create the task which warns of long semaphore waits */ + srv_start_periodic_timer(srv_monitor_timer, srv_monitor_task, + SRV_MONITOR_INTERVAL); + +#ifndef DBUG_OFF +skip_monitors: +#endif + ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN + || !purge_sys.enabled()); + + if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { + srv_undo_sources = true; + /* Create the dict stats gathering task */ + dict_stats_start(); + /* Create the thread that will optimize the + FULLTEXT search index subsystem. */ + fts_optimize_init(); + } + } + + /* Create the SYS_FOREIGN and SYS_FOREIGN_COLS system tables */ + err = dict_create_or_check_foreign_constraint_tables(); + if (err == DB_SUCCESS) { + err = dict_create_or_check_sys_tablespace(); + if (err == DB_SUCCESS) { + err = dict_create_or_check_sys_virtual(); + } + } + switch (err) { + case DB_SUCCESS: + break; + case DB_READ_ONLY: + if (srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) { + break; + } + ib::error() << "Cannot create system tables in read-only mode"; + /* fall through */ + default: + return(srv_init_abort(err)); + } + + if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL) { + /* Initialize the innodb_temporary tablespace and keep + it open until shutdown. */ + err = srv_open_tmp_tablespace(create_new_db); + + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + + trx_temp_rseg_create(); + + if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { + srv_start_periodic_timer(srv_master_timer, srv_master_callback, 1000); + } + } + + if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL + && srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { + srv_init_purge_tasks(); + purge_sys.coordinator_startup(); + srv_wake_purge_thread_if_not_active(); + } + + srv_is_being_started = false; + + if (srv_print_verbose_log) { + ib::info() << INNODB_VERSION_STR + << " started; log sequence number " + << recv_sys.recovered_lsn + << "; transaction id " << trx_sys.get_max_trx_id(); + } + + if (srv_force_recovery == 0) { + /* In the change buffer we may have even bigger tablespace + id's, because we may have dropped those tablespaces, but + the buffered records have not been cleaned yet. */ + ibuf_update_max_tablespace_id(); + } + + if (!srv_read_only_mode) { + if (create_new_db) { + srv_buffer_pool_load_at_startup = FALSE; + } + +#ifdef WITH_WSREP + /* + Create the dump/load thread only when not running with + --wsrep-recover. + */ + if (!get_wsrep_recovery()) { +#endif /* WITH_WSREP */ + + /* Start buffer pool dump/load task */ + buf_load_at_startup(); + +#ifdef WITH_WSREP + } else { + ib::warn() << + "Skipping buffer pool dump/restore during " + "wsrep recovery."; + } +#endif /* WITH_WSREP */ + + /* Create thread(s) that handles key rotation. This is + needed already here as log_preflush_pool_modified_pages + will flush dirty pages and that might need e.g. + fil_crypt_threads_event. */ + fil_system_enter(); + fil_crypt_threads_init(); + fil_system_exit(); + + /* Initialize online defragmentation. */ + btr_defragment_init(); + + srv_started_redo = true; + } + + return(DB_SUCCESS); +} + +/** Shut down background threads that can generate undo log. */ +void srv_shutdown_bg_undo_sources() +{ + srv_shutdown_state = SRV_SHUTDOWN_INITIATED; + + if (srv_undo_sources) { + ut_ad(!srv_read_only_mode); + fts_optimize_shutdown(); + dict_stats_shutdown(); + while (row_get_background_drop_list_len_low()) { + srv_inc_activity_count(); + os_thread_yield(); + } + srv_undo_sources = false; + } +} + +/** + Shutdown purge to make sure that there is no possibility that we call any + plugin code (e.g., audit) inside virtual column computation. +*/ +void innodb_preshutdown() +{ + static bool first_time= true; + if (!first_time) + return; + first_time= false; + + if (srv_read_only_mode) + return; + if (!srv_fast_shutdown && srv_operation == SRV_OPERATION_NORMAL) + { + /* Because a slow shutdown must empty the change buffer, we had + better prevent any further changes from being buffered. */ + innodb_change_buffering= 0; + + if (trx_sys.is_initialised()) + while (trx_sys.any_active_transactions()) + os_thread_sleep(1000); + } + srv_shutdown_bg_undo_sources(); + srv_purge_shutdown(); + + if (srv_n_fil_crypt_threads) + fil_crypt_set_thread_cnt(0); +} + + +/** Shut down InnoDB. */ +void innodb_shutdown() +{ + innodb_preshutdown(); + ut_ad(!srv_undo_sources); + switch (srv_operation) { + case SRV_OPERATION_BACKUP: + case SRV_OPERATION_RESTORE_DELTA: + break; + case SRV_OPERATION_RESTORE: + case SRV_OPERATION_RESTORE_EXPORT: + srv_shutdown_state = SRV_SHUTDOWN_CLEANUP; + if (!buf_page_cleaner_is_active) { + break; + } + mysql_mutex_lock(&buf_pool.flush_list_mutex); + while (buf_page_cleaner_is_active) { + pthread_cond_signal(&buf_pool.do_flush_list); + my_cond_wait(&buf_pool.done_flush_list, + &buf_pool.flush_list_mutex.m_mutex); + } + mysql_mutex_unlock(&buf_pool.flush_list_mutex); + break; + case SRV_OPERATION_NORMAL: + /* Shut down the persistent files. */ + logs_empty_and_mark_files_at_shutdown(); + } + + os_aio_free(); + fil_space_t::close_all(); + /* Exit any remaining threads. */ + ut_ad(!buf_page_cleaner_is_active); + srv_shutdown_threads(); + + if (srv_monitor_file) { + my_fclose(srv_monitor_file, MYF(MY_WME)); + srv_monitor_file = 0; + if (srv_monitor_file_name) { + unlink(srv_monitor_file_name); + ut_free(srv_monitor_file_name); + } + } + + if (srv_misc_tmpfile) { + my_fclose(srv_misc_tmpfile, MYF(MY_WME)); + srv_misc_tmpfile = 0; + } + + ut_ad(dict_sys.is_initialised() || !srv_was_started); + ut_ad(trx_sys.is_initialised() || !srv_was_started); + ut_ad(buf_dblwr.is_initialised() || !srv_was_started + || srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + ut_ad(lock_sys.is_initialised() || !srv_was_started); + ut_ad(log_sys.is_initialised() || !srv_was_started); + ut_ad(ibuf.index || !srv_was_started); + + dict_stats_deinit(); + + if (srv_started_redo) { + ut_ad(!srv_read_only_mode); + /* srv_shutdown_bg_undo_sources() already invoked + fts_optimize_shutdown(); dict_stats_shutdown(); */ + + fil_crypt_threads_cleanup(); + btr_defragment_shutdown(); + } + + /* This must be disabled before closing the buffer pool + and closing the data dictionary. */ + +#ifdef BTR_CUR_HASH_ADAPT + if (dict_sys.is_initialised()) { + btr_search_disable(); + } +#endif /* BTR_CUR_HASH_ADAPT */ + ibuf_close(); + log_sys.close(); + purge_sys.close(); + trx_sys.close(); + buf_dblwr.close(); + lock_sys.close(); + trx_pool_close(); + + if (!srv_read_only_mode) { + mutex_free(&srv_monitor_file_mutex); + mutex_free(&srv_misc_tmpfile_mutex); + } + + dict_sys.close(); + btr_search_sys_free(); + row_mysql_close(); + srv_free(); + fil_system.close(); + pars_lexer_close(); + recv_sys.close(); + + ut_ad(buf_pool.is_initialised() || !srv_was_started); + buf_pool.close(); + sync_check_close(); + + srv_sys_space.shutdown(); + if (srv_tmp_space.get_sanity_check_status()) { + if (fil_system.temp_space) { + fil_system.temp_space->close(); + } + srv_tmp_space.delete_files(); + } + srv_tmp_space.shutdown(); + +#ifdef WITH_INNODB_DISALLOW_WRITES + os_event_destroy(srv_allow_writes_event); +#endif /* WITH_INNODB_DISALLOW_WRITES */ + + if (srv_was_started && srv_print_verbose_log) { + ib::info() << "Shutdown completed; log sequence number " + << srv_shutdown_lsn + << "; transaction id " << trx_sys.get_max_trx_id(); + } + srv_thread_pool_end(); + srv_started_redo = false; + srv_was_started = false; + srv_start_has_been_called = false; +} + +/** Get the meta-data filename from the table name for a +single-table tablespace. +@param[in] table table object +@param[out] filename filename +@param[in] max_len filename max length */ +void +srv_get_meta_data_filename( + dict_table_t* table, + char* filename, + ulint max_len) +{ + ulint len; + char* path; + + /* Make sure the data_dir_path is set. */ + dict_get_and_save_data_dir_path(table, false); + + if (DICT_TF_HAS_DATA_DIR(table->flags)) { + ut_a(table->data_dir_path); + + path = fil_make_filepath( + table->data_dir_path, table->name.m_name, CFG, true); + } else { + path = fil_make_filepath(NULL, table->name.m_name, CFG, false); + } + + ut_a(path); + len = strlen(path); + ut_a(max_len >= len); + + strcpy(filename, path); + + ut_free(path); +} |