summaryrefslogtreecommitdiff
path: root/storage/innobase/include/buf0dblwr.h
blob: d9c9239c0b434720134472aa2fbfbef5cf609867 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*****************************************************************************

Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2022, MariaDB Corporation.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA

*****************************************************************************/

/**************************************************//**
@file include/buf0dblwr.h
Doublewrite buffer module

Created 2011/12/19 Inaam Rana
*******************************************************/

#pragma once

#include "os0file.h"
#include "buf0types.h"

/** Doublewrite control struct */
class buf_dblwr_t
{
  struct element
  {
    /** asynchronous write request */
    IORequest request;
    /** payload size in bytes */
    size_t size;
  };

  struct slot
  {
    /** first free position in write_buf measured in units of
     * srv_page_size */
    ulint first_free;
    /** number of slots reserved for the current write batch */
    ulint reserved;
    /** the doublewrite buffer, aligned to srv_page_size */
    byte* write_buf;
    /** buffer blocks to be written via write_buf */
    element* buf_block_arr;
  };

  /** the page number of the first doublewrite block (block_size() pages) */
  page_id_t block1{0, 0};
  /** the page number of the second doublewrite block (block_size() pages) */
  page_id_t block2{0, 0};

  /** mutex protecting the data members below */
  mysql_mutex_t mutex;
  /** condition variable for !batch_running */
  pthread_cond_t cond;
  /** whether a batch is being written from the doublewrite buffer */
  bool batch_running;
  /** number of expected flush_buffered_writes_completed() calls */
  unsigned flushing_buffered_writes;
  /** pages submitted to flush_buffered_writes() */
  ulint pages_submitted;
  /** number of flush_buffered_writes_completed() calls */
  ulint writes_completed;
  /** number of pages written by flush_buffered_writes_completed() */
  ulint pages_written;
  /** condition variable for !writes_pending */
  pthread_cond_t write_cond;
  /** number of pending page writes */
  size_t writes_pending;

  slot slots[2];
  slot *active_slot;

  /** Initialise the persistent storage of the doublewrite buffer.
  @param header   doublewrite page header in the TRX_SYS page */
  inline void init(const byte *header);

  /** Flush possible buffered writes to persistent storage. */
  bool flush_buffered_writes(const ulint size);

public:
  /** Initialise the doublewrite buffer data structures. */
  void init();
  /** Create or restore the doublewrite buffer in the TRX_SYS page.
  @return whether the operation succeeded */
  bool create();
  /** Free the doublewrite buffer. */
  void close();

  /** Acquire the mutex */
  void lock() { mysql_mutex_lock(&mutex); }
  /** @return the number of submitted page writes */
  ulint submitted() const
  { mysql_mutex_assert_owner(&mutex); return pages_submitted; }
  /** @return the number of completed batches */
  ulint batches() const
  { mysql_mutex_assert_owner(&mutex); return writes_completed; }
  /** @return the number of final pages written */
  ulint written() const
  { mysql_mutex_assert_owner(&mutex); return pages_written; }
  /** Release the mutex */
  void unlock() { mysql_mutex_unlock(&mutex); }

  /** Initialize the doublewrite buffer memory structure on recovery.
  If we are upgrading from a version before MySQL 4.1, then this
  function performs the necessary update operations to support
  innodb_file_per_table. If we are in a crash recovery, this function
  loads the pages from double write buffer into memory.
  @param file File handle
  @param path Path name of file
  @return DB_SUCCESS or error code */
  dberr_t init_or_load_pages(pfs_os_file_t file, const char *path);

  /** Process and remove the double write buffer pages for all tablespaces. */
  void recover();

  /** Update the doublewrite buffer on data page write completion. */
  void write_completed(bool with_doublewrite);
  /** Flush possible buffered writes to persistent storage.
  It is very important to call this function after a batch of writes has been
  posted, and also when we may have to wait for a page latch!
  Otherwise a deadlock of threads can occur. */
  void flush_buffered_writes();
  /** Update the doublewrite buffer on write batch completion
  @param request  the completed batch write request */
  void flush_buffered_writes_completed(const IORequest &request);

  /** Size of the doublewrite block in pages */
  uint32_t block_size() const { return FSP_EXTENT_SIZE; }

  /** Schedule a page write. If the doublewrite memory buffer is full,
  flush_buffered_writes() will be invoked to make space.
  @param request    asynchronous write request
  @param size       payload size in bytes */
  void add_to_batch(const IORequest &request, size_t size);

  /** Determine whether the doublewrite buffer has been created */
  bool is_created() const
  { return UNIV_LIKELY(block1 != page_id_t(0, 0)); }

  /** @return whether a page identifier is part of the doublewrite buffer */
  bool is_inside(const page_id_t id) const
  {
    if (!is_created())
      return false;
    ut_ad(block1 < block2);
    if (id < block1)
      return false;
    const uint32_t size= block_size();
    return id < block1 + size || (id >= block2 && id < block2 + size);
  }

  /** Wait for flush_buffered_writes() to be fully completed */
  void wait_flush_buffered_writes()
  {
    mysql_mutex_lock(&mutex);
    while (batch_running)
      my_cond_wait(&cond, &mutex.m_mutex);
    mysql_mutex_unlock(&mutex);
  }

  /** Register an unbuffered page write */
  void add_unbuffered()
  {
    mysql_mutex_lock(&mutex);
    writes_pending++;
    mysql_mutex_unlock(&mutex);
  }

  size_t pending_writes()
  {
    mysql_mutex_lock(&mutex);
    const size_t pending{writes_pending};
    mysql_mutex_unlock(&mutex);
    return pending;
  }

  /** Wait for writes_pending to reach 0 */
  void wait_for_page_writes()
  {
    mysql_mutex_lock(&mutex);
    while (writes_pending)
      my_cond_wait(&write_cond, &mutex.m_mutex);
    mysql_mutex_unlock(&mutex);
  }

  /** Wait for writes_pending to reach 0 */
  void wait_for_page_writes(const timespec &abstime)
  {
    mysql_mutex_lock(&mutex);
    while (writes_pending)
      my_cond_timedwait(&write_cond, &mutex.m_mutex, &abstime);
    mysql_mutex_unlock(&mutex);
  }
};

/** The doublewrite buffer */
extern buf_dblwr_t buf_dblwr;