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