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

Proposed by Nickolay Ihalainen
Status: Superseded
Proposed branch: lp:~ihanick/percona-server/5.1-innodb-log_archiving
Merge into: lp:percona-server/5.1
Diff against target: 1851 lines (+1316/-17)
26 files modified
Percona-Server/mysql-test/r/percona_server_variables_debug.result (+3/-0)
Percona-Server/mysql-test/r/percona_server_variables_release.result (+3/-0)
Percona-Server/mysql-test/suite/innodb_plugin/r/percona_log_archiving.result (+40/-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 (+157/-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 (+103/-0)
Percona-Server/storage/innodb_plugin/include/fil0fil.h (+14/-1)
Percona-Server/storage/innodb_plugin/include/log0log.h (+18/-0)
Percona-Server/storage/innodb_plugin/include/log0log.ic (+4/-0)
Percona-Server/storage/innodb_plugin/include/os0file.h (+13/-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 (+83/-1)
Percona-Server/storage/innodb_plugin/os/os0file.c (+92/-0)
Percona-Server/storage/innodb_plugin/srv/srv0srv.c (+329/-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 Pending
Review via email: mp+98733@code.launchpad.net

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

This proposal has been superseded by a proposal from 2012-03-26.

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

Fixed problems from last MP:
missing comments, arhived logs could be stored on a different partition

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

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 :
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 :
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...

441. By Nickolay Ihalainen

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

Revision history for this message
Nickolay Ihalainen (ihanick) wrote :
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
442. By Nickolay Ihalainen

fix file copy for archived log located on separate disks

443. By Nickolay Ihalainen

pull changes from 5.1 trunk

444. By Nickolay Ihalainen

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

445. By Nickolay Ihalainen

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

446. By Nickolay Ihalainen

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

447. By Nickolay Ihalainen

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

448. By Nickolay Ihalainen

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

449. By Nickolay Ihalainen

merge with 5.1 trunk

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

Subscribers

People subscribed via source and target branches