1 /***************************************************************************** 2 3 Copyright (c) 1995, 2020, Oracle and/or its affiliates. 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/buf0dblwr.h 28 Doublewrite buffer module 29 30 Created 2011/12/19 Inaam Rana 31 *******************************************************/ 32 33 #ifndef buf0dblwr_h 34 #define buf0dblwr_h 35 36 #include "buf0types.h" 37 #include "log0log.h" 38 #include "log0recv.h" 39 #include "ut0byte.h" 40 41 /** Size of the doublewrite block in pages. */ 42 #define DBLWR_V1_EXTENT_SIZE FSP_EXTENT_SIZE 43 44 /** Offset of the doublewrite buffer header on the trx system header page. */ 45 #define TRX_SYS_DBLWR_V1 (UNIV_PAGE_SIZE - 200) 46 47 /** 4-byte ver number which shows if we have created the doublewrite buffer. */ 48 constexpr ulint DBLWR_VER = FSEG_HEADER_SIZE; 49 50 /** Page number of the first page in the first sequence of 64 (= 51 FSP_EXTENT_SIZE) consecutive pages in the doublewrite buffer. */ 52 constexpr ulint DBLWR_V1_BLOCK1 = (4 + FSEG_HEADER_SIZE); 53 54 /** Page number of the first page in the second sequence of 64 consecutive 55 pages in the doublewrite buffer. */ 56 constexpr ulint DBLWR_V1_BLOCK2 = (8 + FSEG_HEADER_SIZE); 57 58 namespace dblwr { 59 /** IO buffer in UNIV_PAGE_SIZE units and aligned on UNIV_PAGE_SIZE */ 60 struct Buffer { 61 /** Constructor 62 @param[in] n_pages Number of pages to create */ BufferBuffer63 explicit Buffer(size_t n_pages) noexcept 64 : m_n_bytes(n_pages * univ_page_size.physical()) { 65 ut_a(n_pages > 0); 66 67 auto n_bytes = m_n_bytes + univ_page_size.physical(); 68 69 m_ptr_unaligned = static_cast<byte *>(ut_zalloc_nokey(n_bytes)); 70 71 m_ptr = static_cast<byte *>(ut_align(m_ptr_unaligned, UNIV_PAGE_SIZE)); 72 73 ut_a(ptrdiff_t(m_ptr - m_ptr_unaligned) <= 74 (ssize_t)univ_page_size.physical()); 75 76 m_next = m_ptr; 77 } 78 79 /** Destructor */ ~BufferBuffer80 ~Buffer() noexcept { 81 if (m_ptr_unaligned != nullptr) { 82 ut_free(m_ptr_unaligned); 83 } 84 m_ptr_unaligned = nullptr; 85 } 86 87 /** Add the contents of ptr upto n_bytes to the buffer. 88 @return false if it won't fit. Nothing is copied if it won't fit. */ appendBuffer89 bool append(const void *ptr, size_t n_bytes) noexcept { 90 ut_a(m_next >= m_ptr && m_next <= m_ptr + m_n_bytes); 91 92 if (m_next + univ_page_size.physical() > m_ptr + m_n_bytes) { 93 return false; 94 } 95 96 memcpy(m_next, ptr, n_bytes); 97 m_next += univ_page_size.physical(); 98 99 return true; 100 } 101 102 /** @return the start of the buffer to write from. */ beginBuffer103 byte *begin() noexcept { return m_ptr; } 104 105 /** @return the start of the buffer to write from. */ beginBuffer106 const byte *begin() const noexcept { return m_ptr; } 107 108 /** @return the size of of the buffer to write. */ sizeBuffer109 size_t size() const noexcept { 110 ut_a(m_next >= m_ptr); 111 return std::ptrdiff_t(m_next - m_ptr); 112 } 113 114 /** @return the capacity of the buffer in bytes. */ capacityBuffer115 size_t capacity() const noexcept { return m_n_bytes; } 116 117 /** @return true if the buffer is empty. */ emptyBuffer118 bool empty() const noexcept { return size() == 0; } 119 120 /** Empty the buffer. */ clearBuffer121 void clear() noexcept { m_next = m_ptr; } 122 123 /** Write buffer used in writing to the doublewrite buffer, 124 aligned to an address divisible by UNIV_PAGE_SIZE (which is 125 required by Windows AIO) */ 126 byte *m_ptr{}; 127 128 /** Start of next write to the buffer. */ 129 byte *m_next{}; 130 131 /** Pointer to m_ptr, but unaligned */ 132 byte *m_ptr_unaligned{}; 133 134 /** Size of the unaligned (raw) buffer. */ 135 const size_t m_n_bytes{}; 136 137 // Disable copying 138 Buffer(const Buffer &) = delete; 139 Buffer(const Buffer &&) = delete; 140 Buffer &operator=(Buffer &&) = delete; 141 Buffer &operator=(const Buffer &) = delete; 142 }; 143 144 } // namespace dblwr 145 146 #ifndef UNIV_HOTBACKUP 147 148 // Forward declaration 149 class buf_page_t; 150 class Double_write; 151 152 namespace dblwr { 153 154 /** Double write files location. */ 155 extern std::string dir; 156 157 #ifdef UNIV_DEBUG 158 /** Crash the server after writing this page to the data file. */ 159 extern page_id_t Force_crash; 160 #endif /* UNIV_DEBUG */ 161 162 /** Startup the background thread(s) and create the instance. 163 @param[in] create_new_db Create new database. 164 @return DB_SUCCESS or error code */ 165 dberr_t open(bool create_new_db) noexcept MY_ATTRIBUTE((warn_unused_result)); 166 167 /** Shutdown the background thread and destroy the instance */ 168 void close() noexcept; 169 170 /** Force a write of all pages in the queue. 171 @param[in] flush_type FLUSH LIST or LRU_LIST flush request. 172 @param[in] buf_pool_index Buffer pool instance for which called. */ 173 void force_flush(buf_flush_t flush_type, uint32_t buf_pool_index) noexcept; 174 175 /** Writes a page to the doublewrite buffer on disk, syncs it, 176 then writes the page to the datafile. 177 @param[in] flush_type Flush type 178 @param[in] bpage Buffer block to write 179 @param[in] sync True if sync IO requested 180 @return DB_SUCCESS or error code */ 181 dberr_t write(buf_flush_t flush_type, buf_page_t *bpage, bool sync) noexcept 182 MY_ATTRIBUTE((warn_unused_result)); 183 184 /** Updates the double write buffer when a write request is completed. 185 @param[in] bpage Block that has just been writtent to disk. 186 @param[in] flush_type Flush type that triggered the write. */ 187 void write_complete(buf_page_t *bpage, buf_flush_t flush_type) noexcept; 188 189 /** Delete or adjust the dblwr file size if required. */ 190 void reset_files() noexcept; 191 192 namespace v1 { 193 /** Read the boundaries of the legacy dblwr buffer extents. 194 @return DB_SUCCESS or error code. */ 195 dberr_t init() noexcept MY_ATTRIBUTE((warn_unused_result)); 196 197 /** Create the dblwr data structures in the system tablespace. 198 @return DB_SUCCESS or error code. */ 199 dberr_t create() noexcept MY_ATTRIBUTE((warn_unused_result)); 200 201 /** Check if the read is of a page inside the legacy dblwr buffer. 202 @param[in] page_no Page number to check. 203 @return true if offset inside legacy dblwr buffer. */ 204 // clang-format off 205 bool is_inside(page_no_t page_no) noexcept 206 MY_ATTRIBUTE((warn_unused_result)); 207 // clang-format on 208 209 } // namespace v1 210 } // namespace dblwr 211 212 #endif /* !UNIV_HOTBACKUP */ 213 214 namespace dblwr { 215 216 /** Number of pages per doublewrite thread/segment */ 217 extern ulong n_pages; 218 219 /** true if enabled. */ 220 extern bool enabled; 221 222 /** Number of files to use for the double write buffer. It must be <= than 223 the number of buffer pool instances. */ 224 extern ulong n_files; 225 226 /** Maximum number of pages to write in one batch. */ 227 extern ulong batch_size; 228 229 /** Toggle the doublewrite buffer. */ 230 void set(); 231 232 namespace recv { 233 234 class Pages; 235 236 /** Create the recovery dblwr data structures 237 @param[out] pages Pointer to newly created instance */ 238 void create(Pages *&pages) noexcept; 239 240 /** Load the doublewrite buffer pages. 241 @param[in,out] pages For storing the doublewrite pages read 242 from the double write buffer 243 @return DB_SUCCESS or error code */ 244 dberr_t load(Pages *pages) noexcept MY_ATTRIBUTE((warn_unused_result)); 245 246 /** Restore pages from the double write buffer to the tablespace. 247 @param[in,out] pages Pages from the doublewrite buffer 248 @param[in] space Tablespace pages to restore, if set 249 to nullptr then try and restore all. */ 250 void recover(Pages *pages, fil_space_t *space) noexcept; 251 252 /** Find a doublewrite copy of a page. 253 @param[in] pages Pages read from the doublewrite buffer 254 @param[in] page_id Page number to lookup 255 @return page frame 256 @retval NULL if no page was found */ 257 const byte *find(const Pages *pages, const page_id_t &page_id) noexcept 258 MY_ATTRIBUTE((warn_unused_result)); 259 260 /** Check if some pages from the double write buffer could not be 261 restored because of the missing tablespace IDs. 262 @param[in] pages Pages to check */ 263 void check_missing_tablespaces(const Pages *pages) noexcept; 264 265 /** Free the recovery dblwr data structures 266 @param[out] pages Free the instance */ 267 void destroy(Pages *&pages) noexcept; 268 269 /** Redo recovery configuration. */ 270 class DBLWR { 271 public: 272 /** Constructor. */ DBLWR()273 explicit DBLWR() noexcept { create(m_pages); } 274 275 /** Destructor */ ~DBLWR()276 ~DBLWR() noexcept { destroy(m_pages); } 277 278 /** @return true if empty. */ empty()279 bool empty() const noexcept { return (m_pages == nullptr); } 280 281 /** Load the doublewrite buffer pages. Doesn't create the doublewrite 282 @return DB_SUCCESS or error code */ load()283 dberr_t load() noexcept MY_ATTRIBUTE((warn_unused_result)) { 284 return (dblwr::recv::load(m_pages)); 285 } 286 287 /** Restore pages from the double write buffer to the tablespace. 288 @param[in] space Tablespace pages to restore, 289 if set to nullptr then try 290 and restore all. */ 291 void recover(fil_space_t *space = nullptr) noexcept { 292 dblwr::recv::recover(m_pages, space); 293 } 294 295 // clang-format off 296 /** Find a doublewrite copy of a page. 297 @param[in] page_id Page number to lookup 298 @return page frame 299 @retval nullptr if no page was found */ find(const page_id_t & page_id)300 const byte *find(const page_id_t &page_id) noexcept 301 MY_ATTRIBUTE((warn_unused_result)) { 302 return (dblwr::recv::find(m_pages, page_id)); 303 } 304 // clang-format on 305 306 /** Check if some pages from the double write buffer 307 could not be restored because of the missing tablespace IDs. */ check_missing_tablespaces()308 void check_missing_tablespaces() noexcept { 309 dblwr::recv::check_missing_tablespaces(m_pages); 310 } 311 312 #ifndef UNIV_HOTBACKUP 313 /** Note that recovery is complete. Adjust the file sizes if necessary. */ recovered()314 void recovered() noexcept { dblwr::reset_files(); } 315 #endif /* !UNIV_HOTBACKUP */ 316 317 /** Disably copying. */ 318 DBLWR(const DBLWR &) = delete; 319 DBLWR(const DBLWR &&) = delete; 320 DBLWR &operator=(DBLWR &&) = delete; 321 DBLWR &operator=(const DBLWR &) = delete; 322 323 private: 324 /** Pages read from the double write file. */ 325 Pages *m_pages{}; 326 }; 327 } // namespace recv 328 } // namespace dblwr 329 330 #endif /* buf0dblwr_h */ 331