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 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
/*****************************************************************************
Copyright (c) 2019, 2025, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.
This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.
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, version 2.0,
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/**************************************************/ /**
@file include/log0files_capacity.h
Redo log management of capacity.
*******************************************************/
#ifndef log0files_capacity_h
#define log0files_capacity_h
/* Log_files_dict */
#include "log0files_dict.h"
/* log_t&, lsn_t */
#include "log0types.h"
/* os_offset_t */
#include "os0file.h"
/** Responsible for the redo log capacity computations. Computes size for the
next log file that will be created. Tracks the redo resize operation when the
innodb_redo_log_capacity gets changed. Computes maximum ages for dirty pages,
which are then used by page cleaner coordinator.
@remarks When downsize is started, the limits for ages are decreased, forcing
page cleaners to flush more dirty pages then. File sizes for new redo files
are adjusted accordingly, so they could always be effectively used to hold
the whole existing redo log data (for the given current logical size). */
class Log_files_capacity {
public:
/** Initialize on discovered set of redo log files (empty set if new redo
is being created).
@param[in] files in-memory dictionary of existing files
@param[in] current_logical_size current logical size of data in redo,
or 0 if new redo is being created
@param[in] current_checkpoint_age current checkpoint age */
void initialize(const Log_files_dict &files, lsn_t current_logical_size,
lsn_t current_checkpoint_age);
/** Updates all internal limits according to the provided parameters.
If there are any values outside this class, on which computations of
limits depend on, they should be explicitly provided here, except the
server variables (srv_thread_concurrency, srv_redo_log_capacity_used).
@param[in] files in-memory dictionary of files
@param[in] current_logical_size current logical size of data
in the redo log; it depends
directly on the oldest redo
log consumer
@param[in] current_checkpoint_age current checkpoint age */
void update(const Log_files_dict &files, lsn_t current_logical_size,
lsn_t current_checkpoint_age);
/** @return true iff resize-down is pending */
bool is_resizing_down() const;
/** Provides maximum limitation for space occupied on disk.
@note This value changes only during calls to @see update or @see initialize.
@return maximum allowed size on disk, in bytes */
os_offset_t current_physical_capacity() const;
/** If a redo downsize is in progress, it is the targeted value for the
current_physical_capacity() (is equal if there is no resize in progress).
It is set to srv_redo_log_capacity_used when @see update is called.
@note This value changes only during calls to @see update or @see initialize.
@return targeted physical capacity, in bytes */
os_offset_t target_physical_capacity() const;
/** Soft limit for logical capacity of the redo log. When the log writer
exceeds this limitation, all user threads are paused during log_free_check()
calls and message is emitted to the error log. The log writer can still
continue to write until it reaches the hard limit for logical capacity
(value returned by hard_logical_capacity()).
@note This value changes only during calls to @see update or @see initialize.
@return limitation for free space in the redo log for user threads */
lsn_t soft_logical_capacity() const;
/** Hard limit for logical capacity of the redo log. This limitation includes
"extra_writer_margin" that belongs to the log writer thread. The log writer
does not to exceed this limit. If space isn't reclaimed after 1 sec wait,
it writes only as much as possible or crashes the InnoDB.
@note This value changes only during calls to @see update or @see initialize.
@return limitation for free space in the redo log for the log writer thread */
lsn_t hard_logical_capacity() const;
/** Once checkpoint age exceeds this value, the flushing of pages starts to
be adaptive. The adaptive page flushing is becoming more and more aggressive
in the following range: adaptive_flush_min_age()..adaptive_flush_max_age().
@note This value changes only during calls to @see update or @see initialize.
@note Note that it must hold:
adaptive_flush_min_age() < adaptive_flush_max_age() <=
soft_logical_capacity().
@remarks
The diagram below shows how flushing / checkpointing becomes more aggressive
when the age of the oldest modified page gets increased:
adaptive_flush_min_age adaptive_flush_max_age aggressive_checkpoint_min_age
| | |
-------!------------------------!---------------------!----------------->age
regular adaptive flushing aggressive flushing aggr. checkpoints
@return limitation to start adaptive flushing */
lsn_t adaptive_flush_min_age() const;
/** Once checkpoint age exceeds that value, the flushing of pages is the most
aggressive possible since then. For more details @see adaptive_flush_min_age.
@note This value changes only during calls to @see update or @see initialize.
@return limitation to start furious flushing */
lsn_t adaptive_flush_max_age() const;
/** Once checkpoint age exceeds that value, the log checkpointer thread keeps
writing checkpoints aggressively (whatever the progress of last_checkpoint_lsn
would it make). Before that happens, checkpoints could be written periodically
(for more details @see adaptive_flush_min_age).
@note This value changes only during calls to @see update or @see initialize.
@note It holds: adaptive_flush_max_age() < aggressive_checkpoint_min_age().
@return limitation to start aggressive checkpointing */
lsn_t aggressive_checkpoint_min_age() const;
/** Provides size of the next redo log file that will be created. The initial
value becomes set during a call to @see initialize. Since then, it changes
only when innodb_redo_log_capacity is changed, during a call to @see update.
@note Does not depend on whether the file actually might be created or not.
It is log_files_governor's responsibility not to exceed the physical capacity.
@remarks
The strategy used by the Log_files_capacity, guarantees that next redo log
file should always be possible to be created. That's because:
1. The next file size is always chosen as:
innodb_redo_log_capacity / LOG_N_FILES.
1. The logical capacity of the redo log is limited to:
(LOG_N_FILES - 2) / LOG_N_FILES * m_current_physical_capacity.
2. The m_current_physical_capacity is changed only after resize is finished,
and the resize is considered finished only when:
- all redo log files have size <= innodb_redo_log_capacity / LOG_N_FILES,
- and the logical size of the redo log can fit physical size of
LOG_N_FILES - 2 redo files, which guarantees that at most
LOG_N_FILES - 1 redo files will ever need to exist (consider scenario
in which oldest_lsn is at the very end of the oldest redo files and
newest_lsn is at the very beginning of the newest redo file if you
are curious why -2 is there instead of -1).
@return file size suggested for next log file to create */
os_offset_t next_file_size() const;
/** Computes size of a next redo log file that would be chosen for a given
physical capacity.
@param[in] physical_capacity physical capacity assumed for computation
@return file size suggested for next log file to create */
static os_offset_t next_file_size(os_offset_t physical_capacity);
/** Provides margin which might be used ahead of the newest lsn to create
a next file earlier if needed (it will be created as unused redo file).
@note This value changes only during calls to @see update or @see initialize.
@return the maximum allowed margin ahead of the newest lsn to be reserved */
lsn_t next_file_earlier_margin() const;
/** Computes margin which might be used ahead of the newest lsn to create
a next file earlier if needed (it will be created as unused redo file).
The computation is done for a given physical capacity.
@param[in] physical_capacity physical capacity assumed for computation
@return the maximum allowed margin ahead of the newest lsn to be reserved */
static lsn_t next_file_earlier_margin(os_offset_t physical_capacity);
/** Computes hard logical capacity, that corresponds to the provided
soft logical capacity of the redo log (@see soft_logical_capacity()).
@param[in] soft_logical_capacity logical capacity for user threads,
used in log_free_check() calls
@return hard logical capacity */
static lsn_t guess_hard_logical_capacity_for_soft(
lsn_t soft_logical_capacity);
/** Computes soft logical capacity, that corresponds to the provided
hard logical capacity of the redo log (@see hard_logical_capacity()).
@param[in] hard_logical_capacity logical capacity for the log writer
@return soft logical capacity */
static lsn_t soft_logical_capacity_for_hard(lsn_t hard_logical_capacity);
/** Computes hard logical capacity, that corresponds to the provided
physical capacity of the redo log (@see hard_logical_capacity()).
@param[in] physical_capacity physical capacity for the redo log
@return hard logical capacity */
static lsn_t hard_logical_capacity_for_physical(
os_offset_t physical_capacity);
/** Computes maximum age of dirty pages up to which there is no sync flush
enforced on page cleaners. This is a smaller value than soft logical capacity,
because sync flush must be started earlier than log_free_check() calls begin
to stop user threads.
@param[in] soft_logical_capacity logical capacity for user threads,
used in log_free_check() calls
@return maximum age of dirty pages before the sync flush is started */
static lsn_t sync_flush_logical_capacity_for_soft(
lsn_t soft_logical_capacity);
/** Computes soft logical capacity, that corresponds to the provided
maximum age of dirty pages up to which there is no sync flush enforced
on page cleaners. This is a larger value than the provided maximum age,
because sync flush must be started earlier than log_free_check() calls
begin to stop user threads.
@param[in] adaptive_flush_max_age maximum age of dirty page without
sync flush started
@return soft logical capacity */
static lsn_t guess_soft_logical_capacity_for_sync_flush(
lsn_t adaptive_flush_max_age);
private:
/** @see m_exposed */
struct Exposed {
/** Value returned by @see soft_logical_capacity */
atomic_lsn_t m_soft_logical_capacity{0};
/** Value returned by @see hard_logical_capacity */
atomic_lsn_t m_hard_logical_capacity{0};
/** Value returned by @see adaptive_flush_min_age */
atomic_lsn_t m_adaptive_flush_min_age{0};
/** Value returned by @see adaptive_flush_max_age */
atomic_lsn_t m_adaptive_flush_max_age{0};
/** Value returned by @see aggressive_checkpoint_min_age */
atomic_lsn_t m_agressive_checkpoint_min_age{0};
};
/** Cache for values returned by getters in this object, which otherwise
would need to be computed on-demand. These values do not have impact on
state updates of this object.
@note Updated only during calls to @see initialize and @see update. */
Exposed m_exposed{};
/** This is limitation for space on disk we are never allowed to exceed.
This is the guard of disk space - current size of all log files on disk
is always not greater than this value.
@note Updated only during calls to @see initialize and @see update. */
os_offset_t m_current_physical_capacity{0};
/** Goal we are trying to achieve for m_current_physical_capacity when
resize operation is in progress, else: equal to m_current_physical_capacity.
During startup (when srv_is_being_started is true) it stays equal to the
m_current_physical_capacity (which is then computed for discovered log files).
After startup, it's set to srv_redo_log_capacity_used by calls to
@see update. */
os_offset_t m_target_physical_capacity{0};
/** Current resize direction. When user decides to resize down the redo log,
it becomes Log_resize_mode::RESIZING_DOWN until the resize is finished or
user decides to stop it (providing other capacity). Note, that resize is not
started during startup (when srv_is_being_started is true).
@note Updated only during calls to @see initialize and @see update. */
Log_resize_mode m_resize_mode{Log_resize_mode::NONE};
/** Cancels current resize operation immediately.
@remarks If the innodb_redo_log_capacity is changed when there is a previous
redo resize in progress, the previous resize is first cancelled. */
void cancel_resize();
/** Updates m_target_physical_capacity (reading srv_redo_log_capacity_used)
and possibly starts a new downsize operation. Might also update:
m_resize_mode, m_current_physical_capacity. */
void update_target();
/** Checks if target of the resize is reached, with regards to the criteria
based on the current logical size of the redo.
@param[in] current_logical_size current logical size of the redo log
@return true iff the target is reached */
bool is_target_reached_for_logical_size(lsn_t current_logical_size) const;
/** Checks if target of the resize is reached, with regards to the criteria
based on the current physical size of existing log files (excludes unused).
@param[in] current_physical_size total size of existing redo log files,
excluding unused (spare) files
@return true iff the target is reached */
bool is_target_reached_for_physical_size(
os_offset_t current_physical_size) const;
/** Checks if target of the resize is reached, with regards to the criteria
based on the largest existing redo file.
@param[in] files in-memory dictionary of existing files
@return true iff the target is reached */
bool is_target_reached_for_max_file_size(const Log_files_dict &files) const;
/** Checks if target of the resize is reached.
@param[in] files in-memory dictionary of existing files
@param[in] current_logical_size current logical size of the redo log
@return true iff the target is reached */
bool is_target_reached_for_resizing_down(const Log_files_dict &files,
lsn_t current_logical_size) const;
/** Checks if target of the resize is reached with regards to all criteria
and updates the m_resize_mode, m_current_physical_capacity when that happens
(marking the resize operation as finished).
@param[in] files in-memory dictionary of existing files
@param[in] current_logical_size current logical size of the redo log */
void update_if_target_reached(const Log_files_dict &files,
lsn_t current_logical_size);
/** Updates value of server status variable: innodb_redo_log_resize_status. */
void update_resize_status_variable();
/** Updates values of server status variables:
innodb_redo_log_capacity_resized, innodb_redo_log_logical_size,
innodb_redo_log_physical_size, innodb_redo_log_resize_status.
@param[in] files in-memory dictionary of existing files
@param[in] current_logical_size current logical size of the redo log */
void update_status_variables(const Log_files_dict &files,
lsn_t current_logical_size);
/** Updates cached and exposed values related to the logical redo capacity:
- @see m_soft_logical_capacity
- @see m_hard_logical_capacity
- @see m_agressive_checkpoint_min_age
- @see m_adaptive_flush_min_age
- @see m_adaptive_flush_max_age
@param[in] current_logical_size current logical size of the redo log */
void update_exposed(lsn_t current_logical_size);
/** Computes suggested value for the current hard logical capacity.
@remarks This becomes non-trivial when the redo log is being resized down,
because this method is supposed to follow the checkpoint age then.
On the other hand, when the redo log is not being resized down, this method
computes the hard logical capacity by using simple math based on the current
physical capacity only (ie. ignoring the current checkpoint age).
@param[in] current_checkpoint_age current checkpoint age,
used only when resizing down
@return suggested value for current hard logical capacity */
lsn_t get_suggested_hard_logical_capacity(lsn_t current_checkpoint_age) const;
};
/** Retrieves limitations determined by the current state of log.m_capacity.
These values are retrieved atomically (are consistent with each other).
@param[in] log redo log
@param[out] limit_for_free_check soft capacity of the redo decreased by
the current free check margin; this is
limit for size of redo until which the
log_free_check calls do not force waits
@param[out] limit_for_dirty_page_age limit for the oldest dirty page until
which the async (adaptive) flushing is
not forced to be started (it might be
started if turned on explicitly by the
innodb_adaptive_flushing); note that
computation of this value include doing
the subtraction of the current log free
check margin */
void log_files_capacity_get_limits(const log_t &log,
lsn_t &limit_for_free_check,
lsn_t &limit_for_dirty_page_age);
#endif /* !log0files_capacity_h */
|