1/*****************************************************************************
2
3Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file include/trx0purge.h
22Purge old versions
23
24Created 3/26/1996 Heikki Tuuri
25*******************************************************/
26
27#ifndef trx0purge_h
28#define trx0purge_h
29
30#include "trx0rseg.h"
31#include "que0types.h"
32
33/** A dummy undo record used as a return value when we have a whole undo log
34which needs no purge */
35extern trx_undo_rec_t trx_purge_dummy_rec;
36
37/********************************************************************//**
38Calculates the file address of an undo log header when we have the file
39address of its history list node.
40@return file address of the log */
41UNIV_INLINE
42fil_addr_t
43trx_purge_get_log_from_hist(
44/*========================*/
45 fil_addr_t node_addr); /*!< in: file address of the history
46 list node of the log */
47/** Prepend the history list with an undo log.
48Remove the undo log segment from the rseg slot if it is too big for reuse.
49@param[in] trx transaction
50@param[in,out] undo undo log
51@param[in,out] mtr mini-transaction */
52void
53trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr);
54/*******************************************************************//**
55This function runs a purge batch.
56@return number of undo log pages handled in the batch */
57ulint
58trx_purge(
59/*======*/
60 ulint n_purge_threads, /*!< in: number of purge tasks to
61 submit to task queue. */
62 bool truncate); /*!< in: truncate history if true */
63
64/** Rollback segements from a given transaction with trx-no
65scheduled for purge. */
66class TrxUndoRsegs {
67private:
68 typedef std::vector<trx_rseg_t*, ut_allocator<trx_rseg_t*> >
69 trx_rsegs_t;
70public:
71 typedef trx_rsegs_t::iterator iterator;
72 typedef trx_rsegs_t::const_iterator const_iterator;
73
74 /** Default constructor */
75 TrxUndoRsegs() {}
76 /** Constructor */
77 TrxUndoRsegs(trx_rseg_t& rseg)
78 : m_commit(rseg.last_commit), m_rsegs(1, &rseg) {}
79 /** Constructor */
80 TrxUndoRsegs(trx_id_t trx_no, trx_rseg_t& rseg)
81 : m_commit(trx_no << 1), m_rsegs(1, &rseg) {}
82
83 /** @return the transaction commit identifier */
84 trx_id_t trx_no() const { return m_commit >> 1; }
85
86 bool operator!=(const TrxUndoRsegs& other) const
87 { return m_commit != other.m_commit; }
88 bool empty() const { return m_rsegs.empty(); }
89 void erase(iterator& it) { m_rsegs.erase(it); }
90 iterator begin() { return(m_rsegs.begin()); }
91 iterator end() { return(m_rsegs.end()); }
92 const_iterator begin() const { return m_rsegs.begin(); }
93 const_iterator end() const { return m_rsegs.end(); }
94
95 /** Compare two TrxUndoRsegs based on trx_no.
96 @param elem1 first element to compare
97 @param elem2 second element to compare
98 @return true if elem1 > elem2 else false.*/
99 bool operator()(const TrxUndoRsegs& lhs, const TrxUndoRsegs& rhs)
100 {
101 return(lhs.m_commit > rhs.m_commit);
102 }
103
104private:
105 /** Copy trx_rseg_t::last_commit */
106 trx_id_t m_commit;
107 /** Rollback segments of a transaction, scheduled for purge. */
108 trx_rsegs_t m_rsegs;
109};
110
111typedef std::priority_queue<
112 TrxUndoRsegs,
113 std::vector<TrxUndoRsegs, ut_allocator<TrxUndoRsegs> >,
114 TrxUndoRsegs> purge_pq_t;
115
116/** Chooses the rollback segment with the oldest committed transaction */
117struct TrxUndoRsegsIterator {
118 /** Constructor */
119 TrxUndoRsegsIterator();
120 /** Sets the next rseg to purge in purge_sys.
121 Executed in the purge coordinator thread.
122 @return whether anything is to be purged */
123 inline bool set_next();
124
125private:
126 // Disable copying
127 TrxUndoRsegsIterator(const TrxUndoRsegsIterator&);
128 TrxUndoRsegsIterator& operator=(const TrxUndoRsegsIterator&);
129
130 /** The current element to process */
131 TrxUndoRsegs m_rsegs;
132 /** Track the current element in m_rsegs */
133 TrxUndoRsegs::const_iterator m_iter;
134};
135
136/* Namespace to hold all the related functions and variables need for truncate
137of undo tablespace. */
138namespace undo {
139
140 typedef std::vector<ulint> undo_spaces_t;
141 typedef std::vector<trx_rseg_t*> rseg_for_trunc_t;
142
143 /** Magic Number to indicate truncate action is complete. */
144 const ib_uint32_t s_magic = 76845412;
145
146 /** Truncate Log file Prefix. */
147 const char* const s_log_prefix = "undo_";
148
149 /** Truncate Log file Extension. */
150 const char* const s_log_ext = "trunc.log";
151
152 /** Populate log file name based on space_id
153 @param[in] space_id id of the undo tablespace.
154 @return DB_SUCCESS or error code */
155 dberr_t populate_log_file_name(
156 ulint space_id,
157 char*& log_file_name);
158
159 /** Create the truncate log file.
160 @param[in] space_id id of the undo tablespace to truncate.
161 @return DB_SUCCESS or error code. */
162 dberr_t init(ulint space_id);
163
164 /** Mark completion of undo truncate action by writing magic number to
165 the log file and then removing it from the disk.
166 If we are going to remove it from disk then why write magic number ?
167 This is to safeguard from unlink (file-system) anomalies that will keep
168 the link to the file even after unlink action is successfull and
169 ref-count = 0.
170 @param[in] space_id id of the undo tablespace to truncate.*/
171 void done(ulint space_id);
172
173 /** Check if TRUNCATE_DDL_LOG file exist.
174 @param[in] space_id id of the undo tablespace.
175 @return true if exist else false. */
176 bool is_log_present(ulint space_id);
177
178 /** Track UNDO tablespace mark for truncate. */
179 class Truncate {
180 public:
181 void create()
182 {
183 m_undo_for_trunc = ULINT_UNDEFINED;
184 m_scan_start = 1;
185 m_purge_rseg_truncate_frequency =
186 ulint(srv_purge_rseg_truncate_frequency);
187 }
188
189 /** Clear the cached rollback segment. Normally done
190 when purge is about to shutdown. */
191 void clear()
192 {
193 reset();
194 rseg_for_trunc_t temp;
195 m_rseg_for_trunc.swap(temp);
196 }
197
198 /** Is tablespace selected for truncate.
199 @return true if undo tablespace is marked for truncate */
200 bool is_marked() const
201 {
202 return(!(m_undo_for_trunc == ULINT_UNDEFINED));
203 }
204
205 /** Mark the tablespace for truncate.
206 @param[in] undo_id tablespace for truncate. */
207 void mark(ulint undo_id)
208 {
209 m_undo_for_trunc = undo_id;
210
211 m_scan_start = (undo_id + 1)
212 % (srv_undo_tablespaces_active + 1);
213 if (m_scan_start == 0) {
214 /* Note: UNDO tablespace ids starts from 1. */
215 m_scan_start = 1;
216 }
217
218 /* We found an UNDO-tablespace to truncate so set the
219 local purge rseg truncate frequency to 1. This will help
220 accelerate the purge action and in turn truncate. */
221 m_purge_rseg_truncate_frequency = 1;
222 }
223
224 /** Get the tablespace marked for truncate.
225 @return tablespace id marked for truncate. */
226 ulint get_marked_space_id() const
227 {
228 return(m_undo_for_trunc);
229 }
230
231 /** Add rseg to truncate vector.
232 @param[in,out] rseg rseg for truncate */
233 void add_rseg_to_trunc(trx_rseg_t* rseg)
234 {
235 m_rseg_for_trunc.push_back(rseg);
236 }
237
238 /** Get number of rsegs registered for truncate.
239 @return return number of rseg that belongs to tablespace mark
240 for truncate. */
241 ulint rsegs_size() const
242 {
243 return(m_rseg_for_trunc.size());
244 }
245
246 /** Get ith registered rseg.
247 @param[in] id index of rseg to get.
248 @return reference to registered rseg. */
249 trx_rseg_t* get_ith_rseg(ulint id)
250 {
251 ut_ad(id < m_rseg_for_trunc.size());
252 return(m_rseg_for_trunc.at(id));
253 }
254
255 /** Reset for next rseg truncate. */
256 void reset()
257 {
258 m_undo_for_trunc = ULINT_UNDEFINED;
259 m_rseg_for_trunc.clear();
260
261 /* Sync with global value as we are done with
262 truncate now. */
263 m_purge_rseg_truncate_frequency = static_cast<ulint>(
264 srv_purge_rseg_truncate_frequency);
265 }
266
267 /** Get the tablespace id to start scanning from.
268 @return id of UNDO tablespace to start scanning from. */
269 ulint get_scan_start() const
270 {
271 return(m_scan_start);
272 }
273
274 /** Check if the tablespace needs fix-up (based on presence of
275 DDL truncate log)
276 @param space_id space id of the undo tablespace to check
277 @return true if fix up is needed else false */
278 bool needs_fix_up(ulint space_id) const
279 {
280 return(is_log_present(space_id));
281 }
282
283 /** Add undo tablespace to truncate vector.
284 @param[in] space_id space id of tablespace to
285 truncate */
286 static void add_space_to_trunc_list(ulint space_id)
287 {
288 s_spaces_to_truncate.push_back(space_id);
289 }
290
291 /** Clear the truncate vector. */
292 static void clear_trunc_list()
293 {
294 s_spaces_to_truncate.clear();
295 }
296
297 /** Is tablespace marked for truncate.
298 @param[in] space_id space id to check
299 @return true if marked for truncate, else false. */
300 static bool is_tablespace_truncated(ulint space_id)
301 {
302 return(std::find(s_spaces_to_truncate.begin(),
303 s_spaces_to_truncate.end(), space_id)
304 != s_spaces_to_truncate.end());
305 }
306
307 /** Was a tablespace truncated at startup
308 @param[in] space_id space id to check
309 @return whether space_id was truncated at startup */
310 static bool was_tablespace_truncated(ulint space_id)
311 {
312 return(std::find(s_fix_up_spaces.begin(),
313 s_fix_up_spaces.end(),
314 space_id)
315 != s_fix_up_spaces.end());
316 }
317
318 /** Get local rseg purge truncate frequency
319 @return rseg purge truncate frequency. */
320 ulint get_rseg_truncate_frequency() const
321 {
322 return(m_purge_rseg_truncate_frequency);
323 }
324
325 /* Start writing log information to a special file.
326 On successfull completion, file is removed.
327 On crash, file is used to complete the truncate action.
328 @param space_id space id of undo tablespace
329 @return DB_SUCCESS or error code. */
330 dberr_t start_logging(ulint space_id)
331 {
332 return(init(space_id));
333 }
334
335 /* Mark completion of logging./
336 @param space_id space id of undo tablespace */
337 void done_logging(ulint space_id)
338 {
339 return(done(space_id));
340 }
341
342 private:
343 /** UNDO tablespace is mark for truncate. */
344 ulint m_undo_for_trunc;
345
346 /** rseg that resides in UNDO tablespace is marked for
347 truncate. */
348 rseg_for_trunc_t m_rseg_for_trunc;
349
350 /** Start scanning for UNDO tablespace from this space_id.
351 This is to avoid bias selection of one tablespace always. */
352 ulint m_scan_start;
353
354 /** Rollback segment(s) purge frequency. This is local
355 value maintained along with global value. It is set to global
356 value on start but when tablespace is marked for truncate it
357 is updated to 1 and then minimum value among 2 is used by
358 purge action. */
359 ulint m_purge_rseg_truncate_frequency;
360
361 /** List of UNDO tablespace(s) to truncate. */
362 static undo_spaces_t s_spaces_to_truncate;
363 public:
364 /** Undo tablespaces that were truncated at startup */
365 static undo_spaces_t s_fix_up_spaces;
366 }; /* class Truncate */
367
368}; /* namespace undo */
369
370/** The control structure used in the purge operation */
371class purge_sys_t
372{
373public:
374 /** signal state changes; os_event_reset() and os_event_set()
375 are protected by rw_lock_x_lock(latch) */
376 MY_ALIGNED(CACHE_LINE_SIZE)
377 os_event_t event;
378 /** latch protecting view, m_enabled */
379 MY_ALIGNED(CACHE_LINE_SIZE)
380 rw_lock_t latch;
381private:
382 /** whether purge is enabled; protected by latch and my_atomic */
383 int32_t m_enabled;
384 /** number of pending stop() calls without resume() */
385 int32_t m_paused;
386public:
387 que_t* query; /*!< The query graph which will do the
388 parallelized purge operation */
389 MY_ALIGNED(CACHE_LINE_SIZE)
390 ReadView view; /*!< The purge will not remove undo logs
391 which are >= this view (purge view) */
392 /** Total number of tasks submitted by srv_purge_coordinator_thread.
393 Not accessed by other threads. */
394 ulint n_submitted;
395 /** Number of completed tasks. Accessed by srv_purge_coordinator
396 and srv_worker_thread by my_atomic. */
397 ulint n_completed;
398
399 /** Iterator to the undo log records of committed transactions */
400 struct iterator
401 {
402 bool operator<=(const iterator& other) const
403 {
404 if (commit < other.commit) return true;
405 if (commit > other.commit) return false;
406 return undo_no <= other.undo_no;
407 }
408
409 /** @return the commit number of the transaction */
410 trx_id_t trx_no() const { return commit >> 1; }
411 void reset_trx_no(trx_id_t trx_no) { commit = trx_no << 1; }
412
413 /** 2 * trx_t::no + old_insert of the committed transaction */
414 trx_id_t commit;
415 /** The record number within the committed transaction's undo
416 log, increasing, purged from from 0 onwards */
417 undo_no_t undo_no;
418 };
419
420 /** The tail of the purge queue; the last parsed undo log of a
421 committed transaction. */
422 iterator tail;
423 /** The head of the purge queue; any older undo logs of committed
424 transactions may be discarded (history list truncation). */
425 iterator head;
426 /*-----------------------------*/
427 bool next_stored; /*!< whether rseg holds the next record
428 to purge */
429 trx_rseg_t* rseg; /*!< Rollback segment for the next undo
430 record to purge */
431 ulint page_no; /*!< Page number for the next undo
432 record to purge, page number of the
433 log header, if dummy record */
434 ulint offset; /*!< Page offset for the next undo
435 record to purge, 0 if the dummy
436 record */
437 ulint hdr_page_no; /*!< Header page of the undo log where
438 the next record to purge belongs */
439 ulint hdr_offset; /*!< Header byte offset on the page */
440
441
442 TrxUndoRsegsIterator
443 rseg_iter; /*!< Iterator to get the next rseg
444 to process */
445
446 purge_pq_t purge_queue; /*!< Binary min-heap, ordered on
447 TrxUndoRsegs::trx_no. It is protected
448 by the pq_mutex */
449 PQMutex pq_mutex; /*!< Mutex protecting purge_queue */
450
451 undo::Truncate undo_trunc; /*!< Track UNDO tablespace marked
452 for truncate. */
453
454
455 /**
456 Constructor.
457
458 Some members may require late initialisation, thus we just mark object as
459 uninitialised. Real initialisation happens in create().
460 */
461
462 purge_sys_t() : event(NULL), m_enabled(false) {}
463
464
465 /** Create the instance */
466 void create();
467
468 /** Close the purge system on shutdown */
469 void close();
470
471 /** @return whether purge is enabled */
472 bool enabled()
473 {
474 return my_atomic_load32_explicit(&m_enabled, MY_MEMORY_ORDER_RELAXED);
475 }
476 /** @return whether purge is enabled */
477 bool enabled_latched()
478 {
479 ut_ad(rw_lock_own_flagged(&latch, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
480 return bool(m_enabled);
481 }
482 /** @return whether the purge coordinator is paused */
483 bool paused()
484 { return my_atomic_load32_explicit(&m_paused, MY_MEMORY_ORDER_RELAXED); }
485 /** @return whether the purge coordinator is paused */
486 bool paused_latched()
487 {
488 ut_ad(rw_lock_own_flagged(&latch, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
489 return m_paused != 0;
490 }
491
492 /** Enable purge at startup. Not protected by latch; the main thread
493 will wait for purge_sys.enabled() in srv_start() */
494 void coordinator_startup()
495 {
496 ut_ad(!enabled());
497 my_atomic_store32_explicit(&m_enabled, true, MY_MEMORY_ORDER_RELAXED);
498 }
499
500 /** Disable purge at shutdown */
501 void coordinator_shutdown()
502 {
503 ut_ad(enabled());
504 my_atomic_store32_explicit(&m_enabled, false, MY_MEMORY_ORDER_RELAXED);
505 }
506
507 /** @return whether the purge coordinator thread is active */
508 bool running();
509 /** Stop purge during FLUSH TABLES FOR EXPORT */
510 void stop();
511 /** Resume purge at UNLOCK TABLES after FLUSH TABLES FOR EXPORT */
512 void resume();
513};
514
515/** The global data structure coordinating a purge */
516extern purge_sys_t purge_sys;
517
518/** Info required to purge a record */
519struct trx_purge_rec_t {
520 trx_undo_rec_t* undo_rec; /*!< Record to purge */
521 roll_ptr_t roll_ptr; /*!< File pointr to UNDO record */
522};
523
524#include "trx0purge.ic"
525
526#endif /* trx0purge_h */
527