1/*****************************************************************************
2
3Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file trx/trx0sys.cc
22Transaction system
23
24Created 3/26/1996 Heikki Tuuri
25*******************************************************/
26
27#include "ha_prototypes.h"
28
29#include "mysqld.h"
30#include "trx0sys.h"
31#include "sql_error.h"
32
33#include "fsp0fsp.h"
34#include "mtr0log.h"
35#include "mtr0log.h"
36#include "trx0trx.h"
37#include "trx0rseg.h"
38#include "trx0undo.h"
39#include "srv0srv.h"
40#include "srv0start.h"
41#include "trx0purge.h"
42#include "log0log.h"
43#include "log0recv.h"
44#include "os0file.h"
45#include "fsp0sysspace.h"
46
47#include <mysql/service_wsrep.h>
48
49/** The transaction system */
50trx_sys_t trx_sys;
51
52/** Check whether transaction id is valid.
53@param[in] id transaction id to check
54@param[in] name table name */
55void
56ReadView::check_trx_id_sanity(
57 trx_id_t id,
58 const table_name_t& name)
59{
60 if (id >= trx_sys.get_max_trx_id()) {
61
62 ib::warn() << "A transaction id"
63 << " in a record of table "
64 << name
65 << " is newer than the"
66 << " system-wide maximum.";
67 ut_ad(0);
68 THD *thd = current_thd;
69 if (thd != NULL) {
70 char table_name[MAX_FULL_NAME_LEN + 1];
71
72 innobase_format_name(
73 table_name, sizeof(table_name),
74 name.m_name);
75
76 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
77 ER_SIGNAL_WARN,
78 "InnoDB: Transaction id"
79 " in a record of table"
80 " %s is newer than system-wide"
81 " maximum.", table_name);
82 }
83 }
84}
85
86#ifdef UNIV_DEBUG
87/* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
88uint trx_rseg_n_slots_debug = 0;
89#endif
90
91/** Display the MySQL binlog offset info if it is present in the trx
92system header. */
93void
94trx_sys_print_mysql_binlog_offset()
95{
96 if (!*trx_sys.recovered_binlog_filename) {
97 return;
98 }
99
100 ib::info() << "Last binlog file '"
101 << trx_sys.recovered_binlog_filename
102 << "', position "
103 << trx_sys.recovered_binlog_offset;
104}
105
106/** Find an available rollback segment.
107@param[in] sys_header
108@return an unallocated rollback segment slot in the TRX_SYS header
109@retval ULINT_UNDEFINED if not found */
110ulint
111trx_sys_rseg_find_free(const buf_block_t* sys_header)
112{
113 for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
114 if (trx_sysf_rseg_get_page_no(sys_header, rseg_id)
115 == FIL_NULL) {
116 return rseg_id;
117 }
118 }
119
120 return(ULINT_UNDEFINED);
121}
122
123/** Count the number of initialized persistent rollback segment slots. */
124static
125void
126trx_sysf_get_n_rseg_slots()
127{
128 mtr_t mtr;
129 mtr.start();
130
131 srv_available_undo_logs = 0;
132 if (const buf_block_t* sys_header = trx_sysf_get(&mtr, false)) {
133 for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
134 srv_available_undo_logs
135 += trx_sysf_rseg_get_page_no(sys_header,
136 rseg_id)
137 != FIL_NULL;
138 }
139 }
140
141 mtr.commit();
142}
143
144/*****************************************************************//**
145Creates the file page for the transaction system. This function is called only
146at the database creation, before trx_sys_init. */
147static
148void
149trx_sysf_create(
150/*============*/
151 mtr_t* mtr) /*!< in: mtr */
152{
153 ulint slot_no;
154 buf_block_t* block;
155 page_t* page;
156 ulint page_no;
157 byte* ptr;
158
159 ut_ad(mtr);
160
161 /* Note that below we first reserve the file space x-latch, and
162 then enter the kernel: we must do it in this order to conform
163 to the latching order rules. */
164
165 mtr_x_lock(&fil_system.sys_space->latch, mtr);
166 compile_time_assert(TRX_SYS_SPACE == 0);
167
168 /* Create the trx sys file block in a new allocated file segment */
169 block = fseg_create(fil_system.sys_space, 0,
170 TRX_SYS + TRX_SYS_FSEG_HEADER,
171 mtr);
172 buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
173
174 ut_a(block->page.id.page_no() == TRX_SYS_PAGE_NO);
175
176 page = buf_block_get_frame(block);
177
178 mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS,
179 MLOG_2BYTES, mtr);
180
181 /* Reset the doublewrite buffer magic number to zero so that we
182 know that the doublewrite buffer has not yet been created (this
183 suppresses a Valgrind warning) */
184
185 mlog_write_ulint(page + TRX_SYS_DOUBLEWRITE
186 + TRX_SYS_DOUBLEWRITE_MAGIC, 0, MLOG_4BYTES, mtr);
187
188 /* Reset the rollback segment slots. Old versions of InnoDB
189 (before MySQL 5.5) define TRX_SYS_N_RSEGS as 256 and expect
190 that the whole array is initialized. */
191 ptr = TRX_SYS + TRX_SYS_RSEGS + page;
192 compile_time_assert(256 >= TRX_SYS_N_RSEGS);
193 memset(ptr, 0xff, 256 * TRX_SYS_RSEG_SLOT_SIZE);
194 ptr += 256 * TRX_SYS_RSEG_SLOT_SIZE;
195 ut_a(ptr <= page + (srv_page_size - FIL_PAGE_DATA_END));
196
197 /* Initialize all of the page. This part used to be uninitialized. */
198 memset(ptr, 0, srv_page_size - FIL_PAGE_DATA_END + size_t(page - ptr));
199
200 mlog_log_string(TRX_SYS + page, srv_page_size - FIL_PAGE_DATA_END
201 - TRX_SYS, mtr);
202
203 /* Create the first rollback segment in the SYSTEM tablespace */
204 slot_no = trx_sys_rseg_find_free(block);
205 page_no = trx_rseg_header_create(fil_system.sys_space, slot_no, block,
206 mtr);
207
208 ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
209 ut_a(page_no == FSP_FIRST_RSEG_PAGE_NO);
210}
211
212/** Create the instance */
213void
214trx_sys_t::create()
215{
216 ut_ad(this == &trx_sys);
217 ut_ad(!is_initialised());
218 m_initialised = true;
219 mutex_create(LATCH_ID_TRX_SYS, &mutex);
220 UT_LIST_INIT(trx_list, &trx_t::trx_list);
221 my_atomic_store32(&rseg_history_len, 0);
222
223 rw_trx_hash.init();
224}
225
226/*****************************************************************//**
227Creates and initializes the transaction system at the database creation. */
228void
229trx_sys_create_sys_pages(void)
230/*==========================*/
231{
232 mtr_t mtr;
233
234 mtr_start(&mtr);
235
236 trx_sysf_create(&mtr);
237
238 mtr_commit(&mtr);
239}
240
241/** Create the rollback segments.
242@return whether the creation succeeded */
243bool
244trx_sys_create_rsegs()
245{
246 /* srv_available_undo_logs reflects the number of persistent
247 rollback segments that have been initialized in the
248 transaction system header page.
249
250 srv_undo_logs determines how many of the
251 srv_available_undo_logs rollback segments may be used for
252 logging new transactions. */
253 ut_ad(srv_undo_tablespaces <= TRX_SYS_MAX_UNDO_SPACES);
254 ut_ad(srv_undo_logs <= TRX_SYS_N_RSEGS);
255
256 if (srv_read_only_mode) {
257 srv_undo_logs = srv_available_undo_logs = ULONG_UNDEFINED;
258 return(true);
259 }
260
261 /* This is executed in single-threaded mode therefore it is not
262 necessary to use the same mtr in trx_rseg_create(). n_used cannot
263 change while the function is executing. */
264 trx_sysf_get_n_rseg_slots();
265
266 ut_ad(srv_available_undo_logs <= TRX_SYS_N_RSEGS);
267
268 /* The first persistent rollback segment is always initialized
269 in the system tablespace. */
270 ut_a(srv_available_undo_logs > 0);
271
272 if (srv_force_recovery) {
273 /* Do not create additional rollback segments if
274 innodb_force_recovery has been set. */
275 if (srv_undo_logs > srv_available_undo_logs) {
276 srv_undo_logs = srv_available_undo_logs;
277 }
278 } else {
279 for (ulint i = 0; srv_available_undo_logs < srv_undo_logs;
280 i++, srv_available_undo_logs++) {
281 /* Tablespace 0 is the system tablespace.
282 Dedicated undo log tablespaces start from 1. */
283 ulint space = srv_undo_tablespaces > 0
284 ? (i % srv_undo_tablespaces)
285 + srv_undo_space_id_start
286 : TRX_SYS_SPACE;
287
288 if (!trx_rseg_create(space)) {
289 ib::error() << "Unable to allocate the"
290 " requested innodb_undo_logs";
291 return(false);
292 }
293
294 /* Increase the number of active undo
295 tablespace in case new rollback segment
296 assigned to new undo tablespace. */
297 if (space > srv_undo_tablespaces_active) {
298 srv_undo_tablespaces_active++;
299
300 ut_ad(srv_undo_tablespaces_active == space);
301 }
302 }
303 }
304
305 ut_ad(srv_undo_logs <= srv_available_undo_logs);
306
307 ib::info info;
308 info << srv_undo_logs << " out of " << srv_available_undo_logs;
309 if (srv_undo_tablespaces_active) {
310 info << " rollback segments in " << srv_undo_tablespaces_active
311 << " undo tablespaces are active.";
312 } else {
313 info << " rollback segments are active.";
314 }
315
316 return(true);
317}
318
319/** Close the transaction system on shutdown */
320void
321trx_sys_t::close()
322{
323 ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
324 if (!is_initialised()) {
325 return;
326 }
327
328 if (size_t size = view_count()) {
329 ib::error() << "All read views were not closed before"
330 " shutdown: " << size << " read views open";
331 }
332
333 rw_trx_hash.destroy();
334
335 /* There can't be any active transactions. */
336
337 for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
338 if (trx_rseg_t* rseg = rseg_array[i]) {
339 trx_rseg_mem_free(rseg);
340 }
341
342 if (trx_rseg_t* rseg = temp_rsegs[i]) {
343 trx_rseg_mem_free(rseg);
344 }
345 }
346
347 ut_a(UT_LIST_GET_LEN(trx_list) == 0);
348 mutex_free(&mutex);
349 m_initialised = false;
350}
351
352/** @return total number of active (non-prepared) transactions */
353ulint trx_sys_t::any_active_transactions()
354{
355 uint32_t total_trx= 0;
356
357 mutex_enter(&mutex);
358 for (trx_t* trx= UT_LIST_GET_FIRST(trx_sys.trx_list);
359 trx != NULL;
360 trx= UT_LIST_GET_NEXT(trx_list, trx))
361 {
362 if (trx->state == TRX_STATE_COMMITTED_IN_MEMORY ||
363 (trx->state == TRX_STATE_ACTIVE && trx->id))
364 total_trx++;
365 }
366 mutex_exit(&mutex);
367 return total_trx;
368}
369