Skip to content

Latest commit

 

History

History
306 lines (236 loc) · 10.7 KB

code-related-to-log-files.md

File metadata and controls

306 lines (236 loc) · 10.7 KB

Code related to log files

This document summarizes the codes used when creating and reusing log files. The code below is based on MySQL 8.0.15.

Log File Creation

InnoDB creates all log files when starting the server.

storage/innobase/srv/srv0start.cc: 2164

/** Start InnoDB.
@param[in]  create_new_db       Whether to create a new database
@param[in]  scan_directories    Scan directories for .ibd files for
                                        recovery "dir1;dir2; ... dirN"
@return DB_SUCCESS or error code */
dberr_t srv_start(bool create_new_db, const std::string &scan_directories) {
...
    if (create_new_db) {
    ...
    err = create_log_files(logfilename, dirnamelen, flushed_lsn, logfile0,
                           new_checkpoint_lsn);
    ...

Create srv_n_log_files log files.

storage/innobase/srv/srv0start.cc: 366

/** Creates all log files.
@param[in,out]  logfilename     buffer for log file name
@param[in]      dirnamelen      length of the directory path
@param[in]      lsn             FIL_PAGE_FILE_FLUSH_LSN value
@param[out]     logfile0          name of the first log file
@param[out]     checkpoint_lsn  lsn of the first created checkpoint
@return DB_SUCCESS or error code */
static dberr_t create_log_files(char *logfilename, size_t dirnamelen, lsn_t lsn,
                                char *&logfile0, lsn_t &checkpoint_lsn) {
...
    for (unsigned i = 0; i < srv_n_log_files; i++) {
    sprintf(logfilename + dirnamelen, "ib_logfile%u", i ? i : INIT_LOG_FILE0);

    err = create_log_file(&files[i], logfilename);

    if (err != DB_SUCCESS) {
      return (err);
    }
  }
...

storage/innobase/srv/srv0start.cc: 277

/** Creates a log file.
 @return DB_SUCCESS or error code */
static MY_ATTRIBUTE((warn_unused_result)) dberr_t
    create_log_file(pfs_os_file_t *file, /*!< out: file handle */
                    const char *name)    /*!< in: log file name */
{
    bool ret;

    *file = os_file_create(innodb_log_file_key, name,
                         OS_FILE_CREATE | OS_FILE_ON_ERROR_NO_EXIT,
                         OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret);
    ...

os_file_create() eventually calls os_file_create_func(). In os_file_create_func(), InnoDB creates a new file with open() function.

storage/innobase/os/os0file.cc: 3201

/** NOTE! Use the corresponding macro os_file_create(), not directly
this function!
Opens an existing file or creates a new.
@param[in]  name        name of the file or path as a null-terminated
                                string
@param[in]  create_mode create mode
@param[in]  purpose     OS_FILE_AIO, if asynchronous, non-buffered I/O
                                is desired, OS_FILE_NORMAL, if any normal file;
                                NOTE that it also depends on type, os_aio_..
                                and srv_.. variables whether we really use async
                                I/O or unbuffered I/O: look in the function
                                source code for the exact rules
@param[in]  type        OS_DATA_FILE or OS_LOG_FILE
@param[in]  read_only   true, if read only checks should be enforcedm
@param[in]  success     true if succeeded
@return handle to the file, not defined if error, error number
        can be retrieved with os_file_get_last_error */
pfs_os_file_t os_file_create_func(const char *name, ulint create_mode,
                                  ulint purpose, ulint type, bool read_only,
                                  bool *success) {
...
    do {
    file.m_file = ::open(name, create_flag, os_innodb_umask);

    if (file.m_file == -1) {
        const char *operation;

        operation =
            (create_mode == OS_FILE_CREATE && !read_only) ? "create" : "open";

        *success = false;

        if (on_error_no_exit) {
        retry = os_file_handle_error_no_exit(name, operation, on_error_silent);
        } else {
        retry = os_file_handle_error(name, operation);
        }
    } else {
        *success = true;
        retry = false;
    }

    } while (retry);

After creating a log file, InnoDB calls os_file_set_size function, and it writes the specified number (srv_log_file_size) of zeros to the file specific offset (0).

storage/innobase/srv/srv0start.cc: 299

/** Creates a log file.
 @return DB_SUCCESS or error code */
static MY_ATTRIBUTE((warn_unused_result)) dberr_t
    create_log_file(pfs_os_file_t *file, /*!< out: file handle */
                    const char *name)    /*!< in: log file name */
{
    bool ret;

    *file = os_file_create(innodb_log_file_key, name,
                            OS_FILE_CREATE | OS_FILE_ON_ERROR_NO_EXIT,
                            OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret);
    ...
    ret = os_file_set_size(name, *file, 0, (os_offset_t)srv_log_file_size,
                            srv_read_only_mode, true);
    ...
    ret = os_file_close(*file);
    ut_a(ret);

    return (DB_SUCCESS);

At this time, the buffer to write is aligned for possible raw I/O.

storage/innobase/os/os0file.cc: 5346

/**  Write the specified number of zeros to a file from specific offset.
@param[in]  name        name of the file or path as a null-terminated
                                string
@param[in]  file        handle to a file
@param[in]  offset      file offset
@param[in]  size        file size
@param[in]  read_only   Enable read-only checks if true
@param[in]  flush       Flush file content to disk
@return true if success */
bool os_file_set_size(const char *name, pfs_os_file_t file, os_offset_t offset,
                      os_offset_t size, bool read_only, bool flush) {
    /* Write up to FSP_EXTENT_SIZE bytes at a time. */
    ulint buf_size = 0;

    if (size <= UNIV_PAGE_SIZE) {
        buf_size = 1;
    } else {
        buf_size = ut_min(static_cast<ulint>(64),
                        static_cast<ulint>(size / UNIV_PAGE_SIZE));
    }

    ut_ad(buf_size != 0);

    buf_size *= UNIV_PAGE_SIZE;

    /* Align the buffer for possible raw i/o */
    byte *buf2;

    buf2 = static_cast<byte *>(ut_malloc_nokey(buf_size + UNIV_PAGE_SIZE));

    byte *buf = static_cast<byte *>(ut_align(buf2, UNIV_PAGE_SIZE));

    /* Write buffer full of zeros */
    memset(buf, 0, buf_size);
    ...
    err = os_file_write(request, name, file, buf, current_size, n_bytes);
    ...

Log File Reuse

When writing data from the log buffer to the log file, if there is insufficient free space in the log file, start_next_file() function is called.

storage/innobase/log/log0write.cc :1602

static void log_files_write_buffer(log_t &log, byte *buffer, size_t buffer_size,
                                   lsn_t start_lsn) {
    ut_ad(log_writer_mutex_own(log));

    using namespace Log_files_write_impl;

    validate_buffer(log, buffer, buffer_size);

    validate_start_lsn(log, start_lsn, buffer_size);

    checkpoint_no_t checkpoint_no = log.next_checkpoint_no.load();

    const auto real_offset = compute_real_offset(log, start_lsn);

    bool write_from_log_buffer;

    auto write_size = compute_how_much_to_write(log, real_offset, buffer_size,
                                                write_from_log_buffer);

    if (write_size == 0) {
        start_next_file(log, start_lsn);
        return;
    ...

In this function, the new log file number n is calculated through uint32_t nth_file = static_cast<uint32_t>(real_offset / log.file_size);, and the nth log file header is flushed.

storage/innobase/log/log0write.cc: 1231

static void start_next_file(log_t &log, lsn_t start_lsn) {
    const auto before_update = log.current_file_end_offset;

    auto real_offset = before_update;

    ut_a(log.file_size % OS_FILE_LOG_BLOCK_SIZE == 0);
    ut_a(real_offset / log.file_size <= ULINT_MAX);

    ut_a(real_offset <= log.files_real_capacity);

    if (real_offset == log.files_real_capacity) {
        /* Wrapped log files, start at file 0,
        just after its initial headers. */
        real_offset = LOG_FILE_HDR_SIZE;
    }

    ut_a(real_offset + OS_FILE_LOG_BLOCK_SIZE <= log.files_real_capacity);

    /* Flush header of the new log file. */
    uint32_t nth_file = static_cast<uint32_t>(real_offset / log.file_size);
    log_files_header_flush(log, nth_file, start_lsn);

    /* Update following members of log:
    - current_file_lsn,
    - current_file_real_offset,
    - current_file_end_offset.
    The only reason is to optimize future calculations
    of offsets within the new log file. */
    log_files_update_offsets(log, start_lsn);

    ut_a(log.current_file_real_offset == before_update + LOG_FILE_HDR_SIZE ||
        (before_update == log.files_real_capacity &&
            log.current_file_real_offset == LOG_FILE_HDR_SIZE));

    ut_a(log.current_file_real_offset - LOG_FILE_HDR_SIZE ==
        log.current_file_end_offset - log.file_size);

    log.write_ahead_end_offset = 0;
    }

log_files_header_flush() calls log_files_header_fill(). buf equal to the size of the log block (OS_FILE_LOG_BLOCK_SIZE = 512) is initialized to 0 through the memset() function, and then header information is written to buf through the mach_write_to_x() function.

storage/innobase/log/log0chkp.cc: 261

void log_files_header_fill(byte *buf, lsn_t start_lsn, const char *creator) {
  memset(buf, 0, OS_FILE_LOG_BLOCK_SIZE);
  
  mach_write_to_4(buf + LOG_HEADER_FORMAT, LOG_HEADER_FORMAT_CURRENT);
  mach_write_to_8(buf + LOG_HEADER_START_LSN, start_lsn);
    
  strncpy(reinterpret_cast<char *>(buf) + LOG_HEADER_CREATOR, creator,
          LOG_HEADER_CREATOR_END - LOG_HEADER_CREATOR);
  
  ut_ad(LOG_HEADER_CREATOR_END - LOG_HEADER_CREATOR >= strlen(creator));
  
  log_block_set_checksum(buf, log_block_calc_checksum_crc32(buf));
} 

void log_files_header_flush(log_t &log, uint32_t nth_file, lsn_t start_lsn) {
  ut_ad(log_writer_mutex_own(log));
  
  MONITOR_INC(MONITOR_LOG_NEXT_FILE);

  ut_a(nth_file < log.n_files);

  byte *buf = log.file_header_bufs[nth_file];
  
  log_files_header_fill(buf, start_lsn, LOG_HEADER_CREATOR_CURRENT);
  
  DBUG_PRINT("ib_log", ("write " LSN_PF " file " ULINTPF " header", start_lsn,
                        ulint(nth_file)));

  const auto dest_offset = nth_file * uint64_t{log.file_size};
       
  const auto page_no =
      static_cast<page_no_t>(dest_offset / univ_page_size.physical());

  auto err = fil_redo_io(
      IORequestLogWrite, page_id_t{log.files_space_id, page_no}, univ_page_size,
      static_cast<ulint>(dest_offset % univ_page_size.physical()),
      OS_FILE_LOG_BLOCK_SIZE, buf); 

  ut_a(err == DB_SUCCESS);
}

In Summary, after initializing a log block to use with zero (using memset()), the necessary information was written to the log block. There was no function to initialize the entire log file for reuse.