1 /*****************************************************************************
2
3 Copyright (c) 1996, 2020, 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 include/trx0purge.h
28 Purge old versions
29
30 Created 3/26/1996 Heikki Tuuri
31 *******************************************************/
32
33 #ifndef trx0purge_h
34 #define trx0purge_h
35
36 #include "fil0fil.h"
37 #include "mtr0mtr.h"
38 #include "page0page.h"
39 #include "que0types.h"
40 #include "read0types.h"
41 #include "trx0sys.h"
42 #include "trx0types.h"
43 #include "univ.i"
44 #include "usr0sess.h"
45 #ifdef UNIV_HOTBACKUP
46 #include "trx0sys.h"
47 #endif /* UNIV_HOTBACKUP */
48
49 /** The global data structure coordinating a purge */
50 extern trx_purge_t *purge_sys;
51
52 /** Calculates the file address of an undo log header when we have the file
53 address of its history list node.
54 @return file address of the log */
55 UNIV_INLINE
56 fil_addr_t trx_purge_get_log_from_hist(
57 fil_addr_t node_addr); /*!< in: file address of the history
58 list node of the log */
59
60 /** Creates the global purge system control structure and inits the history
61 mutex.
62 @param[in] n_purge_threads number of purge threads
63 @param[in,out] purge_queue UNDO log min binary heap */
64 void trx_purge_sys_create(ulint n_purge_threads, purge_pq_t *purge_queue);
65
66 /** Frees the global purge system control structure. */
67 void trx_purge_sys_close(void);
68
69 /************************************************************************
70 Adds the update undo log as the first log in the history list. Removes the
71 update undo log segment from the rseg slot if it is too big for reuse. */
72 void trx_purge_add_update_undo_to_history(
73 trx_t *trx, /*!< in: transaction */
74 trx_undo_ptr_t *undo_ptr, /*!< in: update undo log. */
75 page_t *undo_page, /*!< in: update undo log header page,
76 x-latched */
77 bool update_rseg_history_len,
78 /*!< in: if true: update rseg history
79 len else skip updating it. */
80 ulint n_added_logs, /*!< in: number of logs added */
81 mtr_t *mtr); /*!< in: mtr */
82
83 /** This function runs a purge batch.
84 @return number of undo log pages handled in the batch */
85 ulint trx_purge(ulint n_purge_threads, /*!< in: number of purge tasks to
86 submit to task queue. */
87 ulint limit, /*!< in: the maximum number of
88 records to purge in one batch */
89 bool truncate); /*!< in: truncate history if true */
90
91 /** Stop purge and wait for it to stop, move to PURGE_STATE_STOP. */
92 void trx_purge_stop(void);
93 /** Resume purge, move to PURGE_STATE_RUN. */
94 void trx_purge_run(void);
95
96 /** Purge states */
97 enum purge_state_t {
98 PURGE_STATE_INIT, /*!< Purge instance created */
99 PURGE_STATE_RUN, /*!< Purge should be running */
100 PURGE_STATE_STOP, /*!< Purge should be stopped */
101 PURGE_STATE_EXIT, /*!< Purge has been shutdown */
102 PURGE_STATE_DISABLED /*!< Purge was never started */
103 };
104
105 /** Get the purge state.
106 @return purge state. */
107 purge_state_t trx_purge_state(void);
108
109 // Forward declaration
110 struct TrxUndoRsegsIterator;
111
112 /** This is the purge pointer/iterator. We need both the undo no and the
113 transaction no up to which purge has parsed and applied the records. */
114 struct purge_iter_t {
purge_iter_tpurge_iter_t115 purge_iter_t() : trx_no(), undo_no(), undo_rseg_space(SPACE_UNKNOWN) {
116 // Do nothing
117 }
118
119 trx_id_t trx_no; /*!< Purge has advanced past all
120 transactions whose number is less
121 than this */
122 undo_no_t undo_no; /*!< Purge has advanced past all records
123 whose undo number is less than this */
124 space_id_t undo_rseg_space;
125 /*!< Last undo record resided in this
126 space id. */
127 trx_id_t modifier_trx_id;
128 /*!< the transaction that created the
129 undo log record. Modifier trx id.*/
130 };
131
132 /* Namespace to hold all the related functions and variables needed
133 to truncate an undo tablespace. */
134 namespace undo {
135
136 /** Magic Number to indicate truncate action is complete. */
137 const ib_uint32_t s_magic = 76845412;
138
139 /** Truncate Log file Prefix. */
140 const char *const s_log_prefix = "undo_";
141
142 /** Truncate Log file Extension. */
143 const char *const s_log_ext = "trunc.log";
144
145 /** The currently used undo space IDs for an undo space number
146 along with a boolean showing whether the undo space number is in use. */
147 struct space_id_account {
148 space_id_t space_id;
149 bool in_use;
150 };
151
152 /** List of currently used undo space IDs for each undo space number
153 along with a boolean showing whether the undo space number is in use. */
154 extern struct space_id_account *space_id_bank;
155
156 /** Check if the space_id is an undo space ID in the reserved range.
157 @param[in] space_id undo tablespace ID
158 @return true if it is in the reserved undo space ID range. */
is_reserved(space_id_t space_id)159 inline bool is_reserved(space_id_t space_id) {
160 return (space_id >= dict_sys_t::s_min_undo_space_id &&
161 space_id <= dict_sys_t::s_max_undo_space_id);
162 }
163
164 /** Convert an undo space number (from 1 to 127) into the undo space_id,
165 given an index indicating which space_id from the pool assigned to that
166 undo number.
167 @param[in] space_num undo tablespace number
168 @param[in] ndx index of the space_id within that undo number
169 @return space_id of the undo tablespace */
num2id(space_id_t space_num,size_t ndx)170 inline space_id_t num2id(space_id_t space_num, size_t ndx) {
171 ut_ad(space_num > 0);
172 ut_ad(space_num <= FSP_MAX_UNDO_TABLESPACES);
173 ut_ad(ndx < dict_sys_t::undo_space_id_range);
174
175 space_id_t space_id = dict_sys_t::s_max_undo_space_id + 1 - space_num -
176 static_cast<space_id_t>(ndx * FSP_MAX_UNDO_TABLESPACES);
177
178 return (space_id);
179 }
180
181 /** Convert an undo space number (from 1 to 127) into an undo space_id.
182 Use the undo::space_id_bank to return the curent space_id assigned to
183 that undo number.
184 @param[in] space_num undo tablespace number
185 @return space_id of the undo tablespace */
num2id(space_id_t space_num)186 inline space_id_t num2id(space_id_t space_num) {
187 ut_ad(space_num > 0);
188 ut_ad(space_num <= FSP_MAX_UNDO_TABLESPACES);
189
190 size_t slot = space_num - 1;
191
192 /* The space_id_back is normally protected by undo::spaces::m_latch.
193 But this can only be called on a specific slot when truncation is not
194 happening on that slot, i.e. the undo tablespace is in use. */
195 ut_ad(undo::space_id_bank[slot].in_use);
196
197 return (undo::space_id_bank[slot].space_id);
198 }
199
200 /* clang-format off */
201 /** Convert an undo space ID into an undo space number.
202 NOTE: This may be an undo space_id from a pre-exisiting 5.7
203 database which used space_ids from 1 to 127. If so, the
204 space_id is the space_num.
205 The space_ids are assigned to number ranges in reverse from high to low.
206 In addition, the first space IDs for each undo number occur sequentionally
207 and descending before the second space_id.
208
209 Since s_max_undo_space_id = 0xFFFFFFEF, FSP_MAX_UNDO_TABLESPACES = 127
210 and undo_space_id_range = 512:
211 Space ID Space Num Space ID Space Num ... Space ID Space Num
212 0xFFFFFFEF 1 0xFFFFFFEe 2 ... 0xFFFFFF71 127
213 0xFFFFFF70 1 0xFFFFFF6F 2 ... 0xFFFFFEF2 127
214 0xFFFFFEF1 1 0xFFFFFEF0 2 ... 0xFFFFFE73 127
215 ...
216
217 This is done to maintain backward compatibility to when there was only one
218 space_id per undo space number.
219 @param[in] space_id undo tablespace ID
220 @return space number of the undo tablespace */
221 /* clang-format on */
id2num(space_id_t space_id)222 inline space_id_t id2num(space_id_t space_id) {
223 if (!is_reserved(space_id)) {
224 return (space_id);
225 }
226
227 return (((dict_sys_t::s_max_undo_space_id - space_id) %
228 FSP_MAX_UNDO_TABLESPACES) +
229 1);
230 }
231
232 /* Given a reserved undo space_id, return the next space_id for the associated
233 undo space number. */
id2next_id(space_id_t space_id)234 inline space_id_t id2next_id(space_id_t space_id) {
235 ut_ad(is_reserved(space_id));
236
237 space_id_t space_num = id2num(space_id);
238 space_id_t first_id = dict_sys_t::s_max_undo_space_id + 1 - space_num;
239 space_id_t last_id = first_id - (FSP_MAX_UNDO_TABLESPACES *
240 (dict_sys_t::undo_space_id_range - 1));
241
242 return (space_id == SPACE_UNKNOWN || space_id == last_id
243 ? first_id
244 : space_id - FSP_MAX_UNDO_TABLESPACES);
245 }
246
247 /** Initialize the undo tablespace space_id bank which is a lock free
248 repository for information about the space IDs used for undo tablespaces.
249 It is used during creation in order to assign an unused space number and
250 during truncation in order to assign the next space_id within that
251 space_number range. */
252 void init_space_id_bank();
253
254 /** Note that the undo space number for a space ID is being used.
255 Put that space_id into the space_id_bank.
256 @param[in] space_id undo tablespace number */
257 void use_space_id(space_id_t space_id);
258
259 /** Mark that the given undo space number is being used and
260 return the next available space_id for that space number.
261 @param[in] space_num undo tablespace number
262 @return the next tablespace ID to use */
263 space_id_t use_next_space_id(space_id_t space_num);
264
265 /** Mark an undo number associated with a given space_id as unused and
266 available to be resused. This happens when the fil_space_t is closed
267 associated with a drop undo tablespace.
268 @param[in] space_id Undo Tablespace ID */
269 void unuse_space_id(space_id_t space_id);
270
271 /** Given a valid undo space_id or SPACE_UNKNOWN, return the next space_id
272 for the given space number.
273 @param[in] space_id undo tablespace ID
274 @param[in] space_num undo tablespace number
275 @return the next tablespace ID to use */
276 space_id_t next_space_id(space_id_t space_id, space_id_t space_num);
277
278 /** Given a valid undo space_id, return the next space_id for that
279 space number.
280 @param[in] space_id undo tablespace ID
281 @return the next tablespace ID to use */
282 space_id_t next_space_id(space_id_t space_id);
283
284 /** Return the next available undo space ID to be used for a new explicit
285 undo tablespaces.
286 @retval if success, next available undo space number.
287 @retval if failure, SPACE_UNKNOWN */
288 space_id_t get_next_available_space_num();
289
290 /** Build a standard undo tablespace name from a space_id.
291 @param[in] space_id id of the undo tablespace.
292 @return tablespace name of the undo tablespace file */
293 char *make_space_name(space_id_t space_id);
294
295 /** Build a standard undo tablespace file name from a space_id.
296 @param[in] space_id id of the undo tablespace.
297 @return file_name of the undo tablespace file */
298 char *make_file_name(space_id_t space_id);
299
300 /** An undo::Tablespace object is used to easily convert between
301 undo_space_id and undo_space_num and to create the automatic file_name
302 and space name. In addition, it is used in undo::Tablespaces to track
303 the trx_rseg_t objects in an Rsegs vector. So we do not allocate the
304 Rsegs vector for each object, only when requested by the constructor. */
305 struct Tablespace {
306 /** Constructor
307 @param[in] id tablespace id */
TablespaceTablespace308 explicit Tablespace(space_id_t id)
309 : m_id(id),
310 m_num(undo::id2num(id)),
311 m_implicit(true),
312 m_new(false),
313 m_space_name(),
314 m_file_name(),
315 m_log_file_name(),
316 m_rsegs() {}
317
318 /** Copy Constructor
319 @param[in] other undo tablespace to copy */
TablespaceTablespace320 Tablespace(Tablespace &other)
321 : m_id(other.id()),
322 m_num(undo::id2num(other.id())),
323 m_implicit(other.is_implicit()),
324 m_new(other.is_new()),
325 m_space_name(),
326 m_file_name(),
327 m_log_file_name(),
328 m_rsegs() {
329 ut_ad(m_id == 0 || is_reserved(m_id));
330
331 set_space_name(other.space_name());
332 set_file_name(other.file_name());
333
334 /* When the copy constructor is used, add an Rsegs
335 vector. This constructor is only used in the global
336 undo::Tablespaces object where rollback segments are
337 tracked. */
338 m_rsegs = UT_NEW_NOKEY(Rsegs());
339 }
340
341 /** Destructor */
~TablespaceTablespace342 ~Tablespace() {
343 if (m_space_name != nullptr) {
344 ut_free(m_space_name);
345 m_space_name = nullptr;
346 }
347
348 if (m_file_name != nullptr) {
349 ut_free(m_file_name);
350 m_file_name = nullptr;
351 }
352
353 if (m_log_file_name != nullptr) {
354 ut_free(m_log_file_name);
355 m_log_file_name = nullptr;
356 }
357
358 /* Clear the cached rollback segments. */
359 if (m_rsegs != nullptr) {
360 UT_DELETE(m_rsegs);
361 m_rsegs = nullptr;
362 }
363 }
364
365 /* Determine if this undo space needs to be truncated.
366 @return true if it should be truncated, false if not. */
367 bool needs_truncation();
368
369 /** Change the space_id from its current value.
370 @param[in] space_id The new undo tablespace ID */
371 void set_space_id(space_id_t space_id);
372
373 /** Replace the standard undo space name if it exists with a copy
374 of the undo tablespace name provided.
375 @param[in] new_space_name non-standard undo space name */
376 void set_space_name(const char *new_space_name);
377
378 /** Get the undo tablespace name. Make it if not yet made.
379 NOTE: This is only called from stack objects so there is no
380 race condition. If it is ever called from a shared object
381 like undo::spaces, then it must be protected by the caller.
382 @return tablespace name created from the space_id */
space_nameTablespace383 char *space_name() {
384 if (m_space_name == nullptr) {
385 #ifndef UNIV_HOTBACKUP
386 m_space_name = make_space_name(m_id);
387 #endif /* !UNIV_HOTBACKUP */
388 }
389
390 return (m_space_name);
391 }
392
393 /** Replace the standard undo file name if it exists with a copy
394 of the file name provided. This name can come in three forms:
395 absolute path, relative path, and basename. Undo ADD DATAFILE
396 does not accept a relative path. So if that comes in here, it
397 was the scaneed name and is relative to the datadir.
398 If this is just a basename, add it to srv_undo_dir.
399 @param[in] file_name explicit undo file name */
400 void set_file_name(const char *file_name);
401
402 /** Get the undo space filename. Make it if not yet made.
403 NOTE: This is only called from stack objects so there is no
404 race condition. If it is ever called from a shared object
405 like undo::spaces, then it must be protected by the caller.
406 @return tablespace filename created from the space_id */
file_nameTablespace407 char *file_name() {
408 if (m_file_name == nullptr) {
409 m_file_name = make_file_name(m_id);
410 }
411
412 return (m_file_name);
413 }
414
415 /** Build a log file name based on space_id
416 @param[in] space_id id of the undo tablespace.
417 @return DB_SUCCESS or error code */
418 char *make_log_file_name(space_id_t space_id);
419
420 /** Get the undo log filename. Make it if not yet made.
421 NOTE: This is only called from stack objects so there is no
422 race condition. If it is ever called from a shared object
423 like undo::spaces, then it must be protected by the caller.
424 @return tablespace filename created from the space_id */
log_file_nameTablespace425 char *log_file_name() {
426 if (m_log_file_name == nullptr) {
427 m_log_file_name = make_log_file_name(m_id);
428 }
429
430 return (m_log_file_name);
431 }
432
433 /** Get the undo tablespace ID.
434 @return tablespace ID */
idTablespace435 space_id_t id() { return (m_id); }
436
437 /** Get the undo tablespace number. This is the same as m_id
438 if m_id is 0 or this is a v5.6-5.7 undo tablespace. v8+ undo
439 tablespaces use a space_id from the reserved range.
440 @return undo tablespace number */
numTablespace441 space_id_t num() {
442 ut_ad(m_num < FSP_MAX_ROLLBACK_SEGMENTS);
443
444 return (m_num);
445 }
446
447 /** Get a reference to the List of rollback segments within
448 this undo tablespace.
449 @return a reference to the Rsegs vector. */
rsegsTablespace450 Rsegs *rsegs() { return (m_rsegs); }
451
452 /** Report whether this undo tablespace was explicitly created
453 by an SQL statement.
454 @return true if the tablespace was created explicitly. */
is_explicitTablespace455 bool is_explicit() { return (!m_implicit); }
456
457 /** Report whether this undo tablespace was implicitly created.
458 @return true if the tablespace was created implicitly. */
is_implicitTablespace459 bool is_implicit() { return (m_implicit); }
460
461 /** Report whether this undo tablespace was created at startup.
462 @retval true if created at startup.
463 @retval false if pre-existed at startup. */
is_newTablespace464 bool is_new() { return (m_new); }
465
466 /** Note that this undo tablespace is being created. */
set_newTablespace467 void set_new() { m_new = true; }
468
469 /** Return whether the undo tablespace is active.
470 @return true if active */
is_activeTablespace471 bool is_active() {
472 if (m_rsegs == nullptr) {
473 return (false);
474 }
475 m_rsegs->s_lock();
476 bool ret = m_rsegs->is_active();
477 m_rsegs->s_unlock();
478 return (ret);
479 }
480
481 /** Return whether the undo tablespace is active. For optimization purposes,
482 do not take a latch.
483 @return true if active */
is_active_no_latchTablespace484 bool is_active_no_latch() {
485 if (m_rsegs == nullptr) {
486 return (false);
487 }
488 return (m_rsegs->is_active());
489 }
490
491 /** Return the rseg at the requested rseg slot if the undo space is active.
492 @param[in] slot The slot of the rseg. 1 to 127
493 @return Rseg pointer of nullptr if the space is not active. */
get_activeTablespace494 trx_rseg_t *get_active(ulint slot) {
495 m_rsegs->s_lock();
496 if (!m_rsegs->is_active()) {
497 m_rsegs->s_unlock();
498 return (nullptr);
499 }
500
501 /* Mark the chosen rseg so that it will not be selected
502 for UNDO truncation. */
503 trx_rseg_t *rseg = m_rsegs->at(slot);
504 rseg->trx_ref_count++;
505
506 m_rsegs->s_unlock();
507
508 return (rseg);
509 }
510
511 /** Return whether the undo tablespace is inactive due to
512 implicit selection by the purge thread.
513 @return true if marked for truncation by the purge thread */
is_inactive_implicitTablespace514 bool is_inactive_implicit() {
515 if (m_rsegs == nullptr) {
516 return (false);
517 }
518 m_rsegs->s_lock();
519 bool ret = m_rsegs->is_inactive_implicit();
520 m_rsegs->s_unlock();
521 return (ret);
522 }
523
524 /** Return whether the undo tablespace was made inactive by
525 ALTER TABLESPACE.
526 @return true if altered inactive */
is_inactive_explicitTablespace527 bool is_inactive_explicit() {
528 if (m_rsegs == nullptr) {
529 return (false);
530 }
531 m_rsegs->s_lock();
532 bool ret = m_rsegs->is_inactive_explicit();
533 m_rsegs->s_unlock();
534 return (ret);
535 }
536
537 /** Return whether the undo tablespace is empty and ready
538 to be dropped.
539 @return true if empty */
is_emptyTablespace540 bool is_empty() {
541 if (m_rsegs == nullptr) {
542 return (true);
543 }
544 m_rsegs->s_lock();
545 bool ret = m_rsegs->is_empty();
546 m_rsegs->s_unlock();
547 return (ret);
548 }
549
550 /** Set the undo tablespace active for use by transactions. */
set_activeTablespace551 void set_active() {
552 m_rsegs->x_lock();
553 m_rsegs->set_active();
554 m_rsegs->x_unlock();
555 }
556
557 /** Set the state of the rollback segments in this undo tablespace to
558 inactive_implicit if currently active. If the state is inactive_explicit,
559 leave as is. Then put the space_id into the callers marked_space_id.
560 This is done when marking a space for truncate. It will not be used
561 for new transactions until it becomes active again. */
set_inactive_implicitTablespace562 void set_inactive_implicit(space_id_t *marked_space_id) {
563 m_rsegs->x_lock();
564 if (m_rsegs->is_active()) {
565 m_rsegs->set_inactive_implicit();
566 }
567 *marked_space_id = m_id;
568
569 m_rsegs->x_unlock();
570 }
571
572 /** Make the undo tablespace inactive so that it will not be
573 used for new transactions. The purge thread will clear out
574 all the undo logs, truncate it, and then mark it empty. */
set_inactive_explicitTablespace575 void set_inactive_explicit() {
576 m_rsegs->x_lock();
577 m_rsegs->set_inactive_explicit();
578 m_rsegs->x_unlock();
579 }
580
581 /** Make the undo tablespace active again so that it will
582 be used for new transactions.
583 If current State is ___ then do:
584 empty: Set active.
585 active_implicit: Ignore. It was not altered inactive. When it is done
586 being truncated it will go back to active.
587 active_explicit: Depends if it is marked for truncation.
588 marked: Set to inactive_implicit. the next state will be active.
589 not yet: Set to active so that it does not get truncated. */
590 void alter_active();
591
592 /** Set the state of the undo tablespace to empty so that it
593 can be dropped. */
set_emptyTablespace594 void set_empty() {
595 m_rsegs->x_lock();
596 m_rsegs->set_empty();
597 m_rsegs->x_unlock();
598 }
599
600 private:
601 /** Undo Tablespace ID. */
602 space_id_t m_id;
603
604 /** Undo Tablespace number, from 1 to 127. This is the
605 7-bit number that is used in a rollback pointer.
606 Use id2num() to get this number from a space_id. */
607 space_id_t m_num;
608
609 /** True if this is an implicit undo tablespace */
610 bool m_implicit;
611
612 /** True if this undo tablespace was implicitly created when
613 this instance started up. False if it pre-existed. */
614 bool m_new;
615
616 /** The tablespace name, auto-generated when needed from
617 the space number. */
618 char *m_space_name;
619
620 /** The tablespace file name, auto-generated when needed
621 from the space number. */
622 char *m_file_name;
623
624 /** The tablespace log file name, auto-generated when needed
625 from the space number. */
626 char *m_log_file_name;
627
628 /** List of rollback segments within this tablespace.
629 This is not always used. Must call init_rsegs to use it. */
630 Rsegs *m_rsegs;
631 };
632
633 /** List of undo tablespaces, each containing a list of
634 rollback segments. */
635 class Tablespaces {
636 using Tablespaces_Vector =
637 std::vector<Tablespace *, ut_allocator<Tablespace *>>;
638
639 public:
Tablespaces()640 Tablespaces() { init(); }
641
~Tablespaces()642 ~Tablespaces() { deinit(); }
643
644 /** Initialize */
645 void init();
646
647 /** De-initialize */
648 void deinit();
649
650 /** Clear the contents of the list of Tablespace objects.
651 This does not deallocate any memory. */
clear()652 void clear() {
653 for (auto undo_space : m_spaces) {
654 UT_DELETE(undo_space);
655 }
656 m_spaces.clear();
657 }
658
659 /** Get the number of tablespaces tracked by this object. */
size()660 ulint size() { return (m_spaces.size()); }
661
662 /** See if the list of tablespaces is empty. */
empty()663 bool empty() { return (m_spaces.empty()); }
664
665 /** Get the Tablespace tracked at a position. */
at(size_t pos)666 Tablespace *at(size_t pos) { return (m_spaces.at(pos)); }
667
668 /** Add a new undo::Tablespace to the back of the vector.
669 The vector has been pre-allocated to 128 so read threads will
670 not loose what is pointed to. If tablespace_name and file_name
671 are standard names, they are optional.
672 @param[in] ref_undo_space undo tablespace */
673 void add(Tablespace &ref_undo_space);
674
675 /** Drop an existing explicit undo::Tablespace.
676 @param[in] undo_space pointer to undo space */
677 void drop(Tablespace *undo_space);
678
679 /** Drop an existing explicit undo::Tablespace.
680 @param[in] ref_undo_space reference to undo space */
681 void drop(Tablespace &ref_undo_space);
682
683 /** Check if the given space_id is in the vector.
684 @param[in] num undo tablespace number
685 @return true if space_id is found, else false */
contains(space_id_t num)686 bool contains(space_id_t num) { return (find(num) != nullptr); }
687
688 /** Find the given space_num in the vector.
689 @param[in] num undo tablespace number
690 @return pointer to an undo::Tablespace struct */
find(space_id_t num)691 Tablespace *find(space_id_t num) {
692 if (m_spaces.empty()) {
693 return (nullptr);
694 }
695
696 /* The sort method above puts this vector in order by
697 Tablespace::num. If there are no gaps, then we should
698 be able to find it quickly. */
699 space_id_t slot = num - 1;
700 if (slot < m_spaces.size()) {
701 auto undo_space = m_spaces.at(slot);
702 if (undo_space->num() == num) {
703 return (undo_space);
704 }
705 }
706
707 /* If there are gaps in the numbering, do a search. */
708 for (auto undo_space : m_spaces) {
709 if (undo_space->num() == num) {
710 return (undo_space);
711 }
712 }
713
714 return (nullptr);
715 }
716
717 #ifdef UNIV_DEBUG
718 /** Determine if this thread owns a lock on m_latch. */
own_latch()719 bool own_latch() {
720 return (rw_lock_own(m_latch, RW_LOCK_X) || rw_lock_own(m_latch, RW_LOCK_S));
721 }
722 #endif /* UNIV_DEBUG */
723
724 /** Get a shared lock on m_spaces. */
s_lock()725 void s_lock() { rw_lock_s_lock(m_latch); }
726
727 /** Release a shared lock on m_spaces. */
s_unlock()728 void s_unlock() { rw_lock_s_unlock(m_latch); }
729
730 /** Get an exclusive lock on m_spaces. */
x_lock()731 void x_lock() { rw_lock_x_lock(m_latch); }
732
733 /** Release an exclusive lock on m_spaces. */
x_unlock()734 void x_unlock() { rw_lock_x_unlock(m_latch); }
735
736 Tablespaces_Vector m_spaces;
737
738 private:
739 /** RW lock to protect m_spaces.
740 x for adding elements, s for scanning, size() etc. */
741 rw_lock_t *m_latch;
742 };
743
744 /** Mutext for serializing undo tablespace related DDL. These have to do with
745 creating and dropping undo tablespaces. */
746 extern ib_mutex_t ddl_mutex;
747
748 /** A global object that contains a vector of undo::Tablespace structs. */
749 extern Tablespaces *spaces;
750
751 #ifdef UNIV_DEBUG
752 /** Inject a crash if a certain SET GLOBAL DEBUG has been set.
753 Before DBUG_SUICIDE(), write an entry about this crash to the error log
754 and flush the redo log. */
755 void inject_crash(const char *injection_point_name);
756
757 /** Inject a failure in the undo truncation debug compiled code at various
758 places so that it fails the first time it hits and succeeds after that. */
759 class Inject_failure_once {
760 bool m_already_failed;
761 const char *m_inject_name;
762
763 public:
Inject_failure_once(const char * inject_name)764 Inject_failure_once(const char *inject_name)
765 : m_already_failed{false}, m_inject_name{inject_name} {}
766
767 /** If a certain SET GLOBAL DEBUG has been set and this is the first time
768 this has been called for that injection point, write an entry to the
769 error log and return true so that the caller can cause the failure.
770 @return true iff compiled with debug and the debug point has been set
771 and this it the first call for this debug point. */
772 bool should_fail();
773 };
774
775 #endif /* UNIV_DEBUG */
776
777 /** Create the truncate log file. Needed to track the state of truncate during
778 a crash. An auxiliary redo log file undo_<space_id>_trunc.log will be created
779 while the truncate of the UNDO is in progress. This file is required during
780 recovery to complete the truncate.
781 @param[in] undo_space undo tablespace to truncate.
782 @return DB_SUCCESS or error code.*/
783 dberr_t start_logging(Tablespace *undo_space);
784
785 /** Mark completion of undo truncate action by writing magic number
786 to the log file and then removing it from the disk.
787 If we are going to remove it from disk then why write magic number?
788 This is to safeguard from unlink (file-system) anomalies that will
789 keep the link to the file even after unlink action is successful
790 and ref-count = 0.
791 @param[in] space_num number of the undo tablespace to truncate. */
792 void done_logging(space_id_t space_num);
793
794 /** Check if TRUNCATE_DDL_LOG file exist.
795 @param[in] space_num undo tablespace number
796 @return true if exist else false. */
797 bool is_active_truncate_log_present(space_id_t space_num);
798
799 /** list of undo tablespaces that need header pages and rollback
800 segments written to them at startup. This can be because they are
801 newly initialized, were being truncated and the system crashed, or
802 they were an old format at startup and were replaced when they were
803 opened. Old format undo tablespaces do not have space_ids between
804 dict_sys_t::s_min_undo_space_id and dict_sys_t::s_max_undo_space_id
805 and they do not contain an RSEG_ARRAY page. */
806 extern Space_Ids s_under_construction;
807
808 /** Add undo tablespace to s_under_construction vector.
809 @param[in] space_id space id of tablespace to
810 truncate */
811 void add_space_to_construction_list(space_id_t space_id);
812
813 /** Clear the s_under_construction vector. */
814 void clear_construction_list();
815
816 /** Is an undo tablespace under constuction at the moment.
817 @param[in] space_id space id to check
818 @return true if marked for truncate, else false. */
819 bool is_under_construction(space_id_t space_id);
820
821 /** Set an undo tablespace active. */
822 void set_active(space_id_t space_id);
823
824 /* Return whether the undo tablespace is active. If this is a
825 non-undo tablespace, then it will not be found in spaces and it
826 will not be under construction, so this function will return true.
827 @param[in] space_id Undo Tablespace ID
828 @param[in] get_latch Specifies whether the rsegs->s_lock() is needed.
829 @return true if active (non-undo spaces are always active) */
830 bool is_active(space_id_t space_id, bool get_latch = true);
831
832 constexpr ulint TRUNCATE_FREQUENCY = 128;
833
834 /** Track an UNDO tablespace marked for truncate. */
835 class Truncate {
836 public:
Truncate()837 Truncate()
838 : m_space_id_marked(SPACE_UNKNOWN),
839 m_purge_rseg_truncate_frequency(
840 static_cast<ulint>(srv_purge_rseg_truncate_frequency)),
841 m_timer() {
842 /* Do Nothing. */
843 }
844
845 /** Is tablespace selected for truncate.
846 @return true if undo tablespace is marked for truncate */
is_marked()847 bool is_marked() const { return (m_space_id_marked != SPACE_UNKNOWN); }
848
849 /** Mark the undo tablespace selected for truncate as empty
850 so that it will be truncated next. */
set_marked_space_empty()851 void set_marked_space_empty() { m_marked_space_is_empty = true; }
852
853 /** Is the tablespace selected for truncate empty of undo logs yet?
854 @return true if the marked undo tablespace has no more undo logs */
is_marked_space_empty()855 bool is_marked_space_empty() const { return (m_marked_space_is_empty); }
856
857 /** Mark the tablespace for truncate.
858 @param[in] undo_space undo tablespace to truncate. */
859 void mark(Tablespace *undo_space);
860
861 /** Get the ID of the tablespace marked for truncate.
862 @return tablespace ID marked for truncate. */
get_marked_space_num()863 space_id_t get_marked_space_num() const {
864 return (id2num(m_space_id_marked));
865 }
866
867 /** Reset for next rseg truncate. */
reset()868 void reset() {
869 /* Sync with global value as we are done with truncate now. */
870 set_rseg_truncate_frequency(
871 static_cast<ulint>(srv_purge_rseg_truncate_frequency));
872
873 reset_timer();
874 m_marked_space_is_empty = false;
875 m_space_id_marked = SPACE_UNKNOWN;
876 }
877
878 /** Get the undo tablespace number to start a scan.
879 Re-adjust in case the spaces::size() went down.
880 @return undo space_num to start scanning. */
get_scan_space_num()881 space_id_t get_scan_space_num() const {
882 s_scan_pos = s_scan_pos % undo::spaces->size();
883
884 Tablespace *undo_space = undo::spaces->at(s_scan_pos);
885
886 return (undo_space->num());
887 }
888
889 /** Increment the scanning position in a round-robin fashion.
890 @return undo space_num at incremented scanning position. */
increment_scan()891 space_id_t increment_scan() const {
892 /** Round-robin way of selecting an undo tablespace for the truncate
893 operation. Once we reach the end of the list of known undo tablespace
894 IDs, move back to the first undo tablespace ID. This will scan active
895 as well as inactive undo tablespaces. */
896 s_scan_pos = (s_scan_pos + 1) % undo::spaces->size();
897
898 return (get_scan_space_num());
899 }
900
901 /** Get local rseg purge truncate frequency
902 @return rseg purge truncate frequency. */
get_rseg_truncate_frequency()903 ulint get_rseg_truncate_frequency() const {
904 return (m_purge_rseg_truncate_frequency);
905 }
906
907 /** Set local rseg purge truncate frequency */
set_rseg_truncate_frequency(ulint frequency)908 void set_rseg_truncate_frequency(ulint frequency) {
909 m_purge_rseg_truncate_frequency = frequency;
910 }
911
912 /** Check if the given space id is equal to the space ID that is marked for
913 truncation.
914 @return true if they are equal, false otherwise. */
is_equal(space_id_t space_id)915 bool is_equal(space_id_t space_id) const {
916 return (m_space_id_marked == space_id);
917 }
918
919 /** @return the number of milliseconds since last reset. */
check_timer()920 int64_t check_timer() const { return (m_timer.elapsed()); }
921
922 /** Reset the timer. */
reset_timer()923 void reset_timer() { m_timer.reset(); }
924
925 private:
926 /** UNDO space ID that is marked for truncate. */
927 space_id_t m_space_id_marked;
928
929 /** This is true if the marked space is empty of undo logs and ready
930 to truncate. We leave the rsegs object 'inactive' until after it is
931 truncated and rebuilt. This allow the code to do the check for undo
932 logs only once. */
933 bool m_marked_space_is_empty;
934
935 /** Rollback segment(s) purge frequency. This is a local value maintained
936 along with the global value. It is set to the global value before each
937 truncate. But when a tablespace is marked for truncate it is updated
938 to 1 and then minimum value among 2 is used by the purge action. */
939 ulint m_purge_rseg_truncate_frequency;
940
941 /** Elapsed time since last truncate check. */
942 ib::Timer m_timer;
943
944 /** Start scanning for UNDO tablespace from this vector position. This is
945 to avoid bias selection of one tablespace always. */
946 static size_t s_scan_pos;
947
948 }; /* class Truncate */
949
950 } /* namespace undo */
951
952 /** The control structure used in the purge operation */
953 struct trx_purge_t {
954 /** System session running the purge query */
955 sess_t *sess;
956
957 /** System transaction running the purge query: this trx is not in the trx
958 list of the trx system and it never ends */
959 trx_t *trx;
960 #ifndef UNIV_HOTBACKUP
961 /** The latch protecting the purge view. A purge operation must acquire an
962 x-latch here for the instant at which it changes the purge view: an undo
963 log operation can prevent this by obtaining an s-latch here. It also
964 protects state and running */
965 rw_lock_t latch;
966 #endif /* !UNIV_HOTBACKUP */
967
968 /** State signal event */
969 os_event_t event;
970
971 /** Counter to track number stops */
972 ulint n_stop;
973
974 /** true, if purge is active, we check this without the latch too */
975 volatile bool running;
976
977 /** Purge coordinator thread states, we check this in several places without
978 holding the latch. */
979 volatile purge_state_t state;
980
981 /** The query graph which will do the parallelized purge operation */
982 que_t *query;
983
984 /** The purge will not remove undo logs which are >= this view (purge view) */
985 ReadView view;
986
987 /** true if view is active */
988 bool view_active;
989
990 /** Count of total tasks submitted to the task queue */
991 volatile ulint n_submitted;
992
993 /** Count of total tasks completed */
994 volatile ulint n_completed;
995
996 /* The following two fields form the 'purge pointer' which advances
997 during a purge, and which is used in history list truncation */
998
999 /** Limit up to which we have read and parsed the UNDO log records. Not
1000 necessarily purged from the indexes. Note that this can never be less than
1001 the limit below, we check for this invariant in trx0purge.cc */
1002 purge_iter_t iter;
1003
1004 /** The 'purge pointer' which advances during a purge, and which is used in
1005 history list truncation */
1006 purge_iter_t limit;
1007 #ifdef UNIV_DEBUG
1008 /** Indicate 'purge pointer' which have purged already accurately. */
1009 purge_iter_t done;
1010 #endif /* UNIV_DEBUG */
1011
1012 /** true if the info of the next record to purge is stored below: if yes, then
1013 the transaction number and the undo number of the record are stored in
1014 purge_trx_no and purge_undo_no above */
1015 bool next_stored;
1016
1017 /** Rollback segment for the next undo record to purge */
1018 trx_rseg_t *rseg;
1019
1020 /** Page number for the next undo record to purge, page number of the log
1021 header, if dummy record */
1022 page_no_t page_no;
1023
1024 /** Page offset for the next undo record to purge, 0 if the dummy record */
1025 ulint offset;
1026
1027 /** Header page of the undo log where the next record to purge belongs */
1028 page_no_t hdr_page_no;
1029
1030 /** Header byte offset on the page */
1031 ulint hdr_offset;
1032
1033 /** Iterator to get the next rseg to process */
1034 TrxUndoRsegsIterator *rseg_iter;
1035
1036 /** Binary min-heap, ordered on TrxUndoRsegs::trx_no. It is protected
1037 by the pq_mutex */
1038 purge_pq_t *purge_queue;
1039
1040 /** Mutex protecting purge_queue */
1041 PQMutex pq_mutex;
1042
1043 /** Track UNDO tablespace marked for truncate. */
1044 undo::Truncate undo_trunc;
1045
1046 /** Heap for reading the undo log records */
1047 mem_heap_t *heap;
1048 };
1049
1050 /** Choose the rollback segment with the smallest trx_no. */
1051 struct TrxUndoRsegsIterator {
1052 /** Constructor */
1053 TrxUndoRsegsIterator(trx_purge_t *purge_sys);
1054
1055 /** Sets the next rseg to purge in m_purge_sys.
1056 @return page size of the table for which the log is.
1057 NOTE: if rseg is NULL when this function returns this means that
1058 there are no rollback segments to purge and then the returned page
1059 size object should not be used. */
1060 const page_size_t set_next();
1061
1062 private:
1063 // Disable copying
1064 TrxUndoRsegsIterator(const TrxUndoRsegsIterator &);
1065 TrxUndoRsegsIterator &operator=(const TrxUndoRsegsIterator &);
1066
1067 /** The purge system pointer */
1068 trx_purge_t *m_purge_sys;
1069
1070 /** The current element to process */
1071 TrxUndoRsegs m_trx_undo_rsegs;
1072
1073 /** Track the current element in m_trx_undo_rseg */
1074 Rseg_Iterator m_iter;
1075
1076 /** Sentinel value */
1077 static const TrxUndoRsegs NullElement;
1078 };
1079
1080 #include "trx0purge.ic"
1081
1082 #endif /* trx0purge_h */
1083