1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /**************************************************//** |
21 | @file include/trx0purge.h |
22 | Purge old versions |
23 | |
24 | Created 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 |
34 | which needs no purge */ |
35 | extern trx_undo_rec_t trx_purge_dummy_rec; |
36 | |
37 | /********************************************************************//** |
38 | Calculates the file address of an undo log header when we have the file |
39 | address of its history list node. |
40 | @return file address of the log */ |
41 | UNIV_INLINE |
42 | fil_addr_t |
43 | trx_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. |
48 | Remove 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 */ |
52 | void |
53 | trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr); |
54 | /*******************************************************************//** |
55 | This function runs a purge batch. |
56 | @return number of undo log pages handled in the batch */ |
57 | ulint |
58 | trx_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 |
65 | scheduled for purge. */ |
66 | class TrxUndoRsegs { |
67 | private: |
68 | typedef std::vector<trx_rseg_t*, ut_allocator<trx_rseg_t*> > |
69 | trx_rsegs_t; |
70 | public: |
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 | |
104 | private: |
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 | |
111 | typedef 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 */ |
117 | struct 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 | |
125 | private: |
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 |
137 | of undo tablespace. */ |
138 | namespace 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 */ |
371 | class purge_sys_t |
372 | { |
373 | public: |
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; |
381 | private: |
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; |
386 | public: |
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 */ |
516 | extern purge_sys_t purge_sys; |
517 | |
518 | /** Info required to purge a record */ |
519 | struct 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 | |