Wrong binlog size reported after moving binlogs on MySQL version 5.6

Some days ago we found very interesting bug report with my friend: http://bugs.mysql.com/bug.php?id=71879

Problem is that, if a client moves binlog files to the other folder, stops MySQL server, update in the config file bin_log and bin_log_index values for a new path and starts server again, then the result of SHOW BINARY LOG command will be wrong:

mysql> show binary logs;
+-------------------------+-----------+
| Log_name                | File_size |
+-------------------------+-----------+
| cluster-repo-bin.000001 |       120 |
| cluster-repo-bin.000002 |         0 |
| cluster-repo-bin.000003 |         0 |
| cluster-repo-bin.000004 |         0 |
| cluster-repo-bin.000005 |         0 |
| cluster-repo-bin.000006 |       120 |
+-------------------------+-----------+

As you see, the size of some binlog files are zero. So why? 😮

If you dive into the mysql internals, you can see these lines of codes in the sql/rpl_master.cc:

/* The file ends with EOF or empty line */

while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)

{

int dir_len;

ulonglong file_length= 0;                   // Length if open fails

fname[–length] = ‘\0’;                     // remove the newline

….

As you see, my_b_gets function reads index file of binlogs and assigns a real path of binlogs to the fname char array. When we debugged fname we detected that fname contains unreal path to the binlog files (for ex: ./mysql-bin.000001). For this reason the size calculator can’t give us real size of binlog files. To resolve this problem we added a few lines of code to correct the real path of files:

// bug fix for http://bugs.mysql.com/bug.php?id=71879

// if bin file path starts with ./ then we will prepend to it full path of binlog directory

if(fname[0] == ‘.’ && fname[1] == ‘/’)

{

std::string ofname(fname);

// get the new folder path of binlog files and assign to the bin_log_value variable.

std::string bin_log_value(mysql_bin_log.get_name());

// corrects the path of binlog file

std::size_t lpos = bin_log_value.find_last_of(“/”);

std::string dir_path = bin_log_value.substr(0, lpos);

std::string full_path = dir_path + ofname.substr(1, ofname.length());

strcpy(fname, full_path.c_str());

}

Bug fix – Error -1 from storage engine while creating table with non-existing datadir on MySQL version 5.6

According to this bug report http://bugs.mysql.com/bug.php?id=79151

when client tries to create a new table with not existing (or write permission denied) datadir, then he gets an error message like this: ERROR 1030 (HY000): Got error -1 from storage engine

As you see it is not informative error message, we don’t know what’s wrong at the backend. But in the version 5.7 it seems this problem was resolved already.

I discovered that at the low level of innodb storage engine, program tries to create subfolders, when occurs a problem it checks system error partly in the file storage/innobase/fil/fil0fil.cc:

success = os_file_create_subdirs_if_needed(path);

os_file_create_subdirs_if_needed function in the storage/innobase/os/os0file.cc file checks system errors but returns only boolean value depending on the result:

/*Creates all missing subdirectories along the given path.

@returnTRUE if call succeeded FALSE otherwise */

UNIV_INTERN

ibool

os_file_create_subdirs_if_needed(

/*=============================*/

const char*path)/*!< in: path name */

{

if (srv_read_only_mode) {

ib_logf(IB_LOG_LEVEL_ERROR,

“read only mode set. Can’t create subdirectories ‘%s'”,

path);

return(FALSE);

}

char*subdir = os_file_dirname(path);

if (strlen(subdir) == 1

    && (*subdir == OS_FILE_PATH_SEPARATOR || *subdir == ‘.’)) {

/* subdir is root or cwd, nothing to do */

mem_free(subdir);

return(TRUE);

}

/* Test if subdir exists */

os_file_type_ttype;

iboolsubdir_exists;

iboolsuccess = os_file_status(subdir, &subdir_exists, &type);

if (success && !subdir_exists) {

/* subdir does not exist, create it */

success = os_file_create_subdirs_if_needed(subdir);

if (!success) {

mem_free(subdir);

return(FALSE);

}

success = os_file_create_directory(subdir, FALSE);

}

mem_free(subdir);

return(success);

}

Depends on the value of boolean variable success in the fil0fil.cc file, program sends to the user unclear message. For this reason I added to the fil0fil.cc file a few lines of codes to resolve this problem:

// checking success variable value if false and global errno variable has a value

if(!success && errno) {

// sends to the client custom message with system error number

my_error(ER_GET_ERRNO, MYF(0), errno);

mem_free(path);

return(DB_ERROR);

}

MySQL doesn’t show informative error message for read-only filesystem in Linux on version 5.6

My first bug fix was about a year ago. You can see bug report here: http://bugs.mysql.com/bug.php?id=72259

If to shortly explain, so problem was that when you make file system read only, and try to start the server, it fails. And there is no exact error message in the log files which explains the problem correctly.

 

I added to the sql/mysqld.cc file a simple function whitch checks the given path’s permission and returns the state code:

// checks if file system is read-only

int is_filesystem_read_only(char const* name) {

    if (access(name, W_OK) == -1) {

        if (access(name, R_OK) == 0) {

            return R_OK; // read only

        } else if (access(name, F_OK) == 0) {

            return F_OK; // file exists but have not any access

        } else {

            return -1; // file does not exist

        }

    } else {

        return W_OK; // read/write

    }

}

Then in the mysqld_main function where everythink starts, I got a value of datadir parameter and pass to the is_filesystem_read_only function, then I check a state code. According to this state code I write a custom informative message to the log file. Codes are below:

// Get –datadir value to check if filesystem is read-only

  for (int i = 1; i < argc; i++) {

      if (strstr(argv[i], “–datadir”)) {

          std::string str(argv[i]);

          char const* dataDirPath = str.substr(10, str.length()).c_str();

          if (is_filesystem_read_only(dataDirPath) == R_OK) {

              my_message_local(ERROR_LEVEL, “File system (%s) is read-only”, dataDirPath);

              return 1;

          }

          break;

      }

  }

Bug fixed 🙂