polardbxengine/storage/innobase/fsp/lizard0fsp.cc

445 lines
13 KiB
C++

/* Copyright (c) 2018, 2021, Alibaba and/or its affiliates. All rights reserved.
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 also distributed 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/PolarDB-X Engine hereby grant you an
additional permission to link the program and your derivative works with the
separately licensed software that they have included with
MySQL/PolarDB-X Engine.
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 fsp/lizard0fsp.cc
Special Lizard tablespace implementation.
Created 2020-03-19 by Jianwei.zhao
*******************************************************/
#include "mtr0mtr.h"
#include "trx0purge.h"
#include "lizard0dict.h"
#include "lizard0fil.h"
#include "lizard0fsp.h"
#include "lizard0fspspace.h"
#include "lizard0txn.h"
namespace lizard {
/** Global lizard tablespace object */
LizardTablespace srv_lizard_space;
/** Judge whether it's lizard tablespace according to space id
@param[in] space id
@return true yes */
bool fsp_is_lizard_tablespace(space_id_t space_id) {
return space_id == dict_lizard::s_lizard_space_id;
}
/** Init the lizard tablespace header when install db
@param[in] size tablesapce inited size
@return true sucesss */
bool fsp_header_init_for_lizard(page_no_t size) {
DBUG_TRACE;
mtr_t mtr;
mtr_start(&mtr);
bool ret = fsp_header_init(dict_lizard::s_lizard_space_id, size, &mtr, false);
mtr_commit(&mtr);
return ret;
}
/** Get the size of lizard tablespace from header */
page_no_t fsp_header_get_lizard_tablespace_size(void) {
fil_space_t *space = fil_space_get_lizard_space();
mtr_t mtr;
mtr_start(&mtr);
mtr_x_lock_space(space, &mtr);
fsp_header_t *header;
header = fsp_get_space_header(dict_lizard::s_lizard_space_id, univ_page_size,
&mtr);
page_no_t size;
size = mach_read_from_4(header + FSP_SIZE);
ut_ad(space->size_in_header == size);
mtr_commit(&mtr);
return size;
}
/** Frees the memory */
void LizardTablespace::shutdown() {
Tablespace::shutdown();
m_auto_extend_last_file = 0;
m_last_file_size_max = 0;
}
/**
Interpret the lizard tablespace configure.
For the simplicity most of the variables are hard code.
@retval true Success
*/
bool LizardTablespace::interpret_file() {
DBUG_TRACE;
const char *file_name;
page_no_t size;
ut_ad(!m_auto_extend_last_file);
ut_ad(m_last_file_size_max == 0);
/** flags, path, and name should have been set in advance */
ut_ad(flags() != 0);
ut_ad(path() && name());
/** The page size is system page size from srv_page_size. */
size = static_cast<page_no_t>(dict_lizard::s_file_init_size / UNIV_PAGE_SIZE);
ut_ad(size > 16);
ut_a(dict_lizard::s_n_files == 1);
file_name = dict_lizard::s_lizard_space_file_name;
m_auto_extend_last_file = dict_lizard::s_file_auto_extend;
m_last_file_size_max = dict_lizard::s_last_file_max_size;
m_files.push_back(Datafile(file_name, flags(), size, 0));
Datafile *df = &m_files.back();
df->make_filepath(path(), file_name, NO_EXT);
/** TODO: here didn't support raw device */
df->m_type = SRV_NOT_RAW;
ut_ad(dict_lizard::s_n_files == ulint(m_files.size()));
return true;
}
/**
Check the data file status.
@param[in] datafile file
@param[in/out] reason_if_failed
@retval DB_SUCCESS success
*/
dberr_t LizardTablespace::check_file_status(const Datafile &file,
file_status_t &reason_if_failed) {
dberr_t err = DB_SUCCESS;
os_file_stat_t stat;
/** Lizard is persist data tablespace, so affected by srv_read_only_mode */
ut_ad(!m_ignore_read_only);
memset(&stat, 0x0, sizeof(stat));
err = os_file_get_status(file.m_filepath, &stat, true, srv_read_only_mode);
reason_if_failed = FILE_STATUS_VOID;
switch (err) {
case DB_FAIL:
lizard_error(ER_LIZARD) << "Check file failed on " << file.name();
err = DB_ERROR;
reason_if_failed = FILE_STATUS_RW_PERMISSION_ERROR;
break;
case DB_SUCCESS:
if (stat.type == OS_FILE_TYPE_FILE) {
if (!stat.rw_perm) {
lizard_error(ER_LIZARD)
<< "The " << file.name() << " must be writable or readable";
err = DB_ERROR;
reason_if_failed = FILE_STATUS_READ_WRITE_ERROR;
}
} else {
lizard_error(ER_LIZARD) << "The " << file.name() << " file is not "
<< " a regular InnoDB data file";
err = DB_ERROR;
reason_if_failed = FILE_STATUS_NOT_REGULAR_FILE_ERROR;
}
break;
case DB_NOT_FOUND:
break;
default:
ut_ad(0);
}
return err;
}
/**
Deal with the file when it's found
@param[in] file Datafile object
@retval DB_SUCCESS success */
void LizardTablespace::file_found(Datafile &file) {
file.m_exists = true;
file.set_open_flags(OS_FILE_OPEN);
}
/**
Deal with the file when it's not found
@param[in] file Datafile object
@param[in] create_new_db
@retval DB_SUCCESS success */
dberr_t LizardTablespace::file_not_found(Datafile &file, bool create_new_db) {
file.m_exists = false;
if (!create_new_db) {
lizard_error(ER_LIZARD)
<< "Data file " << file.name() << " didn't found when boot InnoDB";
return DB_ERROR;
}
lizard_info(ER_LIZARD) << "Create new data file " << file.name() << " in "
<< name();
file.set_open_flags(OS_FILE_CREATE);
return DB_SUCCESS;
}
/**
Check the data file when innodb_init_files()
@param[in] create_new_db Whether init db or restart
@param[in] min_expected_size The min siz of files (bytes)
@return DB_SUCCESS success
*/
dberr_t LizardTablespace::check_file_spec(bool create_new_db,
ulint min_expected_size) {
dberr_t err = DB_SUCCESS;
ut_ad(!m_ignore_read_only);
/** Data file amount limit check */
if (m_files.size() >= 2) {
lizard_error(ER_LIZARD) << "It must be less than 2 files in " << name()
<< " But actually has " << m_files.size();
return DB_ERROR;
}
/** Data file size check */
if (get_sum_of_sizes() < min_expected_size / UNIV_PAGE_SIZE) {
lizard_error(ER_LIZARD) << "Tablespace files size is less than "
<< min_expected_size / 1024 / 1024 << "MB";
return DB_ERROR;
}
for (auto it = m_files.begin(); it != m_files.end(); it++) {
file_status_t reason_if_failed;
err = check_file_status(*it, reason_if_failed);
if (err == DB_NOT_FOUND) {
err = file_not_found(*it, create_new_db);
if (err != DB_SUCCESS)
break;
} else if (err != DB_SUCCESS) {
lizard_error(ER_LIZARD)
<< "Check file status " << it->name() << " failed ";
break;
} else if (create_new_db) {
lizard_error(ER_LIZARD)
<< "Find the data file " << it->name() << " when create database";
err = DB_ERROR;
break;
} else {
file_found(*it);
}
}
return err;
}
/** Verify the size of the physical file.
@param[in] file data file object
@return DB_SUCCESS if OK else error code. */
dberr_t LizardTablespace::check_size(Datafile &file) {
os_offset_t size = os_file_get_size(file.m_handle);
ut_a(size != (os_offset_t)-1);
ut_a(m_files.size() == 1);
/* Under some error conditions like disk full scenarios
or file size reaching filesystem limit the data file
could contain an incomplete extent at the end. When we
extend a data file and if some failure happens, then
also the data file could contain an incomplete extent.
So we need to round the size downward to a megabyte. */
page_no_t rounded_size_pages = static_cast<page_no_t>(
((size / (1024 * 1024)) * ((1024 * 1024) / UNIV_PAGE_SIZE)));
/* If last file */
if (&file == &m_files.back() && m_auto_extend_last_file) {
if (file.m_size > rounded_size_pages ||
(m_last_file_size_max > 0 &&
m_last_file_size_max < rounded_size_pages)) {
lizard_error(ER_LIZARD)
<< "The Auto-extending " << name() << " data file '"
<< file.filepath()
<< "' is"
" of a different size "
<< rounded_size_pages
<< " pages (rounded down to MB) than specified"
" in the .cnf file: initial "
<< file.m_size << " pages, max " << m_last_file_size_max
<< " (relevant if non-zero) pages!";
return (DB_ERROR);
}
file.m_size = rounded_size_pages;
}
if (rounded_size_pages != file.m_size) {
lizard_error(ER_LIZARD)
<< "The " << name() << " data file '" << file.filepath()
<< "' is of a different size " << rounded_size_pages
<< " pages (rounded down to MB)"
" than the "
<< file.m_size
<< " pages specified in"
" the .cnf file!";
return (DB_ERROR);
}
return (DB_SUCCESS);
}
/**
Open or create the lizard space
@param[in] create_new_db
@param[out] size of files
@retval DB_SUCCESS success */
dberr_t LizardTablespace::open_or_create(bool create_new_db,
page_no_t *sum_of_lizard_sizes) {
dberr_t err = DB_SUCCESS;
fil_space_t *space = nullptr;
*sum_of_lizard_sizes = 0;
ut_ad(!m_files.empty());
for (auto it = m_files.begin(); it != m_files.end(); it++) {
if (it->m_exists) {
err = open_file(*it);
} else {
err = create_file(*it);
}
if (err == DB_SUCCESS) {
file_found(*it);
}
if (err != DB_SUCCESS)
return err;
#if !defined(NO_FALLOCATE) && defined(UNIV_LINUX)
/* Note: This should really be per node and not per
tablespace because a tablespace can contain multiple
files (nodes). The implication is that all files of
the tablespace should be on the same medium. */
if (fil_fusionio_enable_atomic_write(it->m_handle)) {
if (srv_use_doublewrite_buf) {
ib::info(ER_IB_MSG_456) << "FusionIO atomic IO enabled,"
" disabling the double write buffer";
srv_use_doublewrite_buf = false;
}
it->m_atomic_write = true;
} else {
it->m_atomic_write = false;
}
#else
it->m_atomic_write = false;
#endif /* !NO_FALLOCATE && UNIV_LINUX*/
}
for (auto it = m_files.begin(); it != m_files.end(); it++) {
ut_ad(it->m_exists);
it->close();
if (it == m_files.begin()) {
space =
fil_space_create(name(), space_id(), flags(), FIL_TYPE_TABLESPACE);
} else {
/** Only support one file rigth now. */
ut_a(0);
}
page_no_t max_size = PAGE_NO_MAX;
if (!fil_node_create(it->m_filepath, it->m_size, space, false,
it->m_atomic_write, max_size)) {
err = DB_ERROR;
break;
}
*sum_of_lizard_sizes += it->m_size;
}
return err;
}
/** Open file */
dberr_t LizardTablespace::open_file(Datafile &file) {
dberr_t err = DB_SUCCESS;
ut_a(file.m_exists);
ut_a(file.m_type == SRV_NOT_RAW);
err = file.open_or_create(srv_read_only_mode);
check_size(file);
return err;
}
/** create file */
dberr_t LizardTablespace::create_file(Datafile &file) {
dberr_t err = DB_SUCCESS;
ut_a(!file.m_exists);
ut_a(file.m_type == SRV_NOT_RAW);
err = file.open_or_create(srv_read_only_mode);
if (err == DB_SUCCESS) err = set_size(file);
return err;
}
/** set size */
dberr_t LizardTablespace::set_size(Datafile &file) {
/* We created the data file and now write it full of zeros */
ib::info(ER_IB_MSG_440)
<< "Setting file '" << file.filepath() << "' size to "
<< (file.m_size >> (20 - UNIV_PAGE_SIZE_SHIFT))
<< " MB."
" Physically writing the file full; Please wait ...";
bool success = os_file_set_size(file.m_filepath, file.m_handle, 0,
static_cast<os_offset_t>(file.m_size)
<< UNIV_PAGE_SIZE_SHIFT,
srv_read_only_mode, true);
if (success) {
ib::info(ER_IB_MSG_441)
<< "File '" << file.filepath() << "' size is now "
<< (file.m_size >> (20 - UNIV_PAGE_SIZE_SHIFT)) << " MB.";
} else {
ib::error(ER_IB_MSG_442) << "Could not set the file size of '"
<< file.name() << "'. Probably out of disk space";
return (DB_ERROR);
}
return DB_SUCCESS;
}
} // namespace lizard