1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file trx/trx0rseg.cc
28  Rollback segment
29 
30  Created 3/26/1996 Heikki Tuuri
31  *******************************************************/
32 
33 #include "trx0rseg.h"
34 
35 #include <stddef.h>
36 #include <algorithm>
37 
38 #include "clone0clone.h"
39 #include "fsp0sysspace.h"
40 #include "fut0lst.h"
41 #include "srv0mon.h"
42 #include "srv0srv.h"
43 #include "srv0start.h"
44 #include "trx0purge.h"
45 #include "trx0undo.h"
46 
47 /** Creates a rollback segment header.
48 This function is called only when a new rollback segment is created in
49 the database.
50 @param[in]	space_id	space id
51 @param[in]	page_size	page size
52 @param[in]	max_size	max size in pages
53 @param[in]	rseg_slot	rseg id == slot number in RSEG_ARRAY
54 @param[in,out]	mtr		mini-transaction
55 @return page number of the created segment, FIL_NULL if fail */
trx_rseg_header_create(space_id_t space_id,const page_size_t & page_size,page_no_t max_size,ulint rseg_slot,mtr_t * mtr)56 page_no_t trx_rseg_header_create(space_id_t space_id,
57                                  const page_size_t &page_size,
58                                  page_no_t max_size, ulint rseg_slot,
59                                  mtr_t *mtr) {
60   page_no_t page_no;
61   trx_rsegf_t *rsegf;
62   trx_sysf_t *sys_header;
63   trx_rsegsf_t *rsegs_header;
64   ulint i;
65   buf_block_t *block;
66 
67   ut_ad(mtr);
68   ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space_id), MTR_MEMO_X_LOCK));
69 
70   /* Allocate a new file segment for the rollback segment */
71   block = fseg_create(space_id, 0, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr);
72 
73   if (block == nullptr) {
74     return (FIL_NULL); /* No space left */
75   }
76 
77   buf_block_dbg_add_level(block, SYNC_RSEG_HEADER_NEW);
78 
79   page_no = block->page.id.page_no();
80 
81   /* Get the rollback segment file page */
82   rsegf = trx_rsegf_get_new(space_id, page_no, page_size, mtr);
83 
84   /* Initialize max size field */
85   mlog_write_ulint(rsegf + TRX_RSEG_MAX_SIZE, max_size, MLOG_4BYTES, mtr);
86 
87   /* Initialize the history list */
88   mlog_write_ulint(rsegf + TRX_RSEG_HISTORY_SIZE, 0, MLOG_4BYTES, mtr);
89   flst_init(rsegf + TRX_RSEG_HISTORY, mtr);
90 
91   /* Reset the undo log slots */
92   for (i = 0; i < TRX_RSEG_N_SLOTS; i++) {
93     trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr);
94   }
95 
96   /* Initialize maximum transaction number. */
97   mlog_write_ull(rsegf + TRX_RSEG_MAX_TRX_NO, 0, mtr);
98 
99   if (space_id == TRX_SYS_SPACE) {
100     /* All rollback segments in the system tablespace need
101     to be found in the TRX_SYS page in the rseg_id slot.
102     Add the rollback segment info to the free slot in the
103     trx system header in the TRX_SYS page. */
104 
105     sys_header = trx_sysf_get(mtr);
106 
107     trx_sysf_rseg_set_space(sys_header, rseg_slot, space_id, mtr);
108 
109     trx_sysf_rseg_set_page_no(sys_header, rseg_slot, page_no, mtr);
110 
111   } else if (fsp_is_system_temporary(space_id)) {
112     /* Rollback segments in the system temporary tablespace
113     are re-created on restart. So they only need to be
114     referenced in memory. */
115 
116   } else {
117     /* Rollback Segments in independent undo tablespaces
118     are tracked in the RSEG_ARRAY page. */
119     rsegs_header = trx_rsegsf_get(space_id, mtr);
120 
121     trx_rsegsf_set_page_no(rsegs_header, rseg_slot, page_no, mtr);
122   }
123 
124   return (page_no);
125 }
126 
127 /** Free an instance of the rollback segment in memory.
128 @param[in]	rseg	pointer to an rseg to free */
trx_rseg_mem_free(trx_rseg_t * rseg)129 void trx_rseg_mem_free(trx_rseg_t *rseg) {
130   trx_undo_t *undo;
131   trx_undo_t *next_undo;
132 
133   mutex_free(&rseg->mutex);
134 
135   if (!srv_apply_log_only) {
136     /* There can't be any active transactions. */
137     ut_a(UT_LIST_GET_LEN(rseg->update_undo_list) == 0);
138     ut_a(UT_LIST_GET_LEN(rseg->insert_undo_list) == 0);
139   } else {
140     for (undo = UT_LIST_GET_FIRST(rseg->update_undo_list); undo != NULL;
141          undo = next_undo) {
142       next_undo = UT_LIST_GET_NEXT(undo_list, undo);
143 
144       UT_LIST_REMOVE(rseg->update_undo_list, undo);
145 
146       MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
147 
148       trx_undo_mem_free(undo);
149     }
150     for (undo = UT_LIST_GET_FIRST(rseg->insert_undo_list); undo != NULL;
151          undo = next_undo) {
152       next_undo = UT_LIST_GET_NEXT(undo_list, undo);
153 
154       UT_LIST_REMOVE(rseg->insert_undo_list, undo);
155 
156       MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
157 
158       trx_undo_mem_free(undo);
159     }
160   }
161 
162   for (undo = UT_LIST_GET_FIRST(rseg->update_undo_cached); undo != nullptr;
163        undo = next_undo) {
164     next_undo = UT_LIST_GET_NEXT(undo_list, undo);
165 
166     UT_LIST_REMOVE(rseg->update_undo_cached, undo);
167 
168     MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
169 
170     trx_undo_mem_free(undo);
171   }
172 
173   for (undo = UT_LIST_GET_FIRST(rseg->insert_undo_cached); undo != nullptr;
174        undo = next_undo) {
175     next_undo = UT_LIST_GET_NEXT(undo_list, undo);
176 
177     UT_LIST_REMOVE(rseg->insert_undo_cached, undo);
178 
179     MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
180 
181     trx_undo_mem_free(undo);
182   }
183 
184   ut_free(rseg);
185 }
186 
trx_rseg_persist_gtid(trx_rseg_t * rseg,trx_id_t gtid_trx_no)187 static void trx_rseg_persist_gtid(trx_rseg_t *rseg, trx_id_t gtid_trx_no) {
188   /* Old server where GTID persistence were not enabled. */
189   if (gtid_trx_no == 0) {
190     return;
191   }
192   /* The mini transactions used in this function should not do any
193   modification/write operation. We read the undo header and send GTIDs
194   to the GTID persistor. There is no impact if the server crashes
195   anytime during the operation. */
196   mtr_t mtr;
197   mtr_start(&mtr);
198 
199   auto rseg_header =
200       trx_rsegf_get_new(rseg->space_id, rseg->page_no, rseg->page_size, &mtr);
201 
202   auto rseg_max_trx_no = mach_read_from_8(rseg_header + TRX_RSEG_MAX_TRX_NO);
203 
204   /* Check if GTID for transactions in this rollback segment are persisted. */
205   if (rseg_max_trx_no < gtid_trx_no) {
206     mtr_commit(&mtr);
207     return;
208   }
209 
210   /* Head of transaction history list in rollback segment. */
211   auto node = rseg_header + TRX_RSEG_HISTORY;
212 
213   fil_addr_t node_addr = flst_get_first(node, &mtr);
214   ut_ad(node_addr.page != FIL_NULL);
215 
216   mtr_commit(&mtr);
217 
218   while (node_addr.page != FIL_NULL) {
219     mtr_start(&mtr);
220     /* Get the undo page pointed by current node. */
221     page_id_t undo_page_id(rseg->space_id, node_addr.page);
222     auto undo_page = trx_undo_page_get(undo_page_id, rseg->page_size, &mtr);
223 
224     /* Get undo log and trx_no for the transaction. */
225     node = undo_page + node_addr.boffset;
226     auto undo_log = node - TRX_UNDO_HISTORY_NODE;
227     auto undo_trx_no = mach_read_from_8(undo_log + TRX_UNDO_TRX_NO);
228 
229     /* Check and exit if the transaction GTID is already persisted. We
230     don't need to check any more as history list is ordered by trx_no. */
231     if (undo_trx_no < gtid_trx_no) {
232       mtr_commit(&mtr);
233       break;
234     }
235     trx_undo_gtid_read_and_persist(undo_log);
236 
237     /* Move to next node. */
238     node_addr = flst_get_next_addr(node, &mtr);
239     mtr_commit(&mtr);
240   }
241 }
242 
trx_rseg_mem_create(ulint id,space_id_t space_id,page_no_t page_no,const page_size_t & page_size,trx_id_t gtid_trx_no,purge_pq_t * purge_queue,mtr_t * mtr)243 trx_rseg_t *trx_rseg_mem_create(ulint id, space_id_t space_id,
244                                 page_no_t page_no, const page_size_t &page_size,
245                                 trx_id_t gtid_trx_no, purge_pq_t *purge_queue,
246                                 mtr_t *mtr) {
247   auto rseg = static_cast<trx_rseg_t *>(ut_zalloc_nokey(sizeof(trx_rseg_t)));
248 
249   rseg->id = id;
250   rseg->space_id = space_id;
251   rseg->page_size.copy_from(page_size);
252   rseg->page_no = page_no;
253   rseg->trx_ref_count = 0;
254 
255   if (fsp_is_system_temporary(space_id)) {
256     mutex_create(LATCH_ID_TEMP_SPACE_RSEG, &rseg->mutex);
257   } else if (fsp_is_undo_tablespace(space_id)) {
258     mutex_create(LATCH_ID_UNDO_SPACE_RSEG, &rseg->mutex);
259   } else {
260     mutex_create(LATCH_ID_TRX_SYS_RSEG, &rseg->mutex);
261   }
262 
263   UT_LIST_INIT(rseg->update_undo_list, &trx_undo_t::undo_list);
264   UT_LIST_INIT(rseg->update_undo_cached, &trx_undo_t::undo_list);
265   UT_LIST_INIT(rseg->insert_undo_list, &trx_undo_t::undo_list);
266   UT_LIST_INIT(rseg->insert_undo_cached, &trx_undo_t::undo_list);
267 
268   auto rseg_header = trx_rsegf_get_new(space_id, page_no, page_size, mtr);
269 
270   rseg->max_size =
271       mtr_read_ulint(rseg_header + TRX_RSEG_MAX_SIZE, MLOG_4BYTES, mtr);
272 
273   /* Initialize the undo log lists according to the rseg header */
274 
275   auto sum_of_undo_sizes = trx_undo_lists_init(rseg);
276 
277   rseg->set_curr_size(
278       mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr) +
279       1 + sum_of_undo_sizes);
280 
281   auto len = flst_get_len(rseg_header + TRX_RSEG_HISTORY);
282 
283   if (len > 0) {
284     trx_sys->rseg_history_len += len;
285 
286     /* Extract GTID from history and send to GTID persister. */
287     trx_rseg_persist_gtid(rseg, gtid_trx_no);
288 
289     auto node_addr = trx_purge_get_log_from_hist(
290         flst_get_last(rseg_header + TRX_RSEG_HISTORY, mtr));
291 
292     rseg->last_page_no = node_addr.page;
293     rseg->last_offset = node_addr.boffset;
294 
295     auto undo_log_hdr =
296         trx_undo_page_get(page_id_t(rseg->space_id, node_addr.page),
297                           rseg->page_size, mtr) +
298         node_addr.boffset;
299 
300     rseg->last_trx_no = mach_read_from_8(undo_log_hdr + TRX_UNDO_TRX_NO);
301 
302 #ifdef UNIV_DEBUG
303     /* Update last transaction number during recovery. */
304     if (rseg->last_trx_no > trx_sys->rw_max_trx_no) {
305       trx_sys->rw_max_trx_no = rseg->last_trx_no;
306     }
307 #endif /* UNIV_DEBUG */
308 
309     rseg->last_del_marks =
310         mtr_read_ulint(undo_log_hdr + TRX_UNDO_DEL_MARKS, MLOG_2BYTES, mtr);
311 
312     TrxUndoRsegs elem(rseg->last_trx_no);
313     elem.push_back(rseg);
314 
315     if (rseg->last_page_no != FIL_NULL) {
316       /* The only time an rseg is added that has existing
317       undo is when the server is being started. So no
318       mutex is needed here. */
319       ut_ad(srv_is_being_started);
320 
321       ut_ad(space_id == TRX_SYS_SPACE ||
322             (srv_is_upgrade_mode != undo::is_reserved(space_id)));
323 
324       purge_queue->push(elem);
325     }
326   } else {
327     rseg->last_page_no = FIL_NULL;
328   }
329 
330   return (rseg);
331 }
332 
333 /** Return a page number from a slot in the rseg_array page of an
334 undo tablespace.
335 @param[in]	space_id	undo tablespace ID
336 @param[in]	rseg_id		rollback segment ID
337 @return page_no Page number of the rollback segment header page */
trx_rseg_get_page_no(space_id_t space_id,ulint rseg_id)338 page_no_t trx_rseg_get_page_no(space_id_t space_id, ulint rseg_id) {
339   mtr_t mtr;
340   mtr.start();
341 
342   trx_rsegsf_t *rsegs_header = trx_rsegsf_get(space_id, &mtr);
343 
344   page_no_t page_no = trx_rsegsf_get_page_no(rsegs_header, rseg_id, &mtr);
345 
346   mtr.commit();
347 
348   return (page_no);
349 }
350 
351 /** Read each rollback segment slot in the TRX_SYS page and the RSEG_ARRAY
352 page of each undo tablespace. Create trx_rseg_t objects for all rollback
353 segments found.  This runs at database startup and initializes the in-memory
354 lists of trx_rseg_t objects.  We need to look at all slots in TRX_SYS and
355 each RSEG_ARRAY page because we need to look for any existing undo log that
356 may need to be recovered by purge.  No latch is needed since this is still
357 single-threaded startup.  If we find existing rseg slots in TRX_SYS page
358 that reference undo tablespaces and have active undo logs, then quit.
359 They require an upgrade of undo tablespaces and that cannot happen with
360 active undo logs.
361 @param[in]	purge_queue	queue of rsegs to purge */
trx_rsegs_init(purge_pq_t * purge_queue)362 void trx_rsegs_init(purge_pq_t *purge_queue) {
363   trx_sys->rseg_history_len = 0;
364 
365   ulint slot;
366   mtr_t mtr;
367   space_id_t space_id;
368   page_no_t page_no;
369   trx_rseg_t *rseg = nullptr;
370 
371   /* Get GTID transaction number from SYS */
372   mtr.start();
373   trx_sysf_t *sys_header = trx_sysf_get(&mtr);
374   auto page = sys_header - TRX_SYS;
375   auto gtid_trx_no = mach_read_from_8(page + TRX_SYS_TRX_NUM_GTID);
376 
377   mtr.commit();
378 
379   auto &gtid_persistor = clone_sys->get_gtid_persistor();
380   gtid_persistor.set_oldest_trx_no_recovery(gtid_trx_no);
381 
382   for (slot = 0; slot < TRX_SYS_N_RSEGS; slot++) {
383     mtr.start();
384     trx_sysf_t *sys_header = trx_sysf_get(&mtr);
385 
386     page_no = trx_sysf_rseg_get_page_no(sys_header, slot, &mtr);
387 
388     if (page_no != FIL_NULL) {
389       space_id = trx_sysf_rseg_get_space(sys_header, slot, &mtr);
390 
391       if (!undo::is_active_truncate_log_present(undo::id2num(space_id))) {
392         /* Create the trx_rseg_t object.
393         Note that all tablespaces with rollback segments
394         use univ_page_size. (system, temp & undo) */
395         rseg = trx_rseg_mem_create(slot, space_id, page_no, univ_page_size,
396                                    gtid_trx_no, purge_queue, &mtr);
397 
398         ut_a(rseg->id == slot);
399 
400         trx_sys->rsegs.push_back(rseg);
401       }
402     }
403     mtr.commit();
404   }
405 
406   undo::spaces->s_lock();
407   for (auto undo_space : undo::spaces->m_spaces) {
408     /* Remember the size of the purge queue before processing this
409     undo tablespace. */
410     size_t purge_queue_size = purge_queue->size();
411 
412     undo_space->rsegs()->x_lock();
413 
414     for (slot = 0; slot < FSP_MAX_ROLLBACK_SEGMENTS; slot++) {
415       page_no = trx_rseg_get_page_no(undo_space->id(), slot);
416 
417       /* There are no gaps in an RSEG_ARRAY page. New rsegs
418       are added sequentially and never deleted until the
419       undo tablespace is truncated.*/
420       if (page_no == FIL_NULL) {
421         break;
422       }
423 
424       mtr.start();
425 
426       /* Create the trx_rseg_t object.
427       Note that all tablespaces with rollback segments
428       use univ_page_size. */
429       rseg =
430           trx_rseg_mem_create(slot, undo_space->id(), page_no, univ_page_size,
431                               gtid_trx_no, purge_queue, &mtr);
432 
433       ut_a(rseg->id == slot);
434 
435       undo_space->rsegs()->push_back(rseg);
436 
437       mtr.commit();
438     }
439     undo_space->rsegs()->x_unlock();
440 
441     /* If there are no undo logs in this explicit undo tablespace at
442     startup, mark it empty so that it will not be used until the state
443     recorded in the DD can be applied in apply_dd_undo_state(). */
444     if (undo_space->is_explicit() && !undo_space->is_empty()) {
445       size_t cur_size = purge_queue->size();
446       if (purge_queue_size == cur_size) {
447         undo_space->set_empty();
448       }
449     }
450   }
451   undo::spaces->s_unlock();
452 }
453 
454 /** Create a rollback segment in the given tablespace. This could be either
455 the system tablespace, the temporary tablespace, or an undo tablespace.
456 @param[in]	space_id	tablespace to get the rollback segment
457 @param[in]	rseg_id		slot number of the rseg within this tablespace
458 @return page number of the rollback segment header page created */
trx_rseg_create(space_id_t space_id,ulint rseg_id)459 page_no_t trx_rseg_create(space_id_t space_id, ulint rseg_id) {
460   mtr_t mtr;
461   fil_space_t *space = fil_space_get(space_id);
462 
463   log_free_check();
464 
465   mtr_start(&mtr);
466 
467   /* To obey the latching order, acquire the file space
468   x-latch before the mutex for trx_sys. */
469   mtr_x_lock(&space->latch, &mtr);
470 
471   ut_ad(space->purpose == (fsp_is_system_temporary(space_id)
472                                ? FIL_TYPE_TEMPORARY
473                                : FIL_TYPE_TABLESPACE));
474   ut_ad(univ_page_size.equals_to(page_size_t(space->flags)));
475 
476   if (fsp_is_system_temporary(space_id)) {
477     mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO);
478   } else if (space_id == TRX_SYS_SPACE) {
479     /* We will modify TRX_SYS_RSEGS in TRX_SYS page. */
480   }
481 
482   page_no_t page_no = trx_rseg_header_create(space_id, univ_page_size,
483                                              PAGE_NO_MAX, rseg_id, &mtr);
484 
485   mtr_commit(&mtr);
486 
487   return (page_no);
488 }
489 
490 /** Initialize */
init()491 void Rsegs::init() {
492   m_rsegs.reserve(TRX_SYS_N_RSEGS);
493 
494   m_latch = static_cast<rw_lock_t *>(ut_zalloc_nokey(sizeof(*m_latch)));
495 
496   rw_lock_create(rsegs_lock_key, m_latch, SYNC_RSEGS);
497 }
498 
499 /** De-initialize */
deinit()500 void Rsegs::deinit() {
501   clear();
502 
503   rw_lock_free(m_latch);
504   ut_free(m_latch);
505   m_latch = nullptr;
506 }
507 
508 /** Clear the vector of cached rollback segments leaving the
509 reserved space allocated. */
clear()510 void Rsegs::clear() {
511   for (auto rseg : m_rsegs) {
512     trx_rseg_mem_free(rseg);
513   }
514   m_rsegs.clear();
515   m_rsegs.shrink_to_fit();
516 }
517 
518 /** Find an rseg in the std::vector that uses the rseg_id given.
519 @param[in]	rseg_id		A slot in a durable array such as
520 the TRX_SYS page or RSEG_ARRAY page.
521 @return a pointer to an trx_rseg_t that uses the rseg_id. */
find(ulint rseg_id)522 trx_rseg_t *Rsegs::find(ulint rseg_id) {
523   trx_rseg_t *rseg;
524 
525   /* In most cases, the rsegs will be in slot order with no gaps. */
526   if (rseg_id < m_rsegs.size()) {
527     rseg = m_rsegs.at(rseg_id);
528     if (rseg->id == rseg_id) {
529       return (rseg);
530     }
531   }
532 
533   /* If there are gaps in the numbering, do a search. */
534   for (auto rseg : m_rsegs) {
535     if (rseg->id == rseg_id) {
536       return (rseg);
537     }
538   }
539 
540   return (nullptr);
541 }
542 
543 /** This does two things to the target tablespace.
544 1. Find or create (trx_rseg_create) the requested number of rollback segments.
545 2. Make sure each rollback segment is tracked in memory (trx_rseg_mem_create).
546 All existing rollback segments were found earlier in trx_rsegs_init().
547 This will add new ones if we need them according to target_rsegs.
548 @param[in]	space_id	tablespace ID that should contain rollback
549                                 segments
550 @param[in]	target_rsegs	target number of rollback segments per
551                                 tablespace
552 @param[in]	rsegs		list of rsegs to add to
553 @param[in,out] n_total_created  A running total of rollback segment created in
554 undo tablespaces
555 @return true if all rsegs are added, false if not. */
trx_rseg_add_rollback_segments(space_id_t space_id,ulong target_rsegs,Rsegs * rsegs,ulint * const n_total_created)556 bool trx_rseg_add_rollback_segments(space_id_t space_id, ulong target_rsegs,
557                                     Rsegs *rsegs,
558                                     ulint *const n_total_created) {
559   bool success = true;
560   mtr_t mtr;
561   page_no_t page_no;
562   trx_rseg_t *rseg;
563   ulint n_existing = 0;
564   ulint n_created = 0;
565   ulint n_tracked = 0;
566 
567   enum space_type_t { TEMP, UNDO } type;
568 
569   ut_ad(space_id != TRX_SYS_SPACE);
570 
571   type = (fsp_is_undo_tablespace(space_id) ? UNDO : TEMP);
572   ut_ad(type == UNDO || fsp_is_system_temporary(space_id));
573 
574   /* Protect against two threads trying to add rollback segments
575   at the same time. */
576   rsegs->x_lock();
577 
578   for (ulint num = 0; num < FSP_MAX_ROLLBACK_SEGMENTS; num++) {
579     if (rsegs->size() >= target_rsegs) {
580       break;
581     }
582 
583     ulint rseg_id = num;
584 
585     /* If the rseg object exists, move to the next rseg_id. */
586     rseg = rsegs->find(rseg_id);
587     if (rseg != nullptr) {
588       ut_ad(rseg->id == rseg_id);
589       n_existing++;
590       continue;
591     }
592 
593     /* Look in the tablespace to discover if the rollback segment
594     already exists. */
595     if (type == UNDO) {
596       page_no = trx_rseg_get_page_no(space_id, rseg_id);
597 
598     } else {
599       /* There is no durable list of rollback segments in
600       the temporary tablespace. Since it was not found in
601       the rsegs vector, assume the rollback segment does
602       not exist in the temp tablespace. */
603       page_no = FIL_NULL;
604     }
605 
606     if (page_no == FIL_NULL) {
607       /* Create the missing rollback segment if allowed. */
608       if (type == TEMP || (!srv_read_only_mode && srv_force_recovery == 0 &&
609                            !srv_apply_log_only)) {
610         page_no = trx_rseg_create(space_id, rseg_id);
611         if (page_no == FIL_NULL) {
612           /* There may not be enough space in
613           the temporary tablespace since it is
614           possible to limit its size. */
615           ut_ad(type == TEMP);
616           continue;
617         }
618         n_created++;
619       } else {
620         /* trx_rseg_create() is being prevented
621         in an UNDO tablespace. Don't try to create
622         any more. */
623         break;
624       }
625     } else {
626       n_existing++;
627     }
628 
629     /* Create the trx_rseg_t object. */
630     mtr.start();
631 
632     fil_space_t *space = fil_space_get(space_id);
633     ut_ad(univ_page_size.equals_to(page_size_t(space->flags)));
634     mtr_x_lock(&space->latch, &mtr);
635 
636     if (type == TEMP) {
637       mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO);
638     }
639 
640     rseg = trx_rseg_mem_create(rseg_id, space_id, page_no, univ_page_size, 0,
641                                purge_sys->purge_queue, &mtr);
642 
643     mtr.commit();
644 
645     if (rseg != nullptr) {
646       ut_a(rseg->id == rseg_id);
647       rsegs->push_back(rseg);
648       n_tracked++;
649     }
650   }
651 
652   rsegs->x_unlock();
653 
654   std::ostringstream loc;
655   switch (type) {
656     case UNDO:
657       loc << "undo tablespace number " << undo::id2num(space_id);
658       break;
659     case TEMP:
660       loc << "the temporary tablespace";
661       break;
662   }
663 
664   ulint n_known = rsegs->size();
665   if (n_known < target_rsegs) {
666     if (srv_read_only_mode || srv_force_recovery > 0 || srv_apply_log_only) {
667       bool use_and = srv_read_only_mode && srv_force_recovery > 0;
668       bool use_and_second = srv_read_only_mode || srv_force_recovery > 0;
669 
670       ib::info(ER_IB_MSG_1191)
671           << "Could not create all " << target_rsegs << " rollback segments in "
672           << loc.str() << " because "
673           << (srv_read_only_mode ? " read-only mode is set" : "")
674           << (use_and ? " and" : "")
675           << (srv_force_recovery > 0 ? " innodb_force_recovery is set" : "")
676           << (use_and_second ? " and" : "")
677           << (srv_apply_log_only ? " --apply-log-only is set" : "")
678           << ". Only " << n_known << " are active.";
679 
680       srv_rollback_segments =
681           ut_min(srv_rollback_segments, static_cast<ulong>(n_known));
682 
683     } else {
684       ib::warn(ER_IB_MSG_1192)
685           << "Could not create all " << target_rsegs << " rollback segments in "
686           << loc.str() << ". Only " << n_known << " are active.";
687 
688       srv_rollback_segments =
689           ut_min(srv_rollback_segments, static_cast<ulong>(n_known));
690 
691       success = false;
692     }
693 
694   } else if (n_created > 0) {
695     ib::info(ER_IB_MSG_1193)
696         << "Created " << n_created << " and tracked " << n_tracked
697         << " new rollback segment(s) in " << loc.str() << ". " << target_rsegs
698         << " are now active.";
699 
700   } else if (n_tracked > 0) {
701     ib::info(ER_IB_MSG_1194)
702         << "Using " << n_tracked << " more rollback segment(s) in " << loc.str()
703         << ". " << target_rsegs << " are now active.";
704 
705   } else if (target_rsegs < n_known) {
706     ib::info(ER_IB_MSG_1195)
707         << target_rsegs << " rollback segment(s) are now active in "
708         << loc.str() << ".";
709   }
710 
711   if (n_total_created != nullptr) {
712     *n_total_created += n_created;
713   }
714 
715   return (success);
716 }
717 
718 /** Add more rsegs to the rseg list in each tablespace until there are
719 srv_rollback_segments of them.  Use any rollback segment that already
720 exists so that the purge_queue can be filled and processed with any
721 existing undo log. If the rollback segments do not exist in this
722 tablespace and we need them according to target_rollback_segments,
723 then build them in the tablespace.
724 @param[in]	target_rollback_segments	new number of rollback
725                                                 segments per space
726 @return true if all necessary rollback segments and trx_rseg_t objects
727 were created. */
trx_rseg_adjust_rollback_segments(ulong target_rollback_segments)728 bool trx_rseg_adjust_rollback_segments(ulong target_rollback_segments) {
729   /** The number of rollback segments created in the datafile. */
730   ulint n_total_created = 0;
731 
732   /* Make sure Temporary Tablespace has enough rsegs. */
733   if (!trx_rseg_add_rollback_segments(srv_tmp_space.space_id(),
734                                       target_rollback_segments,
735                                       &(trx_sys->tmp_rsegs), nullptr)) {
736     return (false);
737   }
738 
739   /* Only the temp rsegs are used with a high force_recovery. */
740   if (srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) {
741     return (true);
742   }
743 
744   /* Adjust the number of rollback segments in each Undo Tablespace
745   whether or not it is currently active. If rollback segments are written
746   to the tablespace, they will be checkpointed. But we cannot hold
747   undo::spaces->s_lock while doing a checkpoint because of latch order
748   violation.  So traverse the list by ID. */
749   undo::spaces->s_lock();
750   for (auto undo_space : undo::spaces->m_spaces) {
751     if (!trx_rseg_add_rollback_segments(
752             undo_space->id(), target_rollback_segments, undo_space->rsegs(),
753             &n_total_created)) {
754       undo::spaces->s_unlock();
755       return (false);
756     }
757   }
758   undo::spaces->s_unlock();
759 
760   /* Make sure these rollback segments are checkpointed. */
761   if (n_total_created > 0 && !srv_read_only_mode && srv_force_recovery == 0) {
762     log_make_latest_checkpoint();
763   }
764 
765   return (true);
766 }
767 
768 /** Create the requested number of Rollback Segments in the undo tablespace
769 and add them to the Rsegs object.
770 @param[in]  space_id                  undo tablespace ID
771 @param[in]  target_rollback_segments  number of rollback segments per space
772 @return true if all necessary rollback segments and trx_rseg_t objects
773 were created. */
trx_rseg_init_rollback_segments(space_id_t space_id,ulong target_rollback_segments)774 bool trx_rseg_init_rollback_segments(space_id_t space_id,
775                                      ulong target_rollback_segments) {
776   /** The number of rollback segments created in the datafile. */
777   ulint n_total_created = 0;
778 
779   undo::spaces->s_lock();
780   space_id_t space_num = undo::id2num(space_id);
781   undo::Tablespace *undo_space = undo::spaces->find(space_num);
782   undo::spaces->s_unlock();
783 
784   if (!trx_rseg_add_rollback_segments(space_id, target_rollback_segments,
785                                       undo_space->rsegs(), &n_total_created)) {
786     return (false);
787   }
788 
789   return (true);
790 }
791 
792 /** Build a list of unique undo tablespaces found in the TRX_SYS page.
793 Do not count the system tablespace. The vector will be sorted on space id.
794 @param[in,out]	spaces_to_open		list of undo tablespaces found. */
trx_rseg_get_n_undo_tablespaces(Space_Ids * spaces_to_open)795 void trx_rseg_get_n_undo_tablespaces(Space_Ids *spaces_to_open) {
796   ulint i;
797   mtr_t mtr;
798   trx_sysf_t *sys_header;
799 
800   ut_ad(spaces_to_open->empty());
801 
802   mtr_start(&mtr);
803 
804   sys_header = trx_sysf_get(&mtr);
805 
806   for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
807     page_no_t page_no;
808     space_id_t space_id;
809 
810     page_no = trx_sysf_rseg_get_page_no(sys_header, i, &mtr);
811 
812     if (page_no == FIL_NULL) {
813       continue;
814     }
815 
816     space_id = trx_sysf_rseg_get_space(sys_header, i, &mtr);
817 
818     /* The system space id should not be in this array. */
819     if (space_id != TRX_SYS_SPACE && !spaces_to_open->contains(space_id)) {
820       spaces_to_open->push_back(space_id);
821     }
822   }
823 
824   mtr_commit(&mtr);
825 
826   ut_a(spaces_to_open->size() <= TRX_SYS_N_RSEGS);
827 }
828 
829 /** Upgrade the TRX_SYS page so that it no longer tracks rsegs in undo
830 tablespaces. It should only track rollback segments in the system tablespace.
831 Put FIL_NULL in the slots in TRX_SYS. Latch protection is not needed since
832 this is during single-threaded startup. */
trx_rseg_upgrade_undo_tablespaces()833 void trx_rseg_upgrade_undo_tablespaces() {
834   ulint i;
835   mtr_t mtr;
836   trx_sysf_t *sys_header;
837 
838   mtr_start(&mtr);
839   fil_space_t *space = fil_space_get(TRX_SYS_SPACE);
840   mtr_x_lock(&space->latch, &mtr);
841 
842   sys_header = trx_sysf_get(&mtr);
843 
844   /* First, put FIL_NULL in all the slots that contain the space_id
845   of any non-system tablespace. The rollback segments in those
846   tablespaces are replaced when the file is replaced. */
847   for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
848     page_no_t page_no;
849     space_id_t space_id;
850 
851     page_no = trx_sysf_rseg_get_page_no(sys_header, i, &mtr);
852 
853     if (page_no == FIL_NULL) {
854       continue;
855     }
856 
857     space_id = trx_sysf_rseg_get_space(sys_header, i, &mtr);
858 
859     /* The TRX_SYS page only tracks older undo tablespaces
860     that do not use the RSEG_ARRAY page. */
861     ut_a(space_id < dict_sys_t::s_min_undo_space_id);
862 
863     /* Leave rollback segments in the system tablespace
864     untouched in case innodb_undo_tablespaces is later
865     set back to 0. */
866     if (space_id != 0) {
867       trx_sysf_rseg_set_space(sys_header, i, FIL_NULL, &mtr);
868 
869       trx_sysf_rseg_set_page_no(sys_header, i, FIL_NULL, &mtr);
870     }
871   }
872 
873   mtr_commit(&mtr);
874 }
875 
876 /** Create the file page for the rollback segment directory in an undo
877 tablespace. This function is called just after an undo tablespace is
878 created so the next page created here should by FSP_FSEG_DIR_PAGE_NUM.
879 @param[in]	space_id	Undo Tablespace ID
880 @param[in]	mtr		mtr */
trx_rseg_array_create(space_id_t space_id,mtr_t * mtr)881 void trx_rseg_array_create(space_id_t space_id, mtr_t *mtr) {
882   trx_rsegsf_t *rsegs_header;
883   buf_block_t *block;
884   page_t *page;
885   byte *ptr;
886   ulint len;
887 
888   /* Create the fseg directory file block in a new allocated file segment */
889   block = fseg_create(space_id, 0,
890                       RSEG_ARRAY_HEADER + RSEG_ARRAY_FSEG_HEADER_OFFSET, mtr);
891   buf_block_dbg_add_level(block, SYNC_RSEG_ARRAY_HEADER);
892 
893   ut_a(block->page.id.page_no() == FSP_RSEG_ARRAY_PAGE_NO);
894 
895   page = buf_block_get_frame(block);
896 
897   mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_RSEG_ARRAY, MLOG_2BYTES,
898                    mtr);
899 
900   rsegs_header = page + RSEG_ARRAY_HEADER;
901 
902   /* Initialize the rseg array version. */
903   mach_write_to_4(rsegs_header + RSEG_ARRAY_VERSION_OFFSET, RSEG_ARRAY_VERSION);
904 
905   /* Initialize the directory size. */
906   mach_write_to_4(rsegs_header + RSEG_ARRAY_SIZE_OFFSET, 0);
907 
908   /* Reset the rollback segment header page slots. Use the full page
909   minus overhead.  Reserve some extra room for future use.  */
910   ptr = RSEG_ARRAY_PAGES_OFFSET + rsegs_header;
911   len = UNIV_PAGE_SIZE - RSEG_ARRAY_HEADER - RSEG_ARRAY_PAGES_OFFSET -
912         RSEG_ARRAY_RESERVED_BYTES - FIL_PAGE_DATA_END;
913   memset(ptr, 0xff, len);
914 
915   mlog_log_string(rsegs_header,
916                   UNIV_PAGE_SIZE - RSEG_ARRAY_HEADER - FIL_PAGE_DATA_END, mtr);
917 }
918 
919 #ifdef UNIV_DEBUG
validate_curr_size(bool take_mutex)920 bool trx_rseg_t::validate_curr_size(bool take_mutex) {
921   mtr_t mtr;
922   mtr_start(&mtr);
923 
924   if (take_mutex) {
925     mutex_enter(&mutex);
926   } else {
927     ut_ad(mutex_own(&mutex));
928   }
929 
930   /* Obtain the rollback segment header. */
931   trx_rsegf_t *rseg_hdr = trx_rsegf_get(space_id, page_no, page_size, &mtr);
932 
933   /* Number of file pages occupied by the logs in the history list */
934   ulint hist_size =
935       mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr);
936 
937   ulint sum_undo_size = 0;
938 
939   for (ulint i = 0; i < TRX_RSEG_N_SLOTS; i++) {
940     /* Get the file page number of the nth undo log slot. */
941     page_no_t undo_page_no = trx_rsegf_get_nth_undo(rseg_hdr, i, &mtr);
942 
943     if (undo_page_no == FIL_NULL) {
944       /* Skip the empty slot. */
945       continue;
946     }
947 
948     /* Get the undo log page. */
949     page_t *undo_page =
950         trx_undo_page_get(page_id_t(space_id, undo_page_no), page_size, &mtr);
951 
952     /* Obtain the undo log segment header. */
953     trx_usegf_t *seg_header = undo_page + TRX_UNDO_SEG_HDR;
954 
955     /* Get the number of pages in the undo log segment. */
956     ulint undo_size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST);
957 
958     sum_undo_size += undo_size;
959   }
960 
961   if (take_mutex) {
962     mutex_exit(&mutex);
963   }
964   mtr_commit(&mtr);
965 
966   ulint total_size = sum_undo_size + hist_size + 1;
967 
968   ut_ad(total_size == curr_size);
969 
970   return (total_size == curr_size);
971 }
972 #endif /* UNIV_DEBUG */
973