Merge lp:~ihanick/percona-server/5.1-innodb-log_archiving into lp:percona-server/5.1

Proposed by Nickolay Ihalainen
Status: Work in progress
Proposed branch: lp:~ihanick/percona-server/5.1-innodb-log_archiving
Merge into: lp:percona-server/5.1
Diff against target: 2050 lines (+1499/-19)
26 files modified
Percona-Server/mysql-test/r/percona_server_variables_debug.result (+4/-0)
Percona-Server/mysql-test/r/percona_server_variables_release.result (+4/-0)
Percona-Server/mysql-test/suite/innodb_plugin/r/percona_log_archiving.result (+45/-0)
Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving-master.opt (+1/-0)
Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving.test (+191/-0)
Percona-Server/sql/handler.cc (+42/-0)
Percona-Server/sql/handler.h (+7/-0)
Percona-Server/sql/lex.h (+1/-0)
Percona-Server/sql/mysqld.cc (+2/-0)
Percona-Server/sql/sql_lex.h (+2/-0)
Percona-Server/sql/sql_parse.cc (+44/-1)
Percona-Server/sql/sql_yacc.yy (+20/-0)
Percona-Server/storage/innodb_plugin/fil/fil0fil.c (+222/-0)
Percona-Server/storage/innodb_plugin/handler/ha_innodb.cc (+130/-0)
Percona-Server/storage/innodb_plugin/include/fil0fil.h (+14/-1)
Percona-Server/storage/innodb_plugin/include/log0log.h (+28/-0)
Percona-Server/storage/innodb_plugin/include/log0log.ic (+4/-0)
Percona-Server/storage/innodb_plugin/include/os0file.h (+14/-0)
Percona-Server/storage/innodb_plugin/include/srv0srv.h (+22/-0)
Percona-Server/storage/innodb_plugin/include/srv0start.h (+22/-0)
Percona-Server/storage/innodb_plugin/log/log0log.c (+90/-3)
Percona-Server/storage/innodb_plugin/os/os0file.c (+152/-0)
Percona-Server/storage/innodb_plugin/srv/srv0srv.c (+366/-0)
Percona-Server/storage/innodb_plugin/srv/srv0start.c (+12/-14)
doc/source/index.rst (+1/-0)
doc/source/management/innodb_log_archiving.rst (+59/-0)
To merge this branch: bzr merge lp:~ihanick/percona-server/5.1-innodb-log_archiving
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Needs Fixing
Alexey Kopytov (community) Needs Fixing
Review via email: mp+103578@code.launchpad.net

This proposal supersedes a proposal from 2012-04-03.

Description of the change

https://blueprints.launchpad.net/percona-server/+spec/log-archiving blueprint implemented.

Based on original Yasufumi work: ~percona-dev/percona-server/5.1.59-innodb_log_archiving

How it works:
1) Additional log archiving thread created inside innodb if log archiving is enabled
2) new transaction log file is created by log archiving script at the start or right after finishing archiving process
3) If active innodb transaction log file is full empty transaction log swapped with the earliest used transaction log file
4) If we have innodb_xtra_log_archive_dir not in the same directory as innodb transaction logs, file move initiated
5) if innodb_xtra_expire_archive_logs_sec is not zero, purge check initiated
6) test case now not using temporary file names and not affected by slow testing environment
7) fixed problems from last MP
8) cross-filesystem archived log renaming

Additionally the patch contains PURGE ARCHIVED LOGS {BEFORE|TO} command and a new handlerton call: purge_archive_logs

Apr 4th:
Fixed problems from last MP:
- Added debug code to check file size on separate filesystem
- use 64bit offsets to copy archived logs
- Implemented and tested IO throttling
- Added innodb variable to control throttling
- use posix_fadvise to prevent caching

Apr 26th:
- fixed indentation and code line lengths
- fixed posix_fadvise usage
- fixed deadlock if archiving enabled dynamically
- Max checkpoint age now increased/decreased on archiving stop/start

Latest test run:
http://jenkins.percona.com/view/Percona%20Server%205.1/job/percona-server-5.1-param/308/

To post a comment you must log in.
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal

Nickolay -

Overall impression looks good. I have some comments/questions:

What we are going to do when/if UNIV_LOG_ARCHIVE InnoDB log archiving
is enabled upstream? The implementations are not drop-in replacements
for each other (see below), so maybe it's good to prevent potential
user confusion by inventing another name for the XtraDB one?

What is the difference between this implementation and
UNIV_LOG_ARCHIVE anyway? As I see it:
1) this one is simpler and more parallel.
2) UNIV_LOG_ARCHIVE is able to archive up to the last checkpoint,
   while this one is able to archive up to the last log file change.

Are there any other differences?

I am not sure about the archive file naming convention, i.e. including
the LSN in the file name instead of sequence number. With sequence
number it is easier to detect continuity errors when some file gets
lost. Perhaps include both?

SLEEP 4 in the test may result in non-deterministic behaviour on
loaded test machines. It's better to use DEBUG_SYNC facilities.

Why does log_sys->lsn need to be protected by log_sys->archived_mutex
too? I see no need to stop it advancing while the archiving is going
on as far as the log_write_low check is in place.

fil_swap_file does not have a header comment.

Please document the new fields in log_sys struct. I think a better
name for archived_mutex would be arch_sys_mutex or similar.

purge_archived_logs should have an InnoDB style header comment.

Please move SRV_PATH_SEPARATOR, srv_calc_low32, srv_calc_high32
definitions from srv0start.c to srv0start.h instead of copying them to
srv0srv.c

s/archivation/archiving

s/"Log archived until"/"Log archived up to" in log_print()

Diff line 315: whitespace mismatch

Inconsistent code formatting: occurences of "if(foo)" or even
"if( foo)". Should be "if (foo)" everywhere.

I'd try to pretend to support multiple log groups with
UT_LIST_GET_NEXT loop over the log_sys->log_groups, but I guess that's
strictly optional.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal
Download full text (3.2 KiB)

Nickolay,

I didn't review everything. Just thought it would be more efficient to do it iteratively.

Generally, I first suggest trying to make a debug build with your patch (I had a few issues see below), and then trying to run the percona_log_archiving test (it crashes due to an assertion failure, see below).

So the comments I have so far:

   - percona_server_variables_debug.result should also be adjusted
   - compiler warning in srv0srv.c. I suggest replacing

    if(expire_sec <= 0 || (ut_time() - fileinfo.mtime) <= expire_sec) {
  with
    if (ut_time() <= (ib_time_t)
        (fileinfo.mtime + expire_sec)) {
   - there is a compile-time assertion in debug builds, because the
     patch introduces 2 new SQLCOM_ commands, but does not add the
     corresponding SHOW STATUS variables to com_status_vars.
   - there are unused 'protocol' variables in ha_purge_archive_logs()
     and ha_purge_archive_logs_to()
   - after fixing the above issues with debug builds, I got a runtime
     assertion failure when trying to run the percona_log_archive
     test. The reason is that PURGE ARCHIVED LOGS sends neither OK nor
     error packet to the client. You should either call my_ok() and set
     res to FALSE in mysql_execute_command() or call my_error() and set
     res to TRUE, see purge_master_logs() for an example
   - there are main.percona_log_archiving failures in release builds in
     http://jenkins.percona.com/view/Percona%20Server%205.1/job/percona-server-5.1-param/254/#showFailuresLink
   - I think it's better to add the test to the innodb_plugin suite
     rather than main one
   - a more common way to pass test-specific options to mysqld is an
     .opt file, not a .cnf file. there are lots of examples in existing
     tests
   - the test case does "drop table if exists t1;" but then creates and
     uses table with the name 't'
   - some unnecessary changes:

  > @@ -645,5 +646,5 @@
  > { "TRIM", SYM(TRIM)},
  > { "VARIANCE", SYM(VARIANCE_SYM)},
  > { "VAR_POP", SYM(VARIANCE_SYM)},
  > - { "VAR_SAMP", SYM(VAR_SAMP_SYM)},
  > + { "VAR_SAMP", SYM(VAR_SAMP_SYM)}
  > };
  >

  and

  > @@ -4560,6 +4560,8 @@
  > return FALSE;
  > }
  >
  > +
  > +
  > bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
  > {
  > List<Item> field_list;
  >

  and

  > @@ -10709,9 +10710,12 @@ select_var_ident:
  > ;
  >
  > purge_options:
  > + ARCHIVED_SYM LOGS_SYM purge_archive_option
  > + |
  > master_or_binary LOGS_SYM purge_option
  > ;
  >
  > +
  > purge_option:
  > TO_SYM TEXT_STRING_sys
  > {
  >

   - please remove commented code, e.g.:

  > + //res = purge_master_logs(thd, lex->to_log);
  > + //fprintf(stderr, "purge archive logs command called %s\n", lex->to_log);
  >

   - SLEEPs in test cases almost never work. They definitely don't work
     for synchronization, but unfortunately there's no way to
     synchronize between mtr and InnoDB, especially when a background
     InnoDB thread has to be executed synchronously. Which means we have
     to modify tests to remove SLEEP()s possibly at the cost of test
     covera...

Read more...

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal
Download full text (4.3 KiB)

Nickolay,

The patch looks generally good to me, but I have a bunch of relatively minor comments:

   - the patch makes ARCHIVED a reserved keyword (so, for example,
     "CREATE TABLE archived(...)" succeeds in upstream server, but fails
     in PS). Please add ARCHIVED_SYM to the keyword_sp rule in sql_yacc.yy

   - innobase_purge_archive_logs() and purge_archived_logs() do
     something odd. So innobase_purge_archive_logs() takes the point in
     time up to which logs must be purged as its 'before_date'
     argument. Basically, all we need to do is to traverse all files in
     the archived logs directory and delete those with mtime <
     before_date, right? Here's what actually happens:

     - innobase_purge_archive_logs() takes the current time, subtracts
       before_date and passes that as the expire_sec argument to
       purge_archived_logs()
     - expire_sec argument is unsigned. So if you specify a point in
       time in the future, i.e. "PURGE ARCHIVED LOGS BEFORE NOW() +
       INTERVAL 1 MONTH" an overflow occurs and unexpected things may
       happen depending on sizeof(time_t) in the OS
     - purge_archived_logs() iterates the log files, takes their mtime,
       adds expire_sec and compares it with _current_ time again. So the
       exact cut-off time is drifting, which may become a problem when
       file deletion takes a long time (which is quite possible)
     - on a side note, purge_archive_logs() skips all files if
       expired_sec is 0. Why?

     So why not just pass before_date to purge_archived_logs() and
     compare mtime with it directly?

   - I think fil_swap_file() does too much unnecessary work on POSIX
     systems. It flushes the file being swapped, then waits for flush to
     complete, then closes the file, just because allegedly "we do not
     trust the operating systems can rename an open file". Well, they
     actually can. It is in the POSIX standard. Windows can do that too,
     but that requires a special flag when opening the file. Since
     Windows is not a priority for us, I suggest enclosing the
     unnecessary for POSIX code in #ifdef __WIN__. We can fix Windows
     later and remove that code in fil_swap_file() completely.

   - fil_swap_file() could use some descriptive comments for the
     function itself and its arguments. Also, fil_rotate_file() would
     probably be a bit more descriptive name.

   - innobase_log_archive_update() has a race condition. The entire
     function body must be protected by log_sys->archived_mutex,
     otherwise multiple concurrent threads calling that function can
     result in multiple log archive threads being created.

   - in innobase_log_archive_update() and
     innobase_log_archive_expire_update() you show update *var_ptr
     rather than innodb_* variables directly. The comment "where the
     formal string goes" in the var_ptr comment looks to be copy-pasted
     and not applicable to those 2 functions?

   - please don't use the Yoda notation, as in "if (FALSE ==
     log_xtra_log_archive_on)"

   - please use OS_FILE_MAX_PATH instead of 10000 in
     purge_archived_logs() and srv_log_archive_thread()

   - there are 2 open_or_...

Read more...

review: Needs Fixing
Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

Nickolay,

If I'm not mistaken, the only change since my previous review comments is revision 435 with some changes to tests. It doesn't look like any of my comments have been addressed yet. Is that correct, or I am missing something? Just trying to understand if the patch is ready for review.

review: Needs Information
Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal

Alexey,

Sorry, just have found your comments at 2012-02-19.
I'm going to fix these problems today and tomorrow.
In lastest MP was fixed only 2012-02-13 problems.

Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal
Download full text (3.2 KiB)

    percona_log_archiving.test: include have_innodb_plugin.inc instead
    of have_innodb.inc.

    I second Alexey's comments that SLEEP in a testcase will not work
    reliably. We can discuss alternatives on IRC.

    sql/handler.cc changes are inconsistent in "foo = " vs "foo=". It
    should be the latter in all cases.

    Replace if (foo) return TRUE; else return FALSE; constructs with
    return foo; (purge_archive_logs_handlerton,
    purge_archive_logs_to_handlerton)

    Replace (CONSTANT == var) with (var == CONSTANT) (same functions).
    Diff line 365: s/data/date ?
    Line 379: remove commented-out code

    fil_rotate_file can be simplified greatly taking into account
    Alexey's comment that we can rename open files.

    innobase_purge_archive_logs: if illegally formed to_filename is
    passed (i.e. no LSN in the file name), strtoll will return 0,
    calling purge_archived_logs with before_lsn == 0, purging all the
    files, which is probably not the best action. A testcase should be
    added for this case and strtoll errors handled.

    Similar issue is in purge_archived_logs. If someone puts a file
    that is named like archive log file but malformed (no LSN) in the
    archived log directory, strtoll will return 0 there to and the
    correctness of purge_archived_logs needs to be checked.

    The option descriptions:

    Line 805: s/"Set to 1 if you want to have logs archived"/"Enables
    log archiving".

    Line 809: s/"Where full logs should be archived."/"Path to
    archived logs directory"

    Line 813: s/expire time/expiration time

    fil_rotate_file declaration in fil0fil should have comments for
    arguments too, as does the definition.

    I have commented previously that new fields in log_sys struct
    should be commented. I think a better name for archived_mutex
    would be arch_sys_mutex or similar.

    I have asked previously, why do the log_sys->lsn increases need to
    be protected by the log_sys->archived_mutex?

    purge_archived_logs should have a clarification in the header
    comment if both args should not be set at the same time or what
    would happen if they are.

    os_file_rename_throttled: the typecasts for args of rename look
    redundant (the casted-to type is identical to the original type)

    The header comment for open_or_create_new_log_file looks truncated
    ("Creates or opens the next log file and closes that")

    srv_log_archive_thread: s/archivation/archiving (I had this
    comment before)

    I am not sure I understand the error message in
    srv_log_archive_thread: ""InnoDB: Warning: The new log file cannot
    be created. The above errors should be fixed
    will retry 10 seconds later again.". What does it mean "should be
    fixed"? Should a DBA be continuously monitoring the error log and
    fixing something in 10 seconds?

    Please use snprintf instead of sprintf everywhere to avoid buffer
    overflows (except maybe in purge_archived_logs: there sprintf use
    could be safe, but I'm not sure and erring on the side of snprintf
    is always safer).

    Documentation will need some more work, maybe with Hrvoje's
    help. It's OK fo...

Read more...

review: Needs Fixing
Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal
Download full text (4.2 KiB)

> Documentation will need some more work, maybe with Hrvoje's
> help. It's OK for documentation changes to be in a separate MP.

Things should be discussed:

>
> I second Alexey's comments that SLEEP in a testcase will not work
> reliably. We can discuss alternatives on IRC.

> I am not sure I understand the error message in
> srv_log_archive_thread: ""InnoDB: Warning: The new log file cannot
> be created. The above errors should be fixed
> will retry 10 seconds later again.". What does it mean "should be
> fixed"? Should a DBA be continuously monitoring the error log and
> fixing something in 10 seconds?
If logging is enabled we should guarantee that there is no missing files.
What we can do in disk full or invalid permissions situation?

> percona_log_archiving.test: include have_innodb_plugin.inc instead
> of have_innodb.inc.
Done

> sql/handler.cc changes are inconsistent in "foo = " vs "foo=". It
> should be the latter in all cases.
done

> Replace if (foo) return TRUE; else return FALSE; constructs with
> return foo; (purge_archive_logs_handlerton,
> purge_archive_logs_to_handlerton)
done

>
> Replace (CONSTANT == var) with (var == CONSTANT) (same functions).
> Diff line 365: s/data/date ?
> Line 379: remove commented-out code
done

>
> fil_rotate_file can be simplified greatly taking into account
> Alexey's comment that we can rename open files.
We should flush all blocks and close file before finishing archiving.
Should I split this function or this one will be fine?

> innobase_purge_archive_logs: if illegally formed to_filename is
> passed (i.e. no LSN in the file name), strtoll will return 0,
> calling purge_archived_logs with before_lsn == 0, purging all the
> files, which is probably not the best action. A testcase should be
> added for this case and strtoll errors handled.
Fixed, Should I add some error message/produce an warning?

>
> Similar issue is in purge_archived_logs. If someone puts a file
> that is named like archive log file but malformed (no LSN) in the
> archived log directory, strtoll will return 0 there to and the
> correctness of purge_archived_logs needs to be checked.
fixed

>
> The option descriptions:
> Line 805: s/"Set to 1 if you want to have logs archived"/"Enables
> log archiving".
> Line 809: s/"Where full logs should be archived."/"Path to
> archived logs directory"
> Line 813: s/expire time/expiration time
> fil_rotate_file declaration in fil0fil should have comments for
> arguments too, as does the definition.
done

>
> I have commented previously that new fields in log_sys struct
> should be commented. I think a better name for archived_mutex
> would be arch_sys_mutex or similar.
done

>
> I have asked previously, why do the log_sys->lsn increases need to
> be protected by the log_sys->archived_mutex?

In srv_log_archive_thread, log_sys->archived_lsn is calculated, value depends on log_sys->lsn
archived_lsn value is also used to understand if we have enough space in logs inside log_write_low.

E.g. If two or more...

Read more...

Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal

I only have cosmetic comments for this MP:

    Re. current SLEEP in testsuite. As previously discussed, I believe
    that it is "almost safe", but I wonder if the following may happen
    on very busy testing servers: around the SLEEP(1) time, the log
    archiving thread is starved (not scheduled to run), SLEEP(1)
    completes, then log archiving thread executes and rotates the log,
    failing the next list_files test. I don't know if that's maybe
    just a theoretical case, and we cannot do much about it anyway
    currently.

    The handler entry purge_archive_logs perhaps could be better
    named purge_archived_logs, but please make this change only if you
    have to resubmit the MP. Likewise for the new functions in handler.h|cc

    The typo fix s/data/date in sql_parse.cc should be made in own
    code, not in otherwise untouched upstream code (i.e. in
    SQLCOM_PURGE_ARCHIVE_BEFORE, not in "PURGE MASTER LOGS BEFORE")

    s/arhiving/archiving in log0log.h new struct log_sys fied
    comments.

    "0 == strcmp(log_xtra_log_archive_dir, srv_log_group_home_dirs[0]"
    in srv_log_archive_thread should be reversed.

Since my comments are so minor, I suggest to wait for Alexey's review before submitting a new MP.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

Nickolay,

I didn't have time to review everything, but the following code in os_file_rename_throttled() caught my eye:

> + success = os_file_get_size(file, &size_low, &size_high);
> + ut_a(success);
> +
> + for (offset_high = 0; offset_high<size_high; ++offset_high) {
> + for (offset = 0; offset<size_low/block_size; ++offset) {

I see the following problems with it:

1. when size_high == 0 (i.e. the file size is < 4 GB), we will have 0 loop iterations, i.e. we won't copy anything.

2. when size_high = 2 and size_low = 1024*1024 (i.e. the file size is 8 GB + 1 MB), we will have 2 iterations, each copying 1 MB, so we'll copy 2 MB in total.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

Oops, it looks like I reviewed and commented on a superseded proposal. But that code is the same with the current MP, so my previous comment still holds.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

Nickolay,

- I think the implementation of file copying in os_file_rename_throttled() could be a lot simpler and easier to read if it just used a 64-bit offset instead of messing with 32-bit parts. You can find an example in fil_reset_too_high_lsns().

- I don't understand this code:

  + if (max_iops > 100 && ((offset_low/block_size) % (max_iops/100)) == 0) {
  + os_thread_sleep(10000 /* 0.01 sec */);

  So what value does the max_iops argument actually has? The comment says "number of I/O operations per second", but looking at the code above, that doesn't seem to be the case.

- max_iops doesn't have any effect, because the caller of os_file_rename_throttled() always passes 0 as its value. Did you mean to add a new configuration variable?

review: Needs Fixing
Revision history for this message
Nickolay Ihalainen (ihanick) wrote : Posted in a previous version of this proposal
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

Nickolay,

   - please follow the InnoDB code style and limit the line
     width to 80 characters at least in your code
   - excessive indentation. instead of having:

    if (ret != 0) {
  long indented block;
 }

 return(TRUE);

      wouldn't it be better to have:

 if (ret == 0) {
    return(TRUE); // or "goto end;"
 }

 // long block with less indentation
 return(TRUE);

   - the above also applies to the "if (err == EXDEV)" block
   - POSIX_FADV_DONTNEED is used in a wrong way, see XtraBackup
     bug #925441
   - I think the following for loop:

for (offset = 0; offset <= file_size-(ib_int64_t)block_size; offset += block_size) {

     is equivalent to:

for (offset = 0; offset < file_size; offset += block_size) {

     but is a bit easier to read.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote :
Download full text (5.7 KiB)

   - as we discussed previously, the major problem with the current
     approach is that when log archiving is enabled the maximum
     checkpoint age is reduced by 1 log file size, which in the most
     common configuration with 2 log files means the available log space
     is reduced in half. but we will get to this issue later after
     implementing the XtraBackup part of the task;

   - another major problem that I discovered is that log archiving is
     not crash-safe: a crash may lead to 1) partially written archive
     files and 2) log archiving itself not being able to recover without
     user intervention; We should fix that, let's discuss the details later;

   Less significant problems:

   - no error handling in innobase_purge_archive_logs(). The function
     comment says "@return non-zero if error", but it always
     returns zero. The same applies to purge_archived_logs(), it should
     return an error code. Not having error handling in those functions
     is bad because it's currently impossible for a user to know after
     running PURGE ARCHIVED LOGS whether it was successful or not
     without looking at the error log. Instead, the PURGE statement
     itself should return an error to the client.
   - while you're fixing innobase_purge_archive_logs() you may want to
     replace strstr() with is_prefix() and
     strlen(IB_ARCHIVED_LOGS_PREFIX) with a constant,
     e.g. IB_ARCHIVED_LOGS_PREFIX_LEN.
   - as Laurynas wrote, the following change fixes a comment typo in the
     upstream comment. please revert it:

> === modified file 'Percona-Server/sql/sql_parse.cc'
> --- Percona-Server/sql/sql_parse.cc 2011-11-24 16:33:30 +0000
> +++ Percona-Server/sql/sql_parse.cc 2012-05-05 14:06:22 +0000
> @@ -2491,7 +2491,7 @@ mysql_execute_command(THD *thd)
>
> if (check_global_access(thd, SUPER_ACL))
> goto error;
> - /* PURGE MASTER LOGS BEFORE 'data' */
> + /* PURGE MASTER LOGS BEFORE 'date' */
> it= (Item *)lex->value_list.head();
> if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
> it->check_cols(1))
>
   - another change in the upstream code for no apparent reasons. Note
     that it changes code inside #ifdef UNIV_LOG_ARCHIVE which is never
     (and in fact, cannot be) enabled in Percona Server:

> @@ -852,7 +897,7 @@ log_init(void)
> #ifdef UNIV_LOG_ARCHIVE
> /* Under MySQL, log archiving is always off */
> log_sys->archiving_state = LOG_ARCH_OFF;
> - log_sys->archived_lsn = log_sys->lsn;
> + log_sys->archived_lsn = 0;
> log_sys->next_archived_lsn = 0;
>
> log_sys->n_pending_archive_ios = 0;
>

    that initialization should probably be outside of #ifdef UNIV_LOG_ARCHIVE?

   - I wonder if we a failure in os_file_rename_throttled() should
     result in a fatal error, i.e. an assertion failure. Currently, we
     do crash if a read or a write fail on a throttled cross-device
     copy. However, when rename() fails, we just return FALSE, which
     only results in a "Note:" message in the error log. Which looks
     inconsistent to me. E.g. if the destination directory does not
     exist, or we have insufficient privileges to write to it, logs will
     not be a...

Read more...

review: Needs Fixing
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

Due to log archiving running in a separate thread we need DBUG_SYNC in code/testcase at least between any places that produce log and check the archived log files, i.e. between last INSERT and following list_files_append_file.

In the code the sync point should be around os_event_set(log_sys->arch_event_rotate_done) call.

review: Needs Fixing

Unmerged revisions

449. By Nickolay Ihalainen

merge with 5.1 trunk

448. By Nickolay Ihalainen

Fixed deadlock if innodb_xtra_log_archive enabled dynamically. Correct posix_fadvise usage, indentation

447. By Nickolay Ihalainen

Add log archiving copy throttle variable to percona server variables test results

446. By Nickolay Ihalainen

fix file copy for archived log located on separate disks: prevent caching cleanup

445. By Nickolay Ihalainen

log archiving, copy log to separate disk, increase chunk to 64 innodb pages.

444. By Nickolay Ihalainen

log archiving, copy log to separate disk: copy now uses 64bit offset, implemented throttling

443. By Nickolay Ihalainen

pull changes from 5.1 trunk

442. By Nickolay Ihalainen

fix file copy for archived log located on separate disks

441. By Nickolay Ihalainen

code standards, comments, test for invalid purge archived logs to filename

440. By Nickolay Ihalainen

Added missing comments

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Percona-Server/mysql-test/r/percona_server_variables_debug.result'
2--- Percona-Server/mysql-test/r/percona_server_variables_debug.result 2012-01-30 04:30:33 +0000
3+++ Percona-Server/mysql-test/r/percona_server_variables_debug.result 2012-04-25 22:22:24 +0000
4@@ -161,6 +161,10 @@
5 INNODB_USE_SYS_STATS_TABLE
6 INNODB_VERSION
7 INNODB_WRITE_IO_THREADS
8+INNODB_XTRA_EXPIRE_ARCHIVE_LOGS_SEC
9+INNODB_XTRA_LOG_ARCHIVE
10+INNODB_XTRA_LOG_ARCHIVE_DIR
11+INNODB_XTRA_LOG_ARCHIVE_THROTTLE
12 INSERT_ID
13 INTERACTIVE_TIMEOUT
14 JOIN_BUFFER_SIZE
15
16=== modified file 'Percona-Server/mysql-test/r/percona_server_variables_release.result'
17--- Percona-Server/mysql-test/r/percona_server_variables_release.result 2011-11-24 02:01:33 +0000
18+++ Percona-Server/mysql-test/r/percona_server_variables_release.result 2012-04-25 22:22:24 +0000
19@@ -159,6 +159,10 @@
20 INNODB_USE_SYS_STATS_TABLE
21 INNODB_VERSION
22 INNODB_WRITE_IO_THREADS
23+INNODB_XTRA_EXPIRE_ARCHIVE_LOGS_SEC
24+INNODB_XTRA_LOG_ARCHIVE
25+INNODB_XTRA_LOG_ARCHIVE_DIR
26+INNODB_XTRA_LOG_ARCHIVE_THROTTLE
27 INSERT_ID
28 INTERACTIVE_TIMEOUT
29 JOIN_BUFFER_SIZE
30
31=== added file 'Percona-Server/mysql-test/suite/innodb_plugin/r/percona_log_archiving.result'
32--- Percona-Server/mysql-test/suite/innodb_plugin/r/percona_log_archiving.result 1970-01-01 00:00:00 +0000
33+++ Percona-Server/mysql-test/suite/innodb_plugin/r/percona_log_archiving.result 2012-04-25 22:22:24 +0000
34@@ -0,0 +1,45 @@
35+drop table if exists t;
36+create table t (a int not null) ENGINE=InnoDB;
37+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
38+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
39+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
40+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
41+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
42+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
43+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
44+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
45+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
46+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
47+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
48+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
49+SELECT sleep(1);
50+sleep(1)
51+0
52+PURGE ARCHIVED LOGS BEFORE ("tmpval" + INTERVAL 1 SECOND);
53+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
54+PURGE ARCHIVED LOGS TO "last_log_file_next";
55+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
56+PURGE ARCHIVED LOGS TO "ib_log_archive_#invalid";
57+last_log_file
58+SET GLOBAL innodb_xtra_log_archive = OFF;
59+SELECT @@innodb_xtra_log_archive;
60+@@innodb_xtra_log_archive
61+0
62+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
63+SET GLOBAL innodb_xtra_log_archive = ON;
64+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
65+SELECT sleep(1);
66+sleep(1)
67+0
68+SET @saved_innodb_xtra_expire_archive_logs_sec = @@innodb_xtra_expire_archive_logs_sec;
69+SET GLOBAL innodb_xtra_expire_archive_logs_sec = 1;
70+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
71+SET GLOBAL innodb_xtra_expire_archive_logs_sec = @saved_innodb_xtra_expire_archive_logs_sec;
72+SET @saved_innodb_xtra_log_archive_throttle = @@innodb_xtra_log_archive_throttle;
73+SET GLOBAL innodb_xtra_log_archive_throttle = 10;
74+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
75+SET GLOBAL innodb_xtra_log_archive_throttle = @saved_innodb_xtra_log_archive_throttle;
76+last_log_file
77+create table archived(id int);
78+drop table archived;
79+DROP TABLE t;
80
81=== added file 'Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving-master.opt'
82--- Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving-master.opt 1970-01-01 00:00:00 +0000
83+++ Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving-master.opt 2012-04-25 22:22:24 +0000
84@@ -0,0 +1,1 @@
85+--innodb_xtra_log_archive=1 --innodb_xtra_log_archive_dir=logs_archive --innodb_xtra_expire_archive_logs_sec=3600 --innodb_log_file_size=2M
86
87=== added file 'Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving.test'
88--- Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving.test 1970-01-01 00:00:00 +0000
89+++ Percona-Server/mysql-test/suite/innodb_plugin/t/percona_log_archiving.test 2012-04-25 22:22:24 +0000
90@@ -0,0 +1,191 @@
91+--source include/have_innodb_plugin.inc
92+--disable_warnings
93+drop table if exists t;
94+--enable_warnings
95+
96+let $MYSQLD_DATADIR= `SELECT @@datadir`;
97+let $MYSQLD_ARCHIVEDIR= `SELECT IF( locate('/', @@innodb_xtra_log_archive_dir) = 1, @@innodb_xtra_log_archive_dir, CONCAT(@@datadir, @@innodb_xtra_log_archive_dir))`;
98+let MYSQLD_ARCHIVEDIR = $MYSQLD_ARCHIVEDIR;
99+--mkdir $MYSQLD_ARCHIVEDIR
100+
101+create table t (a int not null) ENGINE=InnoDB;
102+
103+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
104+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
105+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
106+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
107+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
108+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
109+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
110+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
111+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
112+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
113+insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13);
114+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
115+
116+--list_files_append_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list $MYSQLD_ARCHIVEDIR ib_log_archive_*
117+# Set the purge time 1 second after the last modify time of last ib_log_archive_*
118+perl;
119+open FSRC, "<".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/archived_transaction_logs.list' or die "Tmp file archived_transaction_logs.list not found";
120+open F, ">>".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/percona_archived_logs.tmp' or die "Tmp file percona_archived_logs.tmp not found";
121+my $line;
122+while(<FSRC>) {
123+ chomp;
124+ if( index( $_,'ib_log_archive_') >= 0 ) {
125+ $line = $_;
126+ }
127+}
128+
129+my $binlogpath = $ENV{'MYSQLD_ARCHIVEDIR'}.'/'.$line;
130+my @array = stat($binlogpath);
131+my $filemodifytime = $array[9];
132+my @t = localtime $filemodifytime;
133+my $modifytime = sprintf "%04u-%02u-%02u %02u:%02u:%02u",$t[5]+1900,$t[4]+1,$t[3],$t[2],$t[1],$t[0];
134+printf F ("let \$last_log_file = %s;\n", $line);
135+printf F ("let \$tmpval = %s;\n", $modifytime);
136+close F;
137+EOF
138+
139+--source $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp
140+remove_file $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp;
141+remove_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list;
142+
143+SELECT sleep(1);
144+
145+--replace_result $tmpval tmpval
146+--eval PURGE ARCHIVED LOGS BEFORE ("$tmpval" + INTERVAL 1 SECOND)
147+--list_files $MYSQLD_ARCHIVEDIR $last_log_file
148+
149+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
150+
151+--list_files_append_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list $MYSQLD_ARCHIVEDIR ib_log_archive_*
152+# get last archived transaction log file
153+perl;
154+open FSRC, "<".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/archived_transaction_logs.list' or die "Tmp file archived_transaction_logs.list not found";
155+open F, ">>".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/percona_archived_logs.tmp' or die "Tmp file percona_archived_logs.tmp not found";
156+my $line;
157+my $next_lsn_file;
158+while(<FSRC>) {
159+ chomp;
160+ if( m/ib_log_archive_(\d+)$/ ) { $line = $_;$next_lsn_file = sprintf "ib_log_archive_%016d", ($1+1); }
161+}
162+
163+printf F ("let \$last_log_file = %s;\n", $line);
164+printf F ("let \$last_log_file_next = %s;\n", $next_lsn_file);
165+close F;
166+EOF
167+
168+--source $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp
169+remove_file $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp;
170+remove_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list;
171+
172+--replace_result $last_log_file_next last_log_file_next
173+--eval PURGE ARCHIVED LOGS TO "$last_log_file_next"
174+--list_files $MYSQLD_ARCHIVEDIR $last_log_file
175+
176+# check invalid filename
177+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
178+
179+--list_files_append_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list $MYSQLD_ARCHIVEDIR ib_log_archive_*
180+# get last archived transaction log file
181+perl;
182+open FSRC, "<".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/archived_transaction_logs.list' or die "Tmp file archived_transaction_logs.list not found";
183+open F, ">>".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/percona_archived_logs.tmp' or die "Tmp file percona_archived_logs.tmp not found";
184+my $line;
185+my $next_lsn_file;
186+while(<FSRC>) {
187+ chomp;
188+ if( m/ib_log_archive_(\d+)$/ ) { $line = $_;$next_lsn_file = sprintf "ib_log_archive_%016d", ($1+1); }
189+}
190+
191+printf F ("let \$last_log_file = %s;\n", $line);
192+printf F ("let \$last_log_file_next = %s;\n", $next_lsn_file);
193+close F;
194+EOF
195+
196+--source $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp
197+remove_file $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp;
198+remove_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list;
199+
200+--eval PURGE ARCHIVED LOGS TO "ib_log_archive_#invalid"
201+--replace_result $last_log_file last_log_file
202+--list_files $MYSQLD_ARCHIVEDIR $last_log_file
203+--remove_files_wildcard $MYSQLD_ARCHIVEDIR ib_log_archive_*
204+
205+# Check enable-disable
206+SET GLOBAL innodb_xtra_log_archive = OFF;
207+SELECT @@innodb_xtra_log_archive;
208+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
209+--list_files $MYSQLD_ARCHIVEDIR ib_log_archive_*
210+SET GLOBAL innodb_xtra_log_archive = ON;
211+
212+
213+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
214+
215+--list_files_append_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list $MYSQLD_ARCHIVEDIR ib_log_archive_*
216+perl;
217+open FSRC, "<".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/archived_transaction_logs.list' or die "Tmp file archived_transaction_logs.list not found";
218+open F, ">>".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/percona_archived_logs.tmp' or die "Tmp file percona_archived_logs.tmp not found";
219+my $line;
220+while(<FSRC>) {
221+ chomp;
222+ if( index( $_,'ib_log_archive_') >= 0 ) { $line = $_; }
223+}
224+
225+printf F ("let \$last_log_file = %s;", $line);
226+close F;
227+EOF
228+
229+--source $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp
230+remove_file $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp;
231+remove_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list;
232+
233+SELECT sleep(1);
234+
235+SET @saved_innodb_xtra_expire_archive_logs_sec = @@innodb_xtra_expire_archive_logs_sec;
236+SET GLOBAL innodb_xtra_expire_archive_logs_sec = 1;
237+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
238+--list_files $MYSQLD_ARCHIVEDIR $last_log_file
239+SET GLOBAL innodb_xtra_expire_archive_logs_sec = @saved_innodb_xtra_expire_archive_logs_sec;
240+
241+
242+
243+# check throttle
244+SET @saved_innodb_xtra_log_archive_throttle = @@innodb_xtra_log_archive_throttle;
245+SET GLOBAL innodb_xtra_log_archive_throttle = 10;
246+insert into t (a) select t1.a from t t1, t t2, t t3 LIMIT 100000;
247+SET GLOBAL innodb_xtra_log_archive_throttle = @saved_innodb_xtra_log_archive_throttle;
248+
249+--list_files_append_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list $MYSQLD_ARCHIVEDIR ib_log_archive_*
250+# get last archived transaction log file
251+perl;
252+open FSRC, "<".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/archived_transaction_logs.list' or die "Tmp file archived_transaction_logs.list not found";
253+open F, ">>".$ENV{'MYSQLTEST_VARDIR'}.'/tmp/percona_archived_logs.tmp' or die "Tmp file percona_archived_logs.tmp not found";
254+my $line;
255+my $next_lsn_file;
256+while(<FSRC>) {
257+ chomp;
258+ if( m/ib_log_archive_(\d+)$/ ) { $line = $_;$next_lsn_file = sprintf "ib_log_archive_%016d", ($1+1); }
259+}
260+
261+printf F ("let \$last_log_file = %s;\n", $line);
262+printf F ("let \$last_log_file_next = %s;\n", $next_lsn_file);
263+close F;
264+EOF
265+
266+--source $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp
267+remove_file $MYSQLTEST_VARDIR/tmp/percona_archived_logs.tmp;
268+remove_file $MYSQLTEST_VARDIR/tmp/archived_transaction_logs.list;
269+
270+
271+--replace_result $last_log_file last_log_file
272+--list_files $MYSQLD_ARCHIVEDIR $last_log_file
273+
274+
275+create table archived(id int);
276+drop table archived;
277+
278+DROP TABLE t;
279+
280+--remove_files_wildcard $MYSQLD_ARCHIVEDIR ib_log_archive_*
281+--rmdir $MYSQLD_ARCHIVEDIR
282
283=== modified file 'Percona-Server/sql/handler.cc'
284--- Percona-Server/sql/handler.cc 2011-11-24 16:33:30 +0000
285+++ Percona-Server/sql/handler.cc 2012-04-25 22:22:24 +0000
286@@ -4597,6 +4597,48 @@
287 return result;
288 }
289
290+static my_bool purge_archive_logs_handlerton(THD *thd, plugin_ref plugin,
291+ void *arg)
292+{
293+ ulong before_timestamp= *(ulong*) arg;
294+ handlerton *hton= plugin_data(plugin, handlerton *);
295+
296+ if (hton->purge_archive_logs == NULL)
297+ return TRUE;
298+
299+ return hton->purge_archive_logs(hton, before_timestamp, NULL);
300+}
301+
302+bool ha_purge_archive_logs(THD *thd, handlerton *db_type, void* args)
303+{
304+ if (db_type == NULL)
305+ return plugin_foreach(thd, purge_archive_logs_handlerton,
306+ MYSQL_STORAGE_ENGINE_PLUGIN, args);
307+
308+ return false;
309+}
310+
311+static my_bool purge_archive_logs_to_handlerton(THD *thd, plugin_ref plugin,
312+ void *arg)
313+{
314+ const char* to_filename= (const char*) arg;
315+ handlerton *hton= plugin_data(plugin, handlerton *);
316+
317+ if (hton->purge_archive_logs == NULL)
318+ return TRUE;
319+
320+ return hton->purge_archive_logs(hton, 0, to_filename);
321+}
322+
323+bool ha_purge_archive_logs_to(THD *thd, handlerton *db_type, void* args)
324+{
325+ if (db_type == NULL)
326+ return plugin_foreach(thd, purge_archive_logs_to_handlerton,
327+ MYSQL_STORAGE_ENGINE_PLUGIN, args);
328+
329+ return false;
330+}
331+
332 /*
333 Function to check if the conditions for row-based binlogging is
334 correct for the table.
335
336=== modified file 'Percona-Server/sql/handler.h'
337--- Percona-Server/sql/handler.h 2012-01-17 14:05:16 +0000
338+++ Percona-Server/sql/handler.h 2012-04-25 22:22:24 +0000
339@@ -693,6 +693,8 @@
340 int (*fill_files_table)(handlerton *hton, THD *thd,
341 TABLE_LIST *tables,
342 class Item *cond);
343+ int (*purge_archive_logs)(handlerton *hton, time_t before_date, const char* to_filename);
344+
345 uint32 flags; /* global handler flags */
346 /*
347 Those handlerton functions below are properly initialized at handler
348@@ -2074,6 +2076,11 @@
349 int ha_prepare(THD *thd);
350 int ha_recover(HASH *commit_list);
351
352+/* remove old archived transaction logs files */
353+bool ha_purge_archive_logs(THD *thd, handlerton *db_type, void* args);
354+bool ha_purge_archive_logs_to(THD *thd, handlerton *db_type, void* args);
355+
356+
357 /* transactions: these functions never call handlerton functions directly */
358 int ha_commit_trans(THD *thd, bool all);
359 int ha_autocommit_or_rollback(THD *thd, int error);
360
361=== modified file 'Percona-Server/sql/lex.h'
362--- Percona-Server/sql/lex.h 2011-11-24 02:01:23 +0000
363+++ Percona-Server/sql/lex.h 2012-04-25 22:22:24 +0000
364@@ -68,6 +68,7 @@
365 { "ANALYZE", SYM(ANALYZE_SYM)},
366 { "AND", SYM(AND_SYM)},
367 { "ANY", SYM(ANY_SYM)},
368+ { "ARCHIVED", SYM(ARCHIVED_SYM)},
369 { "AS", SYM(AS)},
370 { "ASC", SYM(ASC)},
371 { "ASCII", SYM(ASCII_SYM)},
372
373=== modified file 'Percona-Server/sql/mysqld.cc'
374--- Percona-Server/sql/mysqld.cc 2012-03-16 09:07:44 +0000
375+++ Percona-Server/sql/mysqld.cc 2012-04-25 22:22:24 +0000
376@@ -3070,6 +3070,8 @@
377 {"prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS},
378 {"purge", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]), SHOW_LONG_STATUS},
379 {"purge_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS},
380+ {"purge_archived", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_ARCHIVE]), SHOW_LONG_STATUS},
381+ {"purge_archived_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_ARCHIVE_BEFORE]), SHOW_LONG_STATUS},
382 {"release_savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RELEASE_SAVEPOINT]), SHOW_LONG_STATUS},
383 {"rename_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS},
384 {"rename_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_USER]), SHOW_LONG_STATUS},
385
386=== modified file 'Percona-Server/sql/sql_lex.h'
387--- Percona-Server/sql/sql_lex.h 2011-11-24 02:01:33 +0000
388+++ Percona-Server/sql/sql_lex.h 2012-04-25 22:22:24 +0000
389@@ -130,6 +130,8 @@
390 // TODO(mcallaghan): update status_vars in mysqld to export these
391 SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
392 SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SHOW_THREAD_STATS,
393+ SQLCOM_PURGE_ARCHIVE,
394+ SQLCOM_PURGE_ARCHIVE_BEFORE,
395 /* This should be the last !!! */
396 SQLCOM_END
397 };
398
399=== modified file 'Percona-Server/sql/sql_parse.cc'
400--- Percona-Server/sql/sql_parse.cc 2011-11-24 16:33:30 +0000
401+++ Percona-Server/sql/sql_parse.cc 2012-04-25 22:22:24 +0000
402@@ -2491,7 +2491,7 @@
403
404 if (check_global_access(thd, SUPER_ACL))
405 goto error;
406- /* PURGE MASTER LOGS BEFORE 'data' */
407+ /* PURGE MASTER LOGS BEFORE 'date' */
408 it= (Item *)lex->value_list.head();
409 if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
410 it->check_cols(1))
411@@ -2508,6 +2508,49 @@
412 res = purge_master_logs_before_date(thd, (ulong)it->val_int());
413 break;
414 }
415+ case SQLCOM_PURGE_ARCHIVE:
416+ {
417+ if (check_global_access(thd, SUPER_ACL))
418+ goto error;
419+ /* PURGE ARCHIVED LOGS TO 'file' */
420+ if (ha_purge_archive_logs_to(NULL, NULL, lex->to_log)) {
421+ my_ok(thd);
422+ } else {
423+ my_error(ER_LOG_PURGE_UNKNOWN_ERR, MYF(0), "PURGE ARCHIVE LOGS TO");
424+ goto error;
425+ }
426+
427+ break;
428+ }
429+ case SQLCOM_PURGE_ARCHIVE_BEFORE:
430+ {
431+ Item *it;
432+
433+ if (check_global_access(thd, SUPER_ACL))
434+ goto error;
435+ /* PURGE ARCHIVE LOGS BEFORE 'data' */
436+ it= (Item *)lex->value_list.head();
437+ if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
438+ it->check_cols(1))
439+ {
440+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE ARCHIVE LOGS BEFORE");
441+ goto error;
442+ }
443+ it= new Item_func_unix_timestamp(it);
444+ /*
445+ it is OK only emulate fix_fieds, because we need only
446+ value of constant
447+ */
448+ it->quick_fix_field();
449+ ulong before_timestamp = (ulong)it->val_int();
450+ if (ha_purge_archive_logs(NULL, NULL, &before_timestamp )) {
451+ my_ok(thd);
452+ } else {
453+ my_error(ER_LOG_PURGE_UNKNOWN_ERR, MYF(0), "PURGE ARCHIVE LOGS BEFORE");
454+ goto error;
455+ }
456+ break;
457+ }
458 #endif
459 case SQLCOM_SHOW_WARNS:
460 {
461
462=== modified file 'Percona-Server/sql/sql_yacc.yy'
463--- Percona-Server/sql/sql_yacc.yy 2012-04-02 02:09:15 +0000
464+++ Percona-Server/sql/sql_yacc.yy 2012-04-25 22:22:24 +0000
465@@ -714,6 +714,7 @@
466 %token AND_AND_SYM /* OPERATOR */
467 %token AND_SYM /* SQL-2003-R */
468 %token ANY_SYM /* SQL-2003-R */
469+%token ARCHIVED_SYM /* MYSQL */
470 %token AS /* SQL-2003-R */
471 %token ASC /* SQL-2003-N */
472 %token ASCII_SYM /* MYSQL-FUNC */
473@@ -10715,6 +10716,8 @@
474 ;
475
476 purge_options:
477+ ARCHIVED_SYM LOGS_SYM purge_archive_option
478+ |
479 master_or_binary LOGS_SYM purge_option
480 ;
481
482@@ -10732,6 +10735,22 @@
483 }
484 ;
485
486+purge_archive_option:
487+ TO_SYM TEXT_STRING_sys
488+ {
489+ Lex->to_log = $2.str;
490+ Lex->sql_command= SQLCOM_PURGE_ARCHIVE;
491+ }
492+ | BEFORE_SYM expr
493+ {
494+ LEX *lex= Lex;
495+ lex->value_list.empty();
496+ lex->value_list.push_front($2);
497+ lex->sql_command= SQLCOM_PURGE_ARCHIVE_BEFORE;
498+ }
499+ ;
500+
501+
502 /* kill threads */
503
504 kill:
505@@ -11776,6 +11795,7 @@
506 | AGGREGATE_SYM {}
507 | ALGORITHM_SYM {}
508 | ANY_SYM {}
509+ | ARCHIVED_SYM {}
510 | AT_SYM {}
511 | AUTHORS_SYM {}
512 | AUTO_INC {}
513
514=== modified file 'Percona-Server/storage/innodb_plugin/fil/fil0fil.c'
515--- Percona-Server/storage/innodb_plugin/fil/fil0fil.c 2012-04-02 02:09:15 +0000
516+++ Percona-Server/storage/innodb_plugin/fil/fil0fil.c 2012-04-25 22:22:24 +0000
517@@ -2658,6 +2658,228 @@
518 return(success);
519 }
520
521+/********************************************************************//**
522+Rotates tablespace like logrotate does: renames tablespace file,
523+new tablespace file is created with the same name and tablespace header
524+as original tablespace
525+@return TRUE if success */
526+UNIV_INTERN
527+ibool
528+fil_rotate_file(
529+/*==========*/
530+ ulint id, /*!< in: space id */
531+ ulint block_offset_in, /*!< in: header offset in a tablespace */
532+ const char* old_file_rename_to, /*!< in: src tablespace file name */
533+ const char* new_file_rename_from, /*!< in: dst tablespace file name */
534+ ulint hdr_size) /*!< in: tablespace header size */
535+
536+{
537+ ibool success;
538+ fil_space_t* space;
539+ fil_node_t* node;
540+ ulint block_offset;
541+ os_file_t file;
542+ ulint size;
543+ ulint size_high;
544+ ulint count = 0;
545+
546+ byte* buf_ptr = NULL;
547+ byte* buf = NULL;
548+
549+ ut_a(id != 0);
550+
551+retry:
552+ block_offset = block_offset_in;
553+ count++;
554+
555+ if (count > 1000) {
556+ ut_print_timestamp(stderr);
557+ fputs(" InnoDB: Warning: problems renaming ", stderr);
558+ ut_print_filename(stderr, old_file_rename_to);
559+ fputs(" or ", stderr);
560+ ut_print_filename(stderr, new_file_rename_from);
561+ fprintf(stderr, ", %lu iterations\n", (ulong) count);
562+ }
563+
564+ mutex_enter(&fil_system->mutex);
565+
566+ space = fil_space_get_by_id(id);
567+
568+ if (space == NULL) {
569+ fprintf(stderr,
570+ "InnoDB: Error: cannot find space id %lu"
571+ " in the tablespace memory cache\n",
572+ (ulong) id);
573+ mutex_exit(&fil_system->mutex);
574+
575+ return(FALSE);
576+ }
577+
578+ if (count > 25000) {
579+error_exit:
580+ space->stop_ios = FALSE;
581+ mutex_exit(&fil_system->mutex);
582+
583+ if (buf_ptr) {
584+ ut_free(buf_ptr);
585+ }
586+
587+ return(FALSE);
588+ }
589+
590+ /* We temporarily close the .ibd file because we do not trust that
591+ operating systems can rename an open file. For the closing we have to
592+ wait until there are no pending i/o's or flushes on the file. */
593+
594+ space->stop_ios = TRUE;
595+
596+ node = UT_LIST_GET_FIRST(space->chain);
597+
598+ for (;;) {
599+ if (UNIV_UNLIKELY(node == NULL)) {
600+ fprintf(stderr,
601+ "InnoDB: Error: The specified block_offset %lu"
602+ " in space %lu,\n"
603+ "InnoDB: space name %s, which is outside the tablespace bounds.\n",
604+ (ulong) block_offset, (ulong) id,
605+ space->name);
606+
607+ goto error_exit;
608+ }
609+
610+ ut_a(node->size != 0);
611+
612+ if (node->size > block_offset) {
613+ /* Found! */
614+ break;
615+ } else {
616+ block_offset -= node->size;
617+ node = UT_LIST_GET_NEXT(chain, node);
618+ }
619+ }
620+
621+ if (node->n_pending > 0 || node->n_pending_flushes > 0) {
622+ /* There are pending i/o's or flushes, sleep for a while and
623+ retry */
624+
625+ mutex_exit(&fil_system->mutex);
626+
627+ os_thread_sleep(20000);
628+
629+ goto retry;
630+
631+ } else if (node->modification_counter > node->flush_counter) {
632+ /* Flush the space */
633+
634+ mutex_exit(&fil_system->mutex);
635+
636+ os_thread_sleep(20000);
637+
638+ fil_flush(id, TRUE);
639+
640+ goto retry;
641+
642+ } else if (node->open) {
643+ /* Close the file */
644+
645+ fil_node_close_file(node, fil_system);
646+ }
647+
648+ /* store old header */
649+ if (hdr_size > 0) {
650+ buf_ptr = ut_malloc(2 * hdr_size);
651+ buf = ut_align(buf_ptr, hdr_size);
652+
653+ file = os_file_create(node->name, OS_FILE_OPEN, OS_FILE_NORMAL,
654+ OS_LOG_FILE, &success);
655+ ut_a(success);
656+
657+ success = os_file_read(file, buf, 0, 0, hdr_size);
658+ ut_a(success);
659+
660+ os_file_close(file);
661+ }
662+
663+ /* check size of the new file */
664+ file = os_file_create(new_file_rename_from, OS_FILE_OPEN, OS_FILE_NORMAL,
665+ OS_LOG_FILE, &success);
666+ if (!success) {
667+ fprintf(stderr,
668+ "InnoDB: Error in opening %s\n", new_file_rename_from);
669+ goto error_exit;
670+ }
671+
672+ success = os_file_get_size(file, &size, &size_high);
673+ ut_a(success);
674+
675+ if (size != (0xFFFFFFFFUL & (node->size << UNIV_PAGE_SIZE_SHIFT))
676+ || size_high != (node->size >> (32 - UNIV_PAGE_SIZE_SHIFT))) {
677+ fprintf(stderr,
678+ "InnoDB: Error: file %s is"
679+ " of different size %lu %lu bytes\n"
680+ "InnoDB: than intended %lu %lu bytes!\n",
681+ new_file_rename_from, (ulong) size_high, (ulong) size,
682+ (ulong) (node->size >> (32 - UNIV_PAGE_SIZE_SHIFT)),
683+ (ulong) (0xFFFFFFFFUL & (node->size << UNIV_PAGE_SIZE_SHIFT)));
684+ os_file_close(file);
685+ goto error_exit;
686+ }
687+
688+ /* copy stored header */
689+ if (hdr_size > 0) {
690+ success = os_file_write(new_file_rename_from, file, buf, 0, 0, hdr_size);
691+ if (!success) {
692+ fprintf(stderr,
693+ "InnoDB: Error: failed to initialize"
694+ " header of %s\n",
695+ new_file_rename_from);
696+ os_file_close(file);
697+ goto error_exit;
698+ }
699+ success = os_file_flush(file, TRUE);
700+ if (!success) {
701+ fprintf(stderr,
702+ "InnoDB: Error: failed to initialize"
703+ " header of %s\n",
704+ new_file_rename_from);
705+ os_file_close(file);
706+ goto error_exit;
707+ }
708+ }
709+
710+ success = os_file_close(file);
711+ ut_a(success);
712+
713+ /* Rename the tablespace and the node in the memory cache */
714+ success = os_file_rename(node->name, old_file_rename_to);
715+
716+ if (success) {
717+ success = os_file_rename(new_file_rename_from, node->name);
718+
719+ if (!success) {
720+ ut_a(os_file_rename(old_file_rename_to, node->name));
721+ }
722+ }
723+
724+ if (success) {
725+ fprintf(stderr,
726+ "InnoDB: file %s was renamed to %s\n"
727+ "InnoDB: file %s was renamed to %s\n",
728+ node->name, old_file_rename_to,
729+ new_file_rename_from, node->name);
730+ }
731+
732+ space->stop_ios = FALSE;
733+
734+ mutex_exit(&fil_system->mutex);
735+
736+ if (buf_ptr) {
737+ ut_free(buf_ptr);
738+ }
739+
740+ return(success);
741+}
742+
743 /*******************************************************************//**
744 Creates a new single-table tablespace to a database directory of MySQL.
745 Database directories are under the 'datadir' of MySQL. The datadir is the
746
747=== modified file 'Percona-Server/storage/innodb_plugin/handler/ha_innodb.cc'
748--- Percona-Server/storage/innodb_plugin/handler/ha_innodb.cc 2012-04-02 02:09:15 +0000
749+++ Percona-Server/storage/innodb_plugin/handler/ha_innodb.cc 2012-04-25 22:22:24 +0000
750@@ -186,6 +186,10 @@
751 static my_bool innobase_log_archive = FALSE;
752 static char* innobase_log_arch_dir = NULL;
753 #endif /* UNIV_LOG_ARCHIVE */
754+static my_bool innobase_xtra_log_archive = FALSE;
755+static char* innobase_xtra_log_archive_dir = NULL;
756+static ulong innodb_xtra_expire_archive_logs_sec = 0;
757+static uint innodb_xtra_archive_logs_throttle = 0;
758 static my_bool innobase_use_doublewrite = TRUE;
759 static my_bool innobase_use_checksums = TRUE;
760 static my_bool innobase_fast_checksum = FALSE;
761@@ -316,6 +320,31 @@
762 static const char innobase_hton_name[]= "InnoDB";
763
764 /*************************************************************//**
765+Removes old archived transaction log files.
766+@return non-zero if error */
767+static int innobase_purge_archive_logs(
768+ handlerton *hton, /*!< in: InnoDB handlerton */
769+ time_t before_date, /*!< in: all files modified
770+ before timestamp should be removed */
771+ const char* to_filename) /*!< in: this and earler files
772+ should be removed */
773+{
774+ if (before_date > 0) {
775+ purge_archived_logs(before_date, 0);
776+ } else if (to_filename) {
777+ if (strstr(to_filename, IB_ARCHIVED_LOGS_PREFIX) == to_filename) {
778+ unsigned long long log_file_lsn = strtoll(to_filename
779+ + strlen(IB_ARCHIVED_LOGS_PREFIX),
780+ NULL, 10);
781+ if (log_file_lsn > 0) {
782+ purge_archived_logs(0, log_file_lsn);
783+ }
784+ }
785+ }
786+ return 0;
787+}
788+
789+/*************************************************************//**
790 Check for a valid value of innobase_commit_concurrency.
791 @return 0 for valid innodb_commit_concurrency */
792 static
793@@ -2134,6 +2163,7 @@
794 innobase_hton->flags=HTON_NO_FLAGS;
795 innobase_hton->release_temporary_latches=innobase_release_temporary_latches;
796 innobase_hton->alter_table_flags = innobase_alter_table_flags;
797+ innobase_hton->purge_archive_logs = innobase_purge_archive_logs;
798
799 ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
800
801@@ -2503,6 +2533,15 @@
802 #ifdef UNIV_LOG_ARCHIVE
803 srv_log_archive_on = (ulint) innobase_log_archive;
804 #endif /* UNIV_LOG_ARCHIVE */
805+ log_xtra_log_archive_on = (ulint) innobase_xtra_log_archive;
806+ log_xtra_log_arch_expire_sec = (ulint) innodb_xtra_expire_archive_logs_sec;
807+ log_xtra_log_arch_throttle = (ulint) innodb_xtra_archive_logs_throttle;
808+
809+ if (innobase_xtra_log_archive_dir && strlen(innobase_xtra_log_archive_dir) > 0) {
810+ log_xtra_log_archive_dir = innobase_xtra_log_archive_dir;
811+ } else {
812+ log_xtra_log_archive_dir = innobase_log_group_home_dir;
813+ }
814 srv_log_buffer_size = (ulint) innobase_log_buffer_size;
815
816 srv_buf_pool_size = (ulint) innobase_buffer_pool_size;
817@@ -11320,6 +11359,75 @@
818 }
819
820 /****************************************************************//**
821+Update the system variable innobase_log_archive using the "saved"
822+value. This function is registered as a callback with MySQL. */
823+static
824+void
825+innobase_log_archive_update(
826+/*==============================*/
827+ THD* thd, /*!< in: thread handle */
828+ struct st_mysql_sys_var* var, /*!< in: pointer to
829+ system variable */
830+ void* var_ptr, /*!< out: unused */
831+ const void* save) /*!< in: immediate result
832+ from check function */
833+{
834+ mutex_enter(&(log_sys->arch_sys_mutex));
835+ if (*(my_bool*) save) {
836+ if (log_xtra_log_archive_on == FALSE) {
837+ log_sys->archived_lsn=0;
838+ innobase_xtra_log_archive=TRUE;
839+ log_xtra_log_archive_on=TRUE;
840+ os_thread_create(&srv_log_archive_thread, NULL, NULL);
841+ }
842+ } else {
843+ if (log_xtra_log_archive_on == TRUE) {
844+ innobase_xtra_log_archive=FALSE;
845+ log_xtra_log_archive_on=FALSE;
846+ os_event_set(log_sys->arch_event_rotate_start);
847+ }
848+ }
849+ mutex_exit(&(log_sys->arch_sys_mutex));
850+}
851+
852+/****************************************************************//**
853+Update the system variable innodb_xtra_expire_archive_logs_sec using
854+the "saved" value. This function is registered as a callback with MySQL. */
855+static
856+void
857+innobase_log_archive_expire_update(
858+/*==============================*/
859+ THD* thd, /*!< in: thread handle */
860+ struct st_mysql_sys_var* var, /*!< in: pointer to
861+ system variable */
862+ void* var_ptr, /*!< out: unused */
863+ const void* save) /*!< in: immediate result
864+ from check function */
865+{
866+ mutex_enter(&(log_sys->arch_sys_mutex));
867+ log_xtra_log_arch_expire_sec = innodb_xtra_expire_archive_logs_sec = *(ulint*) save;
868+ mutex_exit(&(log_sys->arch_sys_mutex));
869+}
870+/****************************************************************//**
871+Update the system variable innodb_xtra_expire_archive_logs_sec using
872+the "saved" value. This function is registered as a callback with MySQL. */
873+static
874+void
875+innobase_log_archive_throttle_update(
876+/*==============================*/
877+ THD* thd, /*!< in: thread handle */
878+ struct st_mysql_sys_var* var, /*!< in: pointer to
879+ system variable */
880+ void* var_ptr, /*!< out: unused */
881+ const void* save) /*!< in: immediate result
882+ from check function */
883+{
884+ mutex_enter(&(log_sys->arch_sys_mutex));
885+ log_xtra_log_arch_throttle = innodb_xtra_archive_logs_throttle = *static_cast<const uint*>(save);
886+ mutex_exit(&(log_sys->arch_sys_mutex));
887+}
888+
889+/****************************************************************//**
890 Update the system variable innodb_old_blocks_pct using the "saved"
891 value. This function is registered as a callback with MySQL. */
892 static
893@@ -11680,6 +11788,24 @@
894 "Set to 1 if you want to have logs archived.", NULL, NULL, FALSE);
895 #endif /* UNIV_LOG_ARCHIVE */
896
897+static MYSQL_SYSVAR_BOOL(xtra_log_archive, innobase_xtra_log_archive,
898+ PLUGIN_VAR_OPCMDARG,
899+ "Enables log archiving.", NULL, innobase_log_archive_update, FALSE);
900+
901+static MYSQL_SYSVAR_STR(xtra_log_archive_dir, innobase_xtra_log_archive_dir,
902+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
903+ "Path to archived logs directory.", NULL, NULL, NULL);
904+
905+static MYSQL_SYSVAR_ULONG(xtra_expire_archive_logs_sec, innodb_xtra_expire_archive_logs_sec,
906+ PLUGIN_VAR_OPCMDARG,
907+ "Expiration time for archived innodb transaction logs.",
908+ NULL, innobase_log_archive_expire_update, 0, 0, ~0L, 0);
909+
910+static MYSQL_SYSVAR_UINT(xtra_log_archive_throttle, innodb_xtra_archive_logs_throttle,
911+ PLUGIN_VAR_OPCMDARG,
912+ "number of read+write pairs of operations per second for archived innodb transaction logs cross device copy operation.",
913+ NULL, innobase_log_archive_throttle_update, 0, 0, ~0L, 0);
914+
915 static MYSQL_SYSVAR_STR(log_group_home_dir, innobase_log_group_home_dir,
916 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
917 "Path to InnoDB log files.", NULL, NULL, NULL);
918@@ -12121,6 +12247,10 @@
919 MYSQL_SYSVAR(log_arch_dir),
920 MYSQL_SYSVAR(log_archive),
921 #endif /* UNIV_LOG_ARCHIVE */
922+ MYSQL_SYSVAR(xtra_log_archive),
923+ MYSQL_SYSVAR(xtra_log_archive_dir),
924+ MYSQL_SYSVAR(xtra_expire_archive_logs_sec),
925+ MYSQL_SYSVAR(xtra_log_archive_throttle),
926 MYSQL_SYSVAR(log_buffer_size),
927 MYSQL_SYSVAR(log_file_size),
928 MYSQL_SYSVAR(log_files_in_group),
929
930=== modified file 'Percona-Server/storage/innodb_plugin/include/fil0fil.h'
931--- Percona-Server/storage/innodb_plugin/include/fil0fil.h 2011-12-09 16:53:35 +0000
932+++ Percona-Server/storage/innodb_plugin/include/fil0fil.h 2012-04-25 22:22:24 +0000
933@@ -426,7 +426,20 @@
934 const char* new_name); /*!< in: new table name in the standard
935 databasename/tablename format
936 of InnoDB */
937-
938+/********************************************************************//**
939+Rotates tablespace like logrotate does: renames tablespace file,
940+new tablespace file is created with the same name and tablespace header
941+as original tablespace
942+@return TRUE if success */
943+UNIV_INTERN
944+ibool
945+fil_rotate_file(
946+/*==========*/
947+ ulint id, /*!< in: space id */
948+ ulint block_offset_in, /*!< in: header offset in a tablespace */
949+ const char* old_file_rename_to, /*!< in: src tablespace file name */
950+ const char* new_file_rename_from, /*!< in: dst tablespace file name */
951+ ulint hdr_size); /*!< in: tablespace header size */
952 /*******************************************************************//**
953 Creates a new single-table tablespace to a database directory of MySQL.
954 Database directories are under the 'datadir' of MySQL. The datadir is the
955
956=== modified file 'Percona-Server/storage/innodb_plugin/include/log0log.h'
957--- Percona-Server/storage/innodb_plugin/include/log0log.h 2012-04-02 02:09:15 +0000
958+++ Percona-Server/storage/innodb_plugin/include/log0log.h 2012-04-25 22:22:24 +0000
959@@ -56,6 +56,10 @@
960 # define log_do_write TRUE
961 #endif /* UNIV_DEBUG */
962
963+extern ibool log_xtra_log_archive_on;
964+extern const char* log_xtra_log_archive_dir;
965+extern time_t log_xtra_log_arch_expire_sec;
966+extern uint log_xtra_log_arch_throttle;
967 /** Wait modes for log_write_up_to @{ */
968 #define LOG_NO_WAIT 91
969 #define LOG_WAIT_ONE_GROUP 92
970@@ -75,6 +79,26 @@
971 /*==========================================*/
972 ulint limit); /*!< in: limit to set */
973 #endif /* !UNIV_HOTBACKUP */
974+/******************************************************//**
975+Calculates the offset of an lsn within a log group.
976+@return offset within the log group */
977+UNIV_INTERN
978+ulint
979+log_group_calc_lsn_offset(
980+/*======================*/
981+ ib_uint64_t lsn, /*!< in: lsn, must be within 4 GB of
982+ group->lsn */
983+ const log_group_t* group); /*!< in: log group */
984+
985+/*****************************************************************//**
986+Calculates the recommended highest values for lsn - last_checkpoint_lsn,
987+lsn - buf_get_oldest_modification(), and lsn - max_archive_lsn_age.
988+@return error value FALSE if the smallest log group is too small to
989+accommodate the number of OS threads in the database server */
990+ibool
991+log_calc_max_ages(void);
992+/*===================*/
993+
994 /*******************************************************************//**
995 Calculates where in log files we find a specified lsn.
996 @return log file number */
997@@ -953,6 +977,10 @@
998 become signaled */
999 /* @} */
1000 #endif /* UNIV_LOG_ARCHIVE */
1001+ os_event_t arch_event_rotate_start; /*!< initiates transaction log archiving */
1002+ os_event_t arch_event_rotate_done; /*!< logging thread can continue normal work after receiving this event */
1003+ mutex_t arch_sys_mutex; /*!< protects LSN and archiving thread manipulations */
1004+ ib_uint64_t archived_lsn; /*!< LSN of last archived transaction log file */
1005 };
1006
1007 #ifdef UNIV_LOG_ARCHIVE
1008
1009=== modified file 'Percona-Server/storage/innodb_plugin/include/log0log.ic'
1010--- Percona-Server/storage/innodb_plugin/include/log0log.ic 2010-06-10 14:31:28 +0000
1011+++ Percona-Server/storage/innodb_plugin/include/log0log.ic 2012-04-25 22:22:24 +0000
1012@@ -372,7 +372,11 @@
1013
1014 ut_ad(log_sys->buf_free <= log_sys->buf_size);
1015
1016+ if (log_xtra_log_archive_on)
1017+ mutex_enter(&(log_sys->arch_sys_mutex));
1018 log_sys->lsn += len;
1019+ if (log_xtra_log_archive_on)
1020+ mutex_exit(&(log_sys->arch_sys_mutex));
1021
1022 #ifdef UNIV_LOG_DEBUG
1023 log_check_log_recs(log_sys->buf + log_sys->old_buf_free,
1024
1025=== modified file 'Percona-Server/storage/innodb_plugin/include/os0file.h'
1026--- Percona-Server/storage/innodb_plugin/include/os0file.h 2011-11-24 02:00:47 +0000
1027+++ Percona-Server/storage/innodb_plugin/include/os0file.h 2012-04-25 22:22:24 +0000
1028@@ -403,6 +403,20 @@
1029 const char* oldpath, /*!< in: old file path as a
1030 null-terminated string */
1031 const char* newpath); /*!< in: new file path */
1032+/***************************************************************************
1033+Renames a file (can also move it to another directory and another drive).
1034+If file should be placed to a different disks throttle will applied.
1035+@return TRUE if success */
1036+ibool
1037+os_file_rename_throttled(
1038+/*===========*/
1039+ const char* oldpath, /* in: old file path as a
1040+ null-terminated
1041+ string */
1042+ const char* newpath, /* in: new file path */
1043+ unsigned int throttle); /* in: number of read+write pairs of
1044+ operations per second. */
1045+
1046 /***********************************************************************//**
1047 Closes a file handle. In case of error, error number can be retrieved with
1048 os_file_get_last_error.
1049
1050=== modified file 'Percona-Server/storage/innodb_plugin/include/srv0srv.h'
1051--- Percona-Server/storage/innodb_plugin/include/srv0srv.h 2011-11-24 02:01:25 +0000
1052+++ Percona-Server/storage/innodb_plugin/include/srv0srv.h 2012-04-25 22:22:24 +0000
1053@@ -185,6 +185,8 @@
1054 extern dulint srv_archive_recovery_limit_lsn;
1055 #endif /* UNIV_LOG_ARCHIVE */
1056
1057+#define IB_ARCHIVED_LOGS_PREFIX "ib_log_archive_"
1058+
1059 extern char* srv_file_flush_method_str;
1060 extern ulint srv_unix_file_flush_method;
1061 extern ulint srv_win_file_flush_method;
1062@@ -445,6 +447,7 @@
1063 #endif
1064 SRV_PURGE, /* thread purging undo records */
1065 SRV_PURGE_WORKER, /* thread purging undo records */
1066+ SRV_ARCHIVE,
1067 SRV_MASTER /**< the master thread, (whose type number must
1068 be biggest) */
1069 };
1070@@ -643,6 +646,13 @@
1071 /*====================*/
1072 void* arg); /*!< in: a dummy parameter required by
1073 os_thread_create */
1074+/********************************************************************//**
1075+A thread which controls archiving transaction log files */
1076+UNIV_INTERN
1077+os_thread_ret_t
1078+srv_log_archive_thread(
1079+/*===================*/
1080+ void* arg);
1081 /******************************************************************//**
1082 Outputs to a file the output of the InnoDB Monitor.
1083 @return FALSE if not all information printed
1084@@ -665,6 +675,18 @@
1085 srv_export_innodb_status(void);
1086 /*==========================*/
1087
1088+/*************************************************************//**
1089+Removes old archived transaction log files.
1090+Both parameters couldn't be provided at the same time */
1091+UNIV_INTERN
1092+void
1093+purge_archived_logs(
1094+ time_t before_date, /*!< in: all files modified
1095+ before timestamp should be removed */
1096+ unsigned long long before_lsn); /*!< in: files with this lsn in name
1097+ and earler should be removed */
1098+/*==========================*/
1099+
1100 /** Thread slot in the thread table */
1101 typedef struct srv_slot_struct srv_slot_t;
1102
1103
1104=== modified file 'Percona-Server/storage/innodb_plugin/include/srv0start.h'
1105--- Percona-Server/storage/innodb_plugin/include/srv0start.h 2011-11-24 02:00:36 +0000
1106+++ Percona-Server/storage/innodb_plugin/include/srv0start.h 2012-04-25 22:22:24 +0000
1107@@ -37,6 +37,22 @@
1108 /*=======================*/
1109 char* str); /*!< in/out: null-terminated character string */
1110 /*********************************************************************//**
1111+Calculates the low 32 bits when a file size which is given as a number
1112+database pages is converted to the number of bytes.
1113+@return low 32 bytes of file size when expressed in bytes */
1114+ulint
1115+srv_calc_low32(
1116+/*===========*/
1117+ ulint file_size); /*!< in: file size in database pages */
1118+/*********************************************************************//**
1119+Calculates the high 32 bits when a file size which is given as a number
1120+database pages is converted to the number of bytes.
1121+@return high 32 bytes of file size when expressed in bytes */
1122+ulint
1123+srv_calc_high32(
1124+/*============*/
1125+ ulint file_size); /*!< in: file size in database pages */
1126+/*********************************************************************//**
1127 Reads the data files and their sizes from a character string given in
1128 the .cnf file.
1129 @return TRUE if ok, FALSE on parse error */
1130@@ -134,4 +150,10 @@
1131 /** reserved for extra system tables */
1132 #define SRV_EXTRA_SYS_SPACE_FIRST_ID 0xFFFFFFE0UL
1133
1134+#ifdef __WIN__
1135+#define SRV_PATH_SEPARATOR '\\'
1136+#else
1137+#define SRV_PATH_SEPARATOR '/'
1138+#endif
1139+
1140 #endif
1141
1142=== modified file 'Percona-Server/storage/innodb_plugin/log/log0log.c'
1143--- Percona-Server/storage/innodb_plugin/log/log0log.c 2012-04-02 02:09:15 +0000
1144+++ Percona-Server/storage/innodb_plugin/log/log0log.c 2012-04-25 22:22:24 +0000
1145@@ -97,6 +97,11 @@
1146 UNIV_INTERN byte log_archive_io;
1147 #endif /* UNIV_LOG_ARCHIVE */
1148
1149+UNIV_INTERN ibool log_xtra_log_archive_on = FALSE;
1150+
1151+UNIV_INTERN const char* log_xtra_log_archive_dir = NULL;
1152+UNIV_INTERN time_t log_xtra_log_arch_expire_sec;
1153+UNIV_INTERN uint log_xtra_log_arch_throttle = 0;
1154 /* A margin for free space in the log buffer before a log entry is catenated */
1155 #define LOG_BUF_WRITE_MARGIN (4 * OS_FILE_LOG_BLOCK_SIZE)
1156
1157@@ -287,10 +292,13 @@
1158 ulint str_len) /*!< in: string length */
1159 {
1160 log_t* log = log_sys;
1161+ log_group_t* group;
1162 ulint len;
1163 ulint data_len;
1164 byte* log_block;
1165
1166+ group = UT_LIST_GET_FIRST(log->log_groups);
1167+
1168 ut_ad(mutex_own(&(log->mutex)));
1169 part_loop:
1170 ut_ad(!recv_no_log_write);
1171@@ -311,6 +319,30 @@
1172 - LOG_BLOCK_TRL_SIZE;
1173 }
1174
1175+ /* If the log write is across file border,
1176+ should check the archive was done. */
1177+ if (log_xtra_log_archive_on) {
1178+ if ((log_group_calc_lsn_offset(log->lsn, group)
1179+ / group->file_size)
1180+ != (log_group_calc_lsn_offset(log->lsn + len, group)
1181+ / group->file_size)) {
1182+ os_event_set(log_sys->arch_event_rotate_start);
1183+ }
1184+wait_for_archived:
1185+ mutex_enter(&(log_sys->arch_sys_mutex));
1186+ if ( !(log_sys->archived_lsn) ||
1187+ log->lsn + len <= log_sys->archived_lsn
1188+ + log_group_get_capacity(group)) {
1189+ mutex_exit(&(log_sys->arch_sys_mutex));
1190+ } else {
1191+ os_event_reset(log_sys->arch_event_rotate_done);
1192+ mutex_exit(&(log_sys->arch_sys_mutex));
1193+ os_event_set(log_sys->arch_event_rotate_start); /* for safety */
1194+ os_event_wait(log_sys->arch_event_rotate_done);
1195+ goto wait_for_archived;
1196+ }
1197+ }
1198+
1199 ut_memcpy(log->buf + log->buf_free, str, len);
1200
1201 str_len -= len;
1202@@ -327,12 +359,20 @@
1203 log_sys->next_checkpoint_no);
1204 len += LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE;
1205
1206+ if (log_xtra_log_archive_on)
1207+ mutex_enter(&(log_sys->arch_sys_mutex));
1208 log->lsn += len;
1209+ if (log_xtra_log_archive_on)
1210+ mutex_exit(&(log_sys->arch_sys_mutex));
1211
1212 /* Initialize the next block header */
1213 log_block_init(log_block + OS_FILE_LOG_BLOCK_SIZE, log->lsn);
1214 } else {
1215+ if (log_xtra_log_archive_on)
1216+ mutex_enter(&(log_sys->arch_sys_mutex));
1217 log->lsn += len;
1218+ if (log_xtra_log_archive_on)
1219+ mutex_exit(&(log_sys->arch_sys_mutex));
1220 }
1221
1222 log->buf_free += len;
1223@@ -552,7 +592,7 @@
1224 /******************************************************//**
1225 Calculates the offset of an lsn within a log group.
1226 @return offset within the log group */
1227-static
1228+UNIV_INTERN
1229 ulint
1230 log_group_calc_lsn_offset(
1231 /*======================*/
1232@@ -670,7 +710,6 @@
1233 lsn - buf_get_oldest_modification(), and lsn - max_archive_lsn_age.
1234 @return error value FALSE if the smallest log group is too small to
1235 accommodate the number of OS threads in the database server */
1236-static
1237 ibool
1238 log_calc_max_ages(void)
1239 /*===================*/
1240@@ -710,6 +749,11 @@
1241 group = UT_LIST_GET_NEXT(log_groups, group);
1242 }
1243
1244+ /* log_archive needs over 1 file margin */
1245+ if (log_xtra_log_archive_on) {
1246+ smallest_capacity = smallest_archive_margin;
1247+ }
1248+
1249 /* Add extra safety */
1250 smallest_capacity = smallest_capacity - smallest_capacity / 10;
1251
1252@@ -786,6 +830,7 @@
1253 log_sys = mem_alloc(sizeof(log_t));
1254
1255 mutex_create(&log_sys->mutex, SYNC_LOG);
1256+ mutex_create(&log_sys->arch_sys_mutex, SYNC_NO_ORDER_CHECK);
1257
1258 mutex_enter(&(log_sys->mutex));
1259
1260@@ -852,7 +897,7 @@
1261 #ifdef UNIV_LOG_ARCHIVE
1262 /* Under MySQL, log archiving is always off */
1263 log_sys->archiving_state = LOG_ARCH_OFF;
1264- log_sys->archived_lsn = log_sys->lsn;
1265+ log_sys->archived_lsn = 0;
1266 log_sys->next_archived_lsn = 0;
1267
1268 log_sys->n_pending_archive_ios = 0;
1269@@ -872,6 +917,11 @@
1270 log_sys->archiving_on = os_event_create(NULL);
1271 #endif /* UNIV_LOG_ARCHIVE */
1272
1273+ log_sys->arch_event_rotate_start = os_event_create(NULL);
1274+ os_event_set(log_sys->arch_event_rotate_start);
1275+ log_sys->arch_event_rotate_done = os_event_create(NULL);
1276+ os_event_set(log_sys->arch_event_rotate_done);
1277+
1278 /*----------------------------*/
1279
1280 log_block_init(log_sys->buf, log_sys->lsn);
1281@@ -1693,13 +1743,29 @@
1282 log_complete_checkpoint(void)
1283 /*=========================*/
1284 {
1285+ log_group_t* group;
1286+
1287 ut_ad(mutex_own(&(log_sys->mutex)));
1288 ut_ad(log_sys->n_pending_checkpoint_writes == 0);
1289
1290+ group = UT_LIST_GET_FIRST(log_sys->log_groups);
1291+
1292+ if (log_xtra_log_archive_on) {
1293+ mutex_enter(&(log_sys->arch_sys_mutex));
1294+
1295+ if ((log_group_calc_lsn_offset(log_sys->next_checkpoint_lsn, group) / group->file_size)
1296+ != (log_group_calc_lsn_offset(log_sys->last_checkpoint_lsn, group) / group->file_size)) {
1297+ os_event_set(log_sys->arch_event_rotate_start);
1298+ }
1299+ }
1300+
1301 log_sys->next_checkpoint_no++;
1302
1303 log_sys->last_checkpoint_lsn = log_sys->next_checkpoint_lsn;
1304
1305+ if (log_xtra_log_archive_on)
1306+ mutex_exit(&(log_sys->arch_sys_mutex));
1307+
1308 rw_lock_x_unlock_gen(&(log_sys->checkpoint_lock), LOG_CHECKPOINT);
1309 }
1310
1311@@ -3158,6 +3224,15 @@
1312 goto loop;
1313 }
1314
1315+ if (log_xtra_log_archive_on
1316+ && srv_n_threads_active[SRV_ARCHIVE] != 0) {
1317+
1318+ mutex_exit(&kernel_mutex);
1319+ os_event_set(log_sys->arch_event_rotate_start);
1320+
1321+ goto loop;
1322+ }
1323+
1324 /* Check that the purge threads ended */
1325 if (srv_use_purge_thread
1326 && (srv_n_threads_active[SRV_PURGE] != 0
1327@@ -3375,6 +3450,14 @@
1328 log_sys->flushed_to_disk_lsn,
1329 log_sys->last_checkpoint_lsn);
1330
1331+ if (log_xtra_log_archive_on) {
1332+ mutex_enter(&(log_sys->arch_sys_mutex));
1333+ fprintf(file,
1334+ "Log archived until %llu\n",
1335+ log_sys->archived_lsn);
1336+ mutex_exit(&(log_sys->arch_sys_mutex));
1337+ }
1338+
1339 fprintf(file,
1340 "Max checkpoint age %lu\n"
1341 "Checkpoint age target %lu\n"
1342@@ -3479,12 +3562,16 @@
1343 rw_lock_free(&log_sys->checkpoint_lock);
1344
1345 mutex_free(&log_sys->mutex);
1346+ mutex_free(&log_sys->arch_sys_mutex);
1347
1348 #ifdef UNIV_LOG_ARCHIVE
1349 rw_lock_free(&log_sys->archive_lock);
1350 os_event_create(log_sys->archiving_on);
1351 #endif /* UNIV_LOG_ARCHIVE */
1352
1353+ os_event_free(log_sys->arch_event_rotate_start);
1354+ os_event_free(log_sys->arch_event_rotate_done);
1355+
1356 #ifdef UNIV_LOG_DEBUG
1357 recv_sys_debug_free();
1358 #endif
1359
1360=== modified file 'Percona-Server/storage/innodb_plugin/os/os0file.c'
1361--- Percona-Server/storage/innodb_plugin/os/os0file.c 2011-12-15 15:27:38 +0000
1362+++ Percona-Server/storage/innodb_plugin/os/os0file.c 2012-04-25 22:22:24 +0000
1363@@ -1685,6 +1685,158 @@
1364 return(TRUE);
1365 #endif
1366 }
1367+/***************************************************************************
1368+Renames a file (can also move it to another directory and another drive).
1369+If file should be placed to a different disks throttle will applied.
1370+@return TRUE if success */
1371+ibool
1372+os_file_rename_throttled(
1373+/*===========*/
1374+ const char* oldpath, /* in: old file path as a
1375+ null-terminated string */
1376+ const char* newpath, /* in: new file path */
1377+ unsigned int throttle) /* in: number of read+write pairs of
1378+ operations per second. */
1379+{
1380+#ifdef __WIN__
1381+ return os_file_rename(oldpath, newpath);
1382+#else
1383+ int ret;
1384+ ulint err;
1385+ ulint block_size = 64*UNIV_PAGE_SIZE;
1386+ os_file_t file;
1387+ os_file_t new_file;
1388+ byte* buf_ptr = NULL;
1389+ byte* buf = NULL;
1390+ ibool success;
1391+ ib_int64_t file_size;
1392+ ib_int64_t offset;
1393+ ulint prev_time = 0 , cur_time = 0;
1394+ int num_io = 0;
1395+
1396+ ret = rename(oldpath, newpath);
1397+
1398+ if (ret == 0) {
1399+ return(TRUE);
1400+ }
1401+
1402+ err = (ulint) errno;
1403+
1404+ if (err != EXDEV) {
1405+ os_file_handle_error_no_exit(oldpath, "rename");
1406+ return(FALSE);
1407+ }
1408+
1409+ buf_ptr = ut_malloc(2*block_size);
1410+ buf = ut_align(buf_ptr, block_size);
1411+
1412+ file = os_file_create(oldpath, OS_FILE_OPEN, OS_FILE_NORMAL,
1413+ OS_LOG_FILE, &success);
1414+ ut_a(success);
1415+ new_file = os_file_create(newpath, OS_FILE_CREATE, OS_FILE_NORMAL,
1416+ OS_LOG_FILE, &success);
1417+ ut_a(success);
1418+
1419+#ifndef USE_POSIX_FADVISE
1420+#ifdef POSIX_FADV_NORMAL
1421+#define USE_POSIX_FADVISE
1422+#endif
1423+#endif
1424+
1425+#ifdef USE_POSIX_FADVISE
1426+ posix_fadvise(new_file, 0, 0, POSIX_FADV_SEQUENTIAL);
1427+ posix_fadvise(file, 0, 0, POSIX_FADV_SEQUENTIAL);
1428+#endif
1429+ file_size = os_file_get_size_as_iblonglong(file);
1430+ ut_a(file_size != -1);
1431+
1432+ if (throttle) {
1433+ prev_time = ut_time_ms();
1434+ }
1435+
1436+ for (offset = 0; offset < file_size; offset += block_size) {
1437+ success = os_file_read(file, buf,
1438+ (ulint)(offset & 0xFFFFFFFFUL),
1439+ (ulint)(offset >> 32),
1440+ block_size);
1441+ ut_a(success);
1442+ success = os_file_write(newpath, new_file, buf,
1443+ (ulint)(offset & 0xFFFFFFFFUL),
1444+ (ulint)(offset >> 32),
1445+ block_size);
1446+ ut_a(success);
1447+
1448+ if (!throttle) {
1449+ continue;
1450+ }
1451+
1452+ ++num_io;
1453+ cur_time = ut_time_ms();
1454+
1455+ if ((cur_time - prev_time) > 0 &&
1456+ num_io*1000/(cur_time - prev_time) <= throttle) {
1457+ continue;
1458+ }
1459+
1460+ os_thread_sleep(1000000/throttle
1461+ - 1000*(cur_time - prev_time)/num_io);
1462+
1463+ prev_time = cur_time;
1464+ num_io = 0;
1465+ }
1466+
1467+ if (file_size - offset > 0) {
1468+ success = os_file_read(file, buf,
1469+ (ulint)(offset & 0xFFFFFFFFUL),
1470+ (ulint)(offset >> 32),
1471+ file_size - offset);
1472+ ut_a(success);
1473+ success = os_file_write(newpath, new_file, buf,
1474+ (ulint)(offset & 0xFFFFFFFFUL),
1475+ (ulint)(offset >> 32),
1476+ file_size - offset);
1477+ ut_a(success);
1478+ }
1479+
1480+ success = os_file_flush(new_file, TRUE);
1481+ ut_a(success);
1482+
1483+#ifdef UNIV_DEBUG
1484+ {
1485+ ib_int64_t new_file_size;
1486+
1487+ new_file_size = os_file_get_size_as_iblonglong(new_file);
1488+ ut_a(new_file_size != -1);
1489+
1490+ if (new_file_size != file_size) {
1491+ fputs( " InnoDB: Warning: problems moving "
1492+ "to different disk: wrong file size", stderr);
1493+ ut_print_filename(stderr, newpath);
1494+ fputs("\n",stderr);
1495+ }
1496+ }
1497+#endif
1498+
1499+#ifdef USE_POSIX_FADVISE
1500+ posix_fadvise(new_file, 0, 0, POSIX_FADV_DONTNEED);
1501+ posix_fadvise(file, 0, 0, POSIX_FADV_DONTNEED);
1502+#endif
1503+
1504+
1505+ os_file_close(file);
1506+ os_file_close(new_file);
1507+ success = os_file_delete_if_exists(oldpath);
1508+ ut_a(success);
1509+
1510+ if (buf_ptr) {
1511+ ut_free(buf_ptr);
1512+ }
1513+
1514+ return(TRUE);
1515+#endif
1516+}
1517+
1518+
1519
1520 /***********************************************************************//**
1521 Closes a file handle. In case of error, error number can be retrieved with
1522
1523=== modified file 'Percona-Server/storage/innodb_plugin/srv/srv0srv.c'
1524--- Percona-Server/storage/innodb_plugin/srv/srv0srv.c 2011-11-24 02:01:25 +0000
1525+++ Percona-Server/storage/innodb_plugin/srv/srv0srv.c 2012-04-25 22:22:24 +0000
1526@@ -2674,6 +2674,372 @@
1527 OS_THREAD_DUMMY_RETURN;
1528 }
1529
1530+/*********************************************************************//**
1531+Creates or opens the next log file. The file will be closed after the call */
1532+static
1533+ulint
1534+open_or_create_new_log_file(
1535+/*========================*/
1536+ const char* name)
1537+{
1538+ ibool ret;
1539+ ulint size;
1540+ ulint size_high;
1541+ os_file_t new_file;
1542+
1543+ new_file = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL,
1544+ OS_LOG_FILE, &ret);
1545+ if (ret == FALSE) {
1546+ if (os_file_get_last_error(FALSE) != OS_FILE_ALREADY_EXISTS) {
1547+ fprintf(stderr,
1548+ "InnoDB: Error in creating"
1549+ " or opening %s\n", name);
1550+
1551+ return(DB_ERROR);
1552+ }
1553+
1554+ new_file = os_file_create(name, OS_FILE_OPEN, OS_FILE_AIO,
1555+ OS_LOG_FILE, &ret);
1556+ if (!ret) {
1557+ fprintf(stderr,
1558+ "InnoDB: Error in opening %s\n", name);
1559+ return(DB_ERROR);
1560+ }
1561+
1562+ ret = os_file_get_size(new_file, &size, &size_high);
1563+ ut_a(ret);
1564+
1565+ if (size != srv_calc_low32(srv_log_file_size)
1566+ || size_high != srv_calc_high32(srv_log_file_size)) {
1567+
1568+ fprintf(stderr,
1569+ "InnoDB: Error: log file %s is"
1570+ " of different size %lu %lu bytes\n"
1571+ "InnoDB: than specified in the .cnf"
1572+ " file %lu %lu bytes! Rename or remove the file.\n",
1573+ name, (ulong) size_high, (ulong) size,
1574+ (ulong) srv_calc_high32(srv_log_file_size),
1575+ (ulong) srv_calc_low32(srv_log_file_size));
1576+
1577+ return(DB_ERROR);
1578+ }
1579+ } else {
1580+ ut_print_timestamp(stderr);
1581+
1582+ fprintf(stderr,
1583+ " InnoDB: Log file %s did not exist:"
1584+ " new to be created\n",
1585+ name);
1586+
1587+ fprintf(stderr, "InnoDB: Setting log file %s size to %lu MB\n",
1588+ name, (ulong) srv_log_file_size
1589+ >> (20 - UNIV_PAGE_SIZE_SHIFT));
1590+
1591+ fprintf(stderr,
1592+ "InnoDB: Database physically writes the file"
1593+ " full: wait...\n");
1594+
1595+ ret = os_file_set_size(name, new_file,
1596+ srv_calc_low32(srv_log_file_size),
1597+ srv_calc_high32(srv_log_file_size));
1598+ if (!ret) {
1599+ fprintf(stderr,
1600+ "InnoDB: Error in creating %s:"
1601+ " probably out of disk space\n",
1602+ name);
1603+
1604+ return(DB_ERROR);
1605+ }
1606+
1607+ fprintf(stderr,
1608+ "InnoDB: Setting log file was done\n");
1609+ }
1610+
1611+ ret = os_file_close(new_file);
1612+ ut_a(ret);
1613+
1614+ return(DB_SUCCESS);
1615+}
1616+
1617+/*************************************************************//**
1618+Removes old archived transaction log files.
1619+Both parameters couldn't be provided at the same time */
1620+void
1621+purge_archived_logs(
1622+ time_t before_date, /*!< in: all files modified
1623+ before timestamp should be removed */
1624+ unsigned long long before_lsn) /*!< in: files with this lsn in name
1625+ and earler should be removed */
1626+{
1627+ os_file_dir_t dir;
1628+ os_file_stat_t fileinfo;
1629+ char archived_log_filename[OS_FILE_MAX_PATH];
1630+ ulint dirnamelen;
1631+
1632+ if (log_xtra_log_archive_dir) {
1633+ dir = os_file_opendir(log_xtra_log_archive_dir, FALSE);
1634+ if (!dir) {
1635+ fprintf(stderr,
1636+ "InnoDB: Note: opening archived log directory "
1637+ "%s was failed. "
1638+ "Purge archived logs are not available\n",
1639+ log_xtra_log_archive_dir);
1640+ return;
1641+ }
1642+ } else {
1643+ return;
1644+ }
1645+
1646+ dirnamelen = strlen(log_xtra_log_archive_dir);
1647+
1648+ memcpy(archived_log_filename, log_xtra_log_archive_dir, dirnamelen);
1649+ if (dirnamelen &&
1650+ archived_log_filename[dirnamelen - 1] != SRV_PATH_SEPARATOR) {
1651+ archived_log_filename[dirnamelen++] = SRV_PATH_SEPARATOR;
1652+ }
1653+
1654+ memset(&fileinfo, 0, sizeof(fileinfo));
1655+ while(!os_file_readdir_next_file(log_xtra_log_archive_dir, dir,
1656+ &fileinfo) ) {
1657+ if (fileinfo.name != strstr(fileinfo.name,
1658+ IB_ARCHIVED_LOGS_PREFIX)) {
1659+ }
1660+ if (dirnamelen + strlen(fileinfo.name) + 2 > OS_FILE_MAX_PATH)
1661+ continue;
1662+
1663+ snprintf(archived_log_filename + dirnamelen, OS_FILE_MAX_PATH,
1664+ "%s", fileinfo.name);
1665+
1666+ if (before_lsn) {
1667+ ib_uint64_t log_file_lsn = strtoll(fileinfo.name +
1668+ strlen(IB_ARCHIVED_LOGS_PREFIX),
1669+ NULL, 10);
1670+ if (log_file_lsn == 0 || before_lsn <= log_file_lsn) {
1671+ continue;
1672+ }
1673+ } else {
1674+ fileinfo.mtime = 0;
1675+ if (os_file_get_status(archived_log_filename,
1676+ &fileinfo) == FALSE ||
1677+ fileinfo.mtime == 0) {
1678+ fprintf(stderr,
1679+ "InnoDB: Note: read archived log last"
1680+ " modification date for: %s.\n",
1681+ archived_log_filename);
1682+ continue;
1683+ }
1684+
1685+ if (before_date == 0 || fileinfo.mtime > before_date) {
1686+ continue;
1687+ }
1688+ }
1689+
1690+ if (os_file_delete(archived_log_filename) == FALSE) {
1691+ fprintf(stderr,
1692+ "InnoDB: Note: can't delete archived log file"
1693+ " %s.\n", archived_log_filename);
1694+ }
1695+ }
1696+
1697+ os_file_closedir(dir);
1698+}
1699+
1700+
1701+/*********************************************************************//**
1702+A thread which controls archiving transaction log files */
1703+UNIV_INTERN
1704+os_thread_ret_t
1705+srv_log_archive_thread(
1706+/*===================*/
1707+ void* arg __attribute__((unused)))
1708+{
1709+ log_group_t* group = UT_LIST_GET_FIRST(log_sys->log_groups);
1710+ ibool next_is_prepared;
1711+ ulint err;
1712+ char new_file_name[OS_FILE_MAX_PATH];
1713+ char archive_file_name[OS_FILE_MAX_PATH];
1714+ char final_file_name[OS_FILE_MAX_PATH];
1715+ ulint dirnamelen;
1716+ ulint final_dirnamelen;
1717+
1718+ ut_a(log_xtra_log_archive_on);
1719+
1720+ mutex_enter(&kernel_mutex);
1721+ srv_table_reserve_slot(SRV_ARCHIVE);
1722+ srv_n_threads_active[SRV_ARCHIVE]++;
1723+ mutex_exit(&kernel_mutex);
1724+
1725+ log_calc_max_ages();
1726+ log_check_margins();
1727+
1728+ /* initialization */
1729+ /* assume before the current file was archived already */
1730+ mutex_enter(&(log_sys->arch_sys_mutex));
1731+ log_sys->archived_lsn = log_sys->lsn
1732+ - ((log_group_calc_lsn_offset(log_sys->lsn - 1, group)
1733+ % group->file_size) + 1 - LOG_FILE_HDR_SIZE);
1734+ os_event_set(log_sys->arch_event_rotate_done);
1735+ mutex_exit(&(log_sys->arch_sys_mutex));
1736+
1737+ fprintf(stderr, "InnoDB: Note: "
1738+ "transaction log archiving thread started.\n");
1739+
1740+ /* confirm new file and create if needed */
1741+ dirnamelen = strlen(srv_log_group_home_dirs[0]);
1742+ ut_a(dirnamelen < (sizeof new_file_name) - 31 - sizeof "ib_logfile");
1743+
1744+ if (log_xtra_log_archive_dir &&
1745+ strcmp(log_xtra_log_archive_dir,
1746+ srv_log_group_home_dirs[0]) != 0
1747+ ) {
1748+ final_dirnamelen = strlen(log_xtra_log_archive_dir);
1749+ memcpy(final_file_name, log_xtra_log_archive_dir,
1750+ final_dirnamelen);
1751+ if (final_dirnamelen &&
1752+ final_file_name[final_dirnamelen - 1]
1753+ != SRV_PATH_SEPARATOR) {
1754+ final_file_name[final_dirnamelen++]
1755+ = SRV_PATH_SEPARATOR;
1756+ }
1757+ } else {
1758+ final_dirnamelen = 0;
1759+ }
1760+
1761+ memcpy(new_file_name, srv_log_group_home_dirs[0], dirnamelen);
1762+ if (dirnamelen && new_file_name[dirnamelen - 1] != SRV_PATH_SEPARATOR) {
1763+ new_file_name[dirnamelen++] = SRV_PATH_SEPARATOR;
1764+ }
1765+ memcpy(archive_file_name, new_file_name, dirnamelen);
1766+
1767+ snprintf(new_file_name + dirnamelen,
1768+ OS_FILE_MAX_PATH, "%s", "ib_next_logfile");
1769+
1770+ next_is_prepared = FALSE;
1771+
1772+loop:
1773+ os_event_reset(log_sys->arch_event_rotate_start);
1774+
1775+/* NOTICE: waiting the event log_sys->arch_event_rotate_done is done with
1776+ log_sys->mutex holding.
1777+ So, we cannot obtain log_sys->mutex in this thread.
1778+ The values shared with the other threads should be protected by
1779+ log_sys->arch_sys_mutex */
1780+
1781+retry_create_file:
1782+ if (!next_is_prepared) {
1783+ err = open_or_create_new_log_file(new_file_name);
1784+ if (err == DB_SUCCESS) {
1785+ next_is_prepared = TRUE;
1786+ } else {
1787+ next_is_prepared = FALSE;
1788+ fprintf(stderr, "InnoDB: Warning: "
1789+ "The new log file cannot be created. "
1790+ "The above errors should be fixed\n"
1791+ "will retry 10 seconds later again.\n");
1792+ os_thread_sleep(10000000);
1793+ goto retry_create_file;
1794+ }
1795+ }
1796+
1797+ ut_a(next_is_prepared);
1798+
1799+ mutex_enter(&(log_sys->arch_sys_mutex));
1800+ if (log_sys->last_checkpoint_lsn
1801+ >= log_sys->archived_lsn + (group->file_size - LOG_FILE_HDR_SIZE)) {
1802+ ulint hdr_offset;
1803+ ibool success;
1804+
1805+ mutex_exit(&(log_sys->arch_sys_mutex));
1806+ trx_sys_print_mysql_binlog_offset();
1807+
1808+ snprintf(archive_file_name + dirnamelen,
1809+ OS_FILE_MAX_PATH, "%s%016lld",
1810+ IB_ARCHIVED_LOGS_PREFIX,
1811+ log_sys->archived_lsn);
1812+
1813+ if (final_dirnamelen) {
1814+ snprintf(final_file_name + final_dirnamelen,
1815+ OS_FILE_MAX_PATH, "%s%016lld",
1816+ IB_ARCHIVED_LOGS_PREFIX,
1817+ log_sys->archived_lsn);
1818+ }
1819+
1820+ hdr_offset = log_group_calc_lsn_offset(
1821+ log_sys->archived_lsn, group)
1822+ / group->file_size * group->file_size / UNIV_PAGE_SIZE;
1823+
1824+ success = fil_rotate_file(group->space_id, hdr_offset,
1825+ archive_file_name, new_file_name,
1826+ LOG_FILE_HDR_SIZE);
1827+
1828+ if (success) {
1829+ mutex_enter(&(log_sys->arch_sys_mutex));
1830+ log_sys->archived_lsn += group->file_size
1831+ - LOG_FILE_HDR_SIZE;
1832+ os_event_set(log_sys->arch_event_rotate_done);
1833+ mutex_exit(&(log_sys->arch_sys_mutex));
1834+ next_is_prepared = FALSE;
1835+
1836+ if (log_xtra_log_archive_dir &&
1837+ !os_file_rename_throttled(
1838+ archive_file_name,
1839+ final_file_name,
1840+ log_xtra_log_arch_throttle)
1841+
1842+ ) {
1843+ fprintf(stderr, "InnoDB: Note: "
1844+ "moving archived log to "
1845+ "%s was failed\n",
1846+ final_file_name);
1847+ }
1848+
1849+ if (log_xtra_log_arch_expire_sec) {
1850+ purge_archived_logs(ut_time()
1851+ - log_xtra_log_arch_expire_sec,
1852+ 0);
1853+ }
1854+
1855+
1856+ goto retry_create_file;
1857+ } else {
1858+ fprintf(stderr,
1859+ "InnoDB: Error: archiving %s was failed\n"
1860+ " will retry 10 seconds later again.\n",
1861+ archive_file_name);
1862+ os_thread_sleep(10000000);
1863+ goto retry_create_file;
1864+ }
1865+ } else {
1866+ mutex_exit(&(log_sys->arch_sys_mutex));
1867+ }
1868+
1869+ if (srv_shutdown_state > 0 || log_xtra_log_archive_on == FALSE) {
1870+ fprintf(stderr,
1871+ "InnoDB: stopping transaction log archiving\n"
1872+ );
1873+ goto exit_func;
1874+ }
1875+
1876+ os_event_wait(log_sys->arch_event_rotate_start);
1877+
1878+ goto loop;
1879+
1880+exit_func:
1881+ os_event_set(log_sys->arch_event_rotate_done);
1882+
1883+ if(srv_shutdown_state == 0) {
1884+ log_calc_max_ages();
1885+ log_check_margins();
1886+ }
1887+
1888+ mutex_enter(&kernel_mutex);
1889+ srv_n_threads_active[SRV_ARCHIVE]--;
1890+ mutex_exit(&kernel_mutex);
1891+ os_thread_exit(NULL);
1892+
1893+ OS_THREAD_DUMMY_RETURN; /* srv_log_archive_thread() */
1894+}
1895+
1896 /*******************************************************************//**
1897 Tells the InnoDB server that there has been activity in the database
1898 and wakes up the master thread if it is suspended (not sleeping). Used
1899
1900=== modified file 'Percona-Server/storage/innodb_plugin/srv/srv0start.c'
1901--- Percona-Server/storage/innodb_plugin/srv/srv0start.c 2011-11-24 02:00:47 +0000
1902+++ Percona-Server/storage/innodb_plugin/srv/srv0start.c 2012-04-25 22:22:24 +0000
1903@@ -127,9 +127,9 @@
1904 static ulint ios;
1905
1906 /** io_handler_thread parameters for thread identification */
1907-static ulint n[SRV_MAX_N_IO_THREADS + 7 + UNIV_MAX_PARALLELISM];
1908+static ulint n[SRV_MAX_N_IO_THREADS + 8 + UNIV_MAX_PARALLELISM];
1909 /** io_handler_thread identifiers */
1910-static os_thread_id_t thread_ids[SRV_MAX_N_IO_THREADS + 7 + UNIV_MAX_PARALLELISM];
1911+static os_thread_id_t thread_ids[SRV_MAX_N_IO_THREADS + 8 + UNIV_MAX_PARALLELISM];
1912
1913 /** We use this mutex to test the return value of pthread_mutex_trylock
1914 on successful locking. HP-UX does NOT return 0, though Linux et al do. */
1915@@ -490,12 +490,6 @@
1916 }
1917 #endif /* !UNIV_HOTBACKUP */
1918
1919-#ifdef __WIN__
1920-#define SRV_PATH_SEPARATOR '\\'
1921-#else
1922-#define SRV_PATH_SEPARATOR '/'
1923-#endif
1924-
1925 /*********************************************************************//**
1926 Normalizes a directory path for Windows: converts slashes to backslashes. */
1927 UNIV_INTERN
1928@@ -520,7 +514,6 @@
1929 Calculates the low 32 bits when a file size which is given as a number
1930 database pages is converted to the number of bytes.
1931 @return low 32 bytes of file size when expressed in bytes */
1932-static
1933 ulint
1934 srv_calc_low32(
1935 /*===========*/
1936@@ -533,7 +526,6 @@
1937 Calculates the high 32 bits when a file size which is given as a number
1938 database pages is converted to the number of bytes.
1939 @return high 32 bytes of file size when expressed in bytes */
1940-static
1941 ulint
1942 srv_calc_high32(
1943 /*============*/
1944@@ -1880,6 +1872,12 @@
1945 os_thread_create(&srv_LRU_dump_restore_thread, NULL,
1946 thread_ids + 5 + SRV_MAX_N_IO_THREADS);
1947
1948+ /* Create the thread which archives transaction log files */
1949+ if (log_xtra_log_archive_on) {
1950+ os_thread_create(&srv_log_archive_thread, NULL,
1951+ thread_ids + 6 + SRV_MAX_N_IO_THREADS);
1952+ }
1953+
1954 /* If srv_blocking_lru_restore is TRUE, load buffer pool contents
1955 synchronously */
1956 if (srv_auto_lru_dump && srv_blocking_lru_restore)
1957@@ -1909,13 +1907,13 @@
1958 ulint i;
1959
1960 os_thread_create(&srv_purge_thread, NULL, thread_ids
1961- + (6 + SRV_MAX_N_IO_THREADS));
1962+ + (7 + SRV_MAX_N_IO_THREADS));
1963
1964 for (i = 0; i < srv_use_purge_thread - 1; i++) {
1965- n[7 + i + SRV_MAX_N_IO_THREADS] = i; /* using as index for arrays in purge_sys */
1966+ n[8 + i + SRV_MAX_N_IO_THREADS] = i; /* using as index for arrays in purge_sys */
1967 os_thread_create(&srv_purge_worker_thread,
1968- n + (7 + i + SRV_MAX_N_IO_THREADS),
1969- thread_ids + (7 + i + SRV_MAX_N_IO_THREADS));
1970+ n + (8 + i + SRV_MAX_N_IO_THREADS),
1971+ thread_ids + (8 + i + SRV_MAX_N_IO_THREADS));
1972 }
1973 }
1974 #ifdef UNIV_DEBUG
1975
1976=== modified file 'doc/source/index.rst'
1977--- doc/source/index.rst 2012-04-02 09:36:37 +0000
1978+++ doc/source/index.rst 2012-04-25 22:22:24 +0000
1979@@ -126,6 +126,7 @@
1980 management/udf_maatkit
1981 management/innodb_fake_changes
1982 management/innodb_kill_idle_trx
1983+ management/innodb_log_archiving
1984
1985 Diagnostics Improvements
1986 ========================
1987
1988=== added file 'doc/source/management/innodb_log_archiving.rst'
1989--- doc/source/management/innodb_log_archiving.rst 1970-01-01 00:00:00 +0000
1990+++ doc/source/management/innodb_log_archiving.rst 2012-04-25 22:22:24 +0000
1991@@ -0,0 +1,59 @@
1992+.. _innodb_fast_index_creation:
1993+
1994+=====================
1995+ Innodb Log archiving
1996+=====================
1997+
1998+The purpose of archiving logs is to be able to use them as a form of incremental backup.
1999+
2000+By replaying archived logs since the last backup taken with xtrabackup, you can get an online incremental backup.
2001+
2002+When log archiving is enabled, instead of rotating through log files, XtraDB will create a new one. There should always be one spare log file so that transactions are never stalled at log file rotation.
2003+
2004+``PURGE ARCHIVE LOGS``
2005+================
2006+
2007+Syntax:
2008+
2009+PURGE ARCHIVED LOGS { TO 'filename' | BEFORE `datetime`}
2010+
2011+Examples:
2012+
2013+PURGE ARCHIVED LOGS TO 'ib_log_archive_000262325167341';
2014+
2015+PURGE ARCHIVED LOGS BEFORE '2008-04-02 22:46:26';
2016+
2017+
2018+
2019+System Variables
2020+================
2021+
2022+.. variable:: innodb_xtra_log_archive
2023+
2024+ :cli: Yes
2025+ :conf: Yes
2026+ :scope: Global
2027+ :dyn: Yes
2028+ :vartype: Boolean
2029+ :default: OFF
2030+ :range: ON/OFF
2031+
2032+.. variable:: innodb_xtra_log_archive_dir
2033+
2034+ :cli: Yes
2035+ :conf: Yes
2036+ :scope: Global
2037+ :dyn: No
2038+ :vartype: String
2039+ :default: ''
2040+
2041+.. variable:: innodb_xtra_expire_archive_logs_sec
2042+
2043+ :cli: Yes
2044+ :conf: Yes
2045+ :scope: Global
2046+ :dyn: Yes
2047+ :vartype: Integer
2048+ :default: 0
2049+ :range: 0-
2050+

Subscribers

People subscribed via source and target branches