Merge lp:~laurynas-biveinis/percona-xtrabackup/xb-changed-page-bitmap into lp:percona-xtrabackup/2.1

Proposed by Laurynas Biveinis
Status: Merged
Approved by: Alexey Kopytov
Approved revision: 535
Merged at revision: 545
Proposed branch: lp:~laurynas-biveinis/percona-xtrabackup/xb-changed-page-bitmap
Merge into: lp:percona-xtrabackup/2.1
Diff against target: 3089 lines (+2134/-172)
35 files modified
doc/source/innobackupex/how_innobackupex_works.rst (+2/-2)
doc/source/xtrabackup_bin/incremental_backups.rst (+1/-1)
doc/source/xtrabackup_bin/xbk_option_reference.rst (+4/-0)
innobackupex (+123/-66)
src/Makefile (+11/-2)
src/changed_page_bitmap.cc (+1013/-0)
src/changed_page_bitmap.h (+84/-0)
src/compact.cc (+1/-1)
src/fil_cur.cc (+23/-19)
src/fil_cur.h (+10/-5)
src/innodb_int.cc (+1/-1)
src/innodb_int.h (+2/-0)
src/read_filt.cc (+206/-0)
src/read_filt.h (+63/-0)
src/xtrabackup.cc (+118/-31)
src/xtrabackup.h (+6/-0)
test/inc/common.sh (+61/-0)
test/inc/ib_incremental_common.sh (+25/-3)
test/t/bug1007446.sh (+1/-1)
test/t/ib_incremental_bitmap.sh (+26/-0)
test/t/ib_incremental_force_full_scan.sh (+27/-0)
test/t/ib_incremental_full_scan.sh (+8/-0)
test/t/xb_incremental_bitmap_misc.sh (+76/-0)
test/t/xb_incremental_compressed.inc (+59/-32)
test/t/xb_incremental_compressed_bitmap_16kb.sh (+28/-0)
test/t/xb_incremental_compressed_bitmap_1kb.sh (+28/-0)
test/t/xb_incremental_compressed_bitmap_2kb.sh (+28/-0)
test/t/xb_incremental_compressed_bitmap_4kb.sh (+28/-0)
test/t/xb_incremental_compressed_bitmap_8kb.sh (+28/-0)
test/t/xb_incremental_compressed_full_scan_16kb.sh (+8/-0)
test/t/xb_incremental_compressed_full_scan_1kb.sh (+8/-0)
test/t/xb_incremental_compressed_full_scan_2kb.sh (+8/-0)
test/t/xb_incremental_compressed_full_scan_4kb.sh (+8/-0)
test/t/xb_incremental_compressed_full_scan_8kb.sh (+8/-0)
test/t/xb_log_overwrap.sh (+3/-8)
To merge this branch: bzr merge lp:~laurynas-biveinis/percona-xtrabackup/xb-changed-page-bitmap
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Approve
Review via email: mp+159207@code.launchpad.net

This proposal supersedes a proposal from 2013-04-16.

Description of the change

4th MP:

http://jenkins.percona.com/job/percona-xtrabackup-2.1-param/259/
- Added option --incremental-force-scan for innobackupex.
- Added new testcase ib_incremental_force_full_scan.sh.

3rd MP:

http://jenkins.percona.com/job/percona-xtrabackup-2.1-param/256/

- Updated to the current 2.1 trunk.
- Added option --incremental-force-scan. Added its test to xb_incremental_bitmap_missing.sh and renamed it to xb_incremental_bitmap_misc.sh.
- Fixed stray semicolons in mysql_query(). (Only in those introduced in this MP, left the existing ones untouched).
- Removed spurious BEGIN/COMMIT from the testcases.
- Updated docs. It is debatable whether it's complete and well-written, but it's a start.

2nd MP:

http://jenkins.percona.com/view/XtraBackup/job/percona-xtrabackup-2.1-param/242/

Updated to the current 2.1 trunk.

1st MP:

BT 16274

http://jenkins.percona.com/job/percona-xtrabackup-2.1-param/237/

The MP has a couple of changes to anticipate the BT 28340 5.6 merge in innodb_int.h (introduction of lsn_t and friends). But this MP and the 28340 MP will need some updating depending on their trunk merge order.

Implement bitmap-based incremental backups.
Blueprints:
https://blueprints.launchpad.net/percona-xtrabackup/+spec/changed-page-bmp-inc-backups
and
https://blueprints.launchpad.net/percona-xtrabackup/+spec/multipl-bmp-file-inc-backup
- innobackupex: split the xtrabackup_suspended to three different
  files depending on the context (1st suspend, 2nd suspend, log copy
  end). If backing up, check if server supports changed page bitmaps
  by querying for the presence of I_S.INNODB_CHANGED_PAGES plugin. If
  found, add 1st suspend to xtrabackup invocation and issue FLUSH
  CHANGED_PAGE_BITMAPS during it. Factor out waitpid/sync file
  presence loop out of wait_for_ibbackup_suspend and resume_ibbackup
  into new subroutine wait_for_ibbackup_file_create. Split the log
  copying finish logic out of resume_ibbackup into a new subroutine
  wait_for_ibbackup_log_copy_finish.

- New source files changed_page_bitmap.h and changed_page_bitmap.cc
  containing the types and functions for working with page bitmaps and
  their iterators.

- New source files read_filt.h and read_filt.cc containing the data
  file read filter abstraction. Provide two filters: rf_pass_through
  for no filtering, and rf_bitmap that uses the changed page bitmaps.

- fil_cur.h, fil_cur.cc: xb_fil_cur_t: move offset field to
  xb_read_filt_ctxt_t. Add fields for the read filter and its
  context: read_filter and read_filter_ctxt. xb_fil_cur_open(): new
  arg read_filter. Initialize the read filter context.
  xb_fil_cur_read: use the read filter to get the next read offset and
  length. xb_fil_cur_close(): deinitialize the read filter.

- innodb_int.h: include mysql_version.h to make version checks not
  depend on mysql_version.h having been included before innodb_int.h.

- innodb_int.cc: fix an unrelated warning to make it easier to ensure
  that the feature does not regress in warnings: xb_space_get_by_name:
  mark fold as unused.

- xtrabackup.h: declare checkpoint_lsn_start and changed_page_bitmap.

- xtrabackup.cc: split xtrabackup_suspended to three different files
  as in innobackupex. New functions xb_make_sync_file_name and
  xtrabackup_suspend, rename xb_create_suspend_file to
  xb_create_sync_file. Add new option --suspend-at-start to suspend
  xtrabackup after the log copying thread has started.
  xtrabackup_copy_datafile: set up a bitmap read filter if the bitmap
  has been allocated, a pass through filter otherwise and pass it to
  the file cursor.
  xtrabackup_backup_func(): in case of incremental backups attempt to
  read the changed page bitmap. Write a message if the bitmap was
  found, otherwise that a full scan is going to be used. Free the
  bitmap in the end.

- test/inc/common.sh: init_server_variables(), reset_server_variables:
  set up server error log location in SRV_MYSQLD_ERRFILE[].
  switch_server(): set up MySQL error log.
  check_full_scan_inc_backup(), check_bitmap_inc_backup(): new
  functions for grepping XtraBackup output for the incremental backup
  type used. wait_for_xb_suspend(), resume_suspended_xb(): new helper
  functions for the testcases that use xtrabackup suspend.

- Rename test test/t/ib_incremental.sh to a test include file
  test/inc/ib_incremental_common.sh. Pass additional args in
  $mysqld_extra_args to mysqld.

- Rename test test/t/xb_incremental.sh to an include file
  test/inc/xb_incremental_common.sh. Pass additional args in
  $mysqld_extra_args to mysqld. Adjust server options to have a
  minimum possible log file size (1M) and thread concurrency that can
  support such size for 5.0. Change the schema of test table T2 to
  have large rows. Insert enough rows into a database so that the
  resulting bitmap file spans at least two bitmap pages for the T2
  tablespace.

- Rename test test/t/ib_incremental.sh to an include file
  test/inc/ib_incremental_common.sh. Pass additional args in
  $mysqld_extra_args to mysqld. Tweak it to increase the testing
  coverage. ADd two new testcases ib_incremental_bitmap.sh and
  ib_incremental_full_scan.sh that use it.

- Adjust test/t/xb_incremental_compressed.inc for bitmap backups. New
  tests xb_incremental_compressed_bitmap_(1-16)kb.sh. Rename
  the previous xb_incremental_compressed_*.sh tests to
  xb_incremental_compressed_full_scan_*.sh. Remove the InnoDB version
  check from xb_incremental_compressed.inc as it's always passing with
  the currently supported versions.

- New test xb_incremental_bitmap_missing.sh to test diagnostics for
  partially-missing bitmap data.

- Temporarily disable all the bitmap tests on XtraDB Cluster
  configuration until a supporting version is released.

- Adjust test/t/bug1007446.sh for changed suspend file names.

- Adjust test/t/xb_log_overwraph.sh to use
  wait_for_xb_to_suspend/resume_suspended_xb.

- Makefile: update dependencies.

To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote : Posted in a previous version of this proposal

I have merged the 28340 MP, so this probably needs updating now according to the comments?

review: Needs Information
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal

Yes, I will update it. I expect mostly mechanical changes (innodb_int.h does not need to provide lsn_t, 64-bit args instead of 32-bit pairs for I/O, etc).

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

Laurynas,

There's no time for a detailed review, but:

   - it would be nice to have an option to force the old .delta
     calculation method even if bitmaps are available; something like
     --incremental-force-scan?
   - there's no need to append ';' to the end of a query in
     mysql_query()
   - the optimization attempt to wrap multiple INSERT statements for
     incremental tests into BEGIN/COMMIT does not work, as we disconnect
     between mysql client invokations; it should be smth like the
     following:
: (echo "BEGIN;"; echo "..."; echo "COMMIT;") | $MYSQL $MYSQL_ARGS ...
   - would updating docs in the same MP be a stretch? Just trying to
     avoid the communication overhead when docs are updated by Hrvoje,
     and the required docs updates seem to be minor

Please let me know if addressing the above would take more than a day. We can then merge the current MP as is, and address the remaining stuff later.

review: Needs Fixing
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal

> - it would be nice to have an option to force the old .delta
> calculation method even if bitmaps are available; something like
> --incremental-force-scan?

Yes.

> - there's no need to append ';' to the end of a query in
> mysql_query()

Ack

> - the optimization attempt to wrap multiple INSERT statements for
> incremental tests into BEGIN/COMMIT does not work, as we disconnect
> between mysql client invokations; it should be smth like the
> following:
> : (echo "BEGIN;"; echo "..."; echo "COMMIT;") | $MYSQL $MYSQL_ARGS ...

Right, I forgot that it's all separate connections, thus separate transactions. I had some sort of persistent connection setup in progress at bug 1119451, comments #2 and #3. I think here we can just go with one statement transactions and optimize later as needed (bug 1119451 stuff is incomplete).

> - would updating docs in the same MP be a stretch? Just trying to
> avoid the communication overhead when docs are updated by Hrvoje,
> and the required docs updates seem to be minor

Will review, should be feasible.

> Please let me know if addressing the above would take more than a day. We can
> then merge the current MP as is, and address the remaining stuff later.

It should take less than a day, and I will work on this today (maybe tomorrow morning will be needed to wait for Jenkins run etc).

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

Laurynas,

I meant to say it would be nice to have --incremental-force-scan option in innobackupex (it is implied that xtrabackup also has to support it). Since innobackupex does a part of the work for changed page bitmap backups and generates command line arguments for xtrabackup, it is currently impossible to actually disable that method when innobackupex is used.

review: Needs Fixing
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote : Posted in a previous version of this proposal

--incremental-force-scan option in innobackupex

It's obvious, but, the work of hurry :(

Meanwhile I'll be glad to take any documentation change suggestions.

Revision history for this message
Alexey Kopytov (akopytov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/source/innobackupex/how_innobackupex_works.rst'
2--- doc/source/innobackupex/how_innobackupex_works.rst 2012-09-07 13:20:26 +0000
3+++ doc/source/innobackupex/how_innobackupex_works.rst 2013-04-16 17:44:27 +0000
4@@ -15,7 +15,7 @@
5
6 If no mode is specified, |innobackupex| will assume the backup mode.
7
8-By default, it starts :program:`xtrabackup` with the :option:`--suspend-at-end` option, and lets it copy the InnoDB data files. When :program:`xtrabackup` finishes that, :program:`innobackupex` sees it create the :file:`xtrabackup_suspended` file and executes ``FLUSH TABLES WITH READ LOCK``. Then it begins copying the rest of the files.
9+By default, it starts :program:`xtrabackup` with the :option:`--suspend-at-end` option, and lets it copy the InnoDB data files. When :program:`xtrabackup` finishes that, :program:`innobackupex` sees it create the :file:`xtrabackup_suspended_2` file and executes ``FLUSH TABLES WITH READ LOCK``. Then it begins copying the rest of the files.
10
11 If the :option:`--ibbackup` is not supplied, |innobackupex| will try to detect it: if the :file:`xtrabackup_binary` file exists on the backup directory, it reads from it which binary of |xtrabackup| will be used. Otherwise, it will try to connect to the database server in order to determine it. If the connection can't be established, |xtrabackup| will fail and you must specify it (see :ref:`ibk-right-binary`).
12
13@@ -25,7 +25,7 @@
14
15 Once this is done, the backup of the files will begin. It will backup :term:`.frm`, :term:`.MRG`, :term:`.MYD`, :term:`.MYI`, :term:`.TRG`, :term:`.TRN`, :term:`.ARM`, :term:`.ARZ`, :term:`.CSM`, :term:`.CSV` and :term:`.opt` files.
16
17-When all the files are backed up, it resumes :program:`ibbackup` and wait until it finishes copying the transactions done while the backup was done. Then, the tables are unlocked, the slave is started (if the option :option:`--safe-slave-backup` was used) and the connection with the server is closed. Then, it removes the :file:`xtrabackup_suspended` file and permits :program:`xtrabackup` to exit.
18+When all the files are backed up, it resumes :program:`ibbackup` and wait until it finishes copying the transactions done while the backup was done. Then, the tables are unlocked, the slave is started (if the option :option:`--safe-slave-backup` was used) and the connection with the server is closed. Then, it removes the :file:`xtrabackup_suspended_2` file and permits :program:`xtrabackup` to exit.
19
20 It will also create the following files in the directory of the backup:
21
22
23=== modified file 'doc/source/xtrabackup_bin/incremental_backups.rst'
24--- doc/source/xtrabackup_bin/incremental_backups.rst 2013-01-17 15:08:37 +0000
25+++ doc/source/xtrabackup_bin/incremental_backups.rst 2013-04-16 17:44:27 +0000
26@@ -4,7 +4,7 @@
27
28 Both |xtrabackup| and |innobackupex| tools supports incremental backups, which means that it can copy only the data that has changed since the last full backup. You can perform many incremental backups between each full backup, so you can set up a backup process such as a full backup once a week and an incremental backup every day, or full backups every day and incremental backups every hour.
29
30-Incremental backups work because each InnoDB page (usually 16kb in size) contains a log sequence number, or :term:`LSN`. The :term:`LSN` is the system version number for the entire database. Each page's :term:`LSN` shows how recently it was changed. An incremental backup copies each page whose :term:`LSN` is newer than the previous incremental or full backup's :term:`LSN`.
31+Incremental backups work because each InnoDB page (usually 16kb in size) contains a log sequence number, or :term:`LSN`. The :term:`LSN` is the system version number for the entire database. Each page's :term:`LSN` shows how recently it was changed. An incremental backup copies each page whose :term:`LSN` is newer than the previous incremental or full backup's :term:`LSN`. There are two algorithms in use to find the set of such pages to be copied. The first one, available with all the server types and versions, is to check the page :term:`LSN` directly by reading all the data pages. The second one, available with |Percona Server|, is to enable the changed page tracking feature on the server, which will note the pages as they are being changed. This information will be then written out in a compact separate so-called bitmap file. The |xtrabackup| binary will use that file to read only the data pages it needs for the incremental backup, potentially saving many read requests. The latter algorithm is enabled by default if the |xtrabackup| binary finds the bitmap file. It is possible to specify :option:`--incremental-force-scan` to read all the pages even if the bitmap data is available.
32
33 Incremental backups do not actually compare the data files to the previous backup's data files. In fact, you can use :option:`--incremental-lsn` to perform an incremental backup without even having the previous backup, if you know its :term:`LSN`. Incremental backups simply read the pages and compare their :term:`LSN` to the last backup's :term:`LSN`. You still need a full backup to recover the incremental changes, however; without a full backup to act as a base, the incremental backups are useless.
34
35
36=== modified file 'doc/source/xtrabackup_bin/xbk_option_reference.rst'
37--- doc/source/xtrabackup_bin/xbk_option_reference.rst 2012-05-18 11:19:17 +0000
38+++ doc/source/xtrabackup_bin/xbk_option_reference.rst 2013-04-16 17:44:27 +0000
39@@ -65,6 +65,10 @@
40
41 When preparing an incremental backup, this is the directory where the incremental backup is combined with the full backup to make a new full backup.
42
43+.. option:: --incremental-force-scan
44+
45+ When creating an incremental backup, force a full scan of the data pages in the instance being backuped even if the complete changed page bitmap data is available.
46+
47 .. option:: --incremental-lsn=name
48
49 When creating an incremental backup, you can specify the log sequence number (:term:`LSN`) instead of specifying :option:`--incremental-basedir`. For databases created by *MySQL* and *Percona Server* 5.0-series versions, specify the :term:`LSN` as two 32-bit integers in high:low format. For databases created in 5.1 and later, specify the :term:`LSN` as a single 64-bit integer. ##ATTENTION##: If a wrong LSN value is specified, it is impossible to diagnose this, causing the backup to be unusable. Be careful!
50
51=== modified file 'innobackupex'
52--- innobackupex 2013-04-09 10:58:46 +0000
53+++ innobackupex 2013-04-16 17:44:27 +0000
54@@ -71,6 +71,10 @@
55 # end of modifiable parameters
56 ######################################################################
57
58+#######################
59+# Server capabilities #
60+#######################
61+my $have_changed_page_bitmaps = 0;
62
63 # command line options
64 my $option_help = '';
65@@ -113,6 +117,7 @@
66 my $option_incremental = '';
67 my $option_incremental_basedir = '';
68 my $option_incremental_dir = '';
69+my $option_incremental_force_scan = 0;
70 my $option_incremental_lsn = '';
71 my $option_extra_lsndir = '';
72 my $option_rsync = '';
73@@ -144,8 +149,8 @@
74 # backup directory pathname
75 my $backup_dir = '';
76
77-# name of the ibbackup suspend-at-end file
78-my $suspend_file = '';
79+# directory where innobackupex aux files are cretead
80+my $work_dir;
81
82 # home directory of innoDB log files
83 my $innodb_log_group_home_dir = '';
84@@ -221,6 +226,12 @@
85 my $xtrabackup_pid_file = 'xtrabackup_pid';
86 my %rsync_files_hash;
87
88+my $xb_fn_suspended_at_start = "/xtrabackup_suspended_1";
89+my $xb_fn_suspended_at_end = "/xtrabackup_suspended_2";
90+my $xb_fn_log_copied = "/xtrabackup_log_copied";
91+my @xb_suspend_files = ($xb_fn_suspended_at_start, $xb_fn_suspended_at_end,
92+ $xb_fn_log_copied);
93+
94 my $copy_dir_src;
95 my $copy_dir_dst;
96 my $copy_dir_exclude_regexp;
97@@ -379,12 +390,26 @@
98 #
99 sub backup {
100 my $orig_datadir = get_option(\%config, $option_defaults_group, 'datadir');
101+ my $suspend_file;
102+
103+ if ($option_incremental) {
104+ detect_mysql_capabilities_for_backup(\%mysql);
105+ }
106
107 # start ibbackup as a child process
108 start_ibbackup();
109
110+ if ($option_incremental && $have_changed_page_bitmaps
111+ && !$option_incremental_force_scan) {
112+ $suspend_file = $work_dir . $xb_fn_suspended_at_start;
113+ wait_for_ibbackup_suspend($suspend_file);
114+ mysql_query(\%mysql, 'FLUSH NO_WRITE_TO_BINLOG CHANGED_PAGE_BITMAPS');
115+ resume_ibbackup($suspend_file);
116+ }
117+
118 # wait for ibbackup to suspend itself
119- wait_for_ibbackup_suspend();
120+ $suspend_file = $work_dir . $xb_fn_suspended_at_end;
121+ wait_for_ibbackup_suspend($suspend_file);
122
123 if ($option_safe_slave_backup) {
124 wait_for_safe_slave(\%mysql);
125@@ -408,8 +433,10 @@
126 # (or finalize the backup by syncing changes if using rsync)
127 backup_files(0);
128
129- # resume ibbackup and wait till log copying is finished
130- resume_ibbackup();
131+ # resume XtraBackup and wait till it has finished
132+ resume_ibbackup($suspend_file);
133+ $suspend_file = $work_dir . $xb_fn_log_copied;
134+ wait_for_ibbackup_log_copy_finish($suspend_file);
135
136 # release read locks on all tables
137 mysql_unlockall(\%mysql) if !$option_no_lock;
138@@ -887,24 +914,50 @@
139
140 }
141
142-
143-#
144-# wait_for_ibbackup_suspend subroutine waits until ibbackup has suspended
145+#
146+# Loops until the specified file is created or the ibbackup process dies.
147+#
148+#
149+# If this function detects that the xtrabackup process has terminated, it also
150+# sets ibbackup_exit_code and resets ibbackup_pid to avoid trying to reap a
151+# non-existing process later
152+#
153+sub wait_for_ibbackup_file_create {
154+
155+ my $suspend_file = shift;
156+ my $pid = -1;
157+
158+ for (;;) {
159+ # check if the xtrabackup process is still alive _before_ checking if
160+ # the file exists to avoid a race condition when the file is created
161+ # and the process terminates right after we do the file check
162+ $pid = waitpid($ibbackup_pid, &WNOHANG);
163+
164+ last if -e $suspend_file;
165+
166+ if ($ibbackup_pid == $pid) {
167+ # The file doesn't exist, but the process has terminated
168+ Die "ibbackup child process has died";
169+ }
170+
171+ sleep 1;
172+ }
173+
174+ if ($pid == $ibbackup_pid) {
175+ $ibbackup_exit_code = $CHILD_ERROR >> 8;
176+ $ibbackup_pid = '';
177+ }
178+}
179+
180+#
181+# wait_for_ibbackup_suspend subroutine waits until ibbackup has suspended
182 # itself.
183 #
184 sub wait_for_ibbackup_suspend {
185+ my $suspend_file = shift;
186 print STDERR "$prefix Waiting for ibbackup (pid=$ibbackup_pid) to suspend\n";
187 print STDERR "$prefix Suspend file '$suspend_file'\n\n";
188- for (;;) {
189- usleep(100000);
190- last if -e $suspend_file;
191-
192- # check that ibbackup child process is still alive
193- if ($ibbackup_pid == waitpid($ibbackup_pid, &WNOHANG)) {
194- $ibbackup_pid = '';
195- Die "ibbackup child process has died";
196- }
197- }
198+ wait_for_ibbackup_file_create($suspend_file);
199 $now = current_time();
200 open XTRABACKUP_PID, "> $option_tmpdir/$xtrabackup_pid_file";
201 print XTRABACKUP_PID $ibbackup_pid;
202@@ -912,43 +965,27 @@
203 print STDERR "\n$now $prefix Continuing after ibbackup has suspended\n";
204 }
205
206-
207 #
208 # resume_ibbackup subroutine signals ibbackup to finish log copying by deleting
209-# the 'xtrabackup_suspended' file and waits until log copying is stopped,
210-# i.e. until 'xtrabackup_suspended' is created again.
211-#
212-# If this functions detects that the xtrabackup process has terminated, it also
213-# sets ibbackup_exit_code and resets ibbackup_pid to avoid trying to reap a
214-# non-existing process later
215+# $suspend_file.
216 #
217 sub resume_ibbackup {
218- my $pid = -1;
219+ my $suspend_file = shift;
220+ unlink $suspend_file || Die "Failed to delete '$suspend_file': $!";
221+}
222
223- $now = current_time();
224+#
225+# Wait until xtrabackup finishes copying log or dies. The log copying is
226+# signaled by a sync file creation. If this function detects that the
227+# xtrabackup process has terminated, it also sets ibbackup_exit_code and resets
228+# ibbackup_pid to avoid trying to reap a non-existing process later.
229+#
230+sub wait_for_ibbackup_log_copy_finish {
231+ my $suspend_file = shift;
232+ my $now = current_time();
233 print STDERR "$now $prefix Waiting for log copying to finish\n\n";
234- unlink $suspend_file || Die "Failed to delete '$suspend_file': $!";
235-
236- for (;;) {
237- # check if the xtrabackup process is still alive _before_ checking if
238- # the file exists to avoid a race condition when the file is created and
239- # the process terminates right after we do the file check
240- $pid = waitpid($ibbackup_pid, &WNOHANG);
241-
242- last if -e $suspend_file;
243-
244- if ($ibbackup_pid == $pid) {
245- # The file doesn't exist, but the process has terminated
246- Die "ibbackup child process has died";
247- }
248-
249- sleep 1;
250- }
251-
252- if ($pid == $ibbackup_pid) {
253- $ibbackup_exit_code = $CHILD_ERROR >> 8;
254- $ibbackup_pid = '';
255- }
256+
257+ wait_for_ibbackup_file_create($suspend_file);
258
259 unlink $suspend_file || Die "Failed to delete '$suspend_file': $!";
260 }
261@@ -972,7 +1009,6 @@
262 $ibbackup_pid = '';
263 return $CHILD_ERROR >> 8;
264 }
265-
266
267 #
268 # start_ibbackup subroutine spawns a child process running ibbackup
269@@ -1051,6 +1087,11 @@
270 } else {
271 $options = $options . " --incremental-basedir='$incremental_basedir'";
272 }
273+ if ($option_incremental_force_scan) {
274+ $options = $options . " --incremental-force-scan";
275+ } elsif ($have_changed_page_bitmaps) {
276+ $options = $options . " --suspend-at-start";
277+ }
278 }
279
280 if ($option_tables_file) {
281@@ -1638,28 +1679,31 @@
282 $backup_dir = File::Spec->rel2abs(make_backup_dir());
283 print STDERR "$prefix Created backup directory $backup_dir\n";
284 if (!$option_stream) {
285- $backup_config_file = $backup_dir . '/backup-my.cnf';
286- $suspend_file = $backup_dir . '/xtrabackup_suspended';
287- $binlog_info = $backup_dir . '/xtrabackup_binlog_info';
288- $galera_info = $backup_dir . '/xtrabackup_galera_info';
289- $slave_info = $backup_dir . '/xtrabackup_slave_info';
290+ $work_dir = $backup_dir;
291 } else {
292- $suspend_file = $option_tmpdir . '/xtrabackup_suspended';
293- $backup_config_file = $option_tmpdir . '/backup-my.cnf';
294- $binlog_info = $option_tmpdir . '/xtrabackup_binlog_info';
295- $galera_info = $option_tmpdir . '/xtrabackup_galera_info';
296- $slave_info = $option_tmpdir . '/xtrabackup_slave_info';
297+ $work_dir = $option_tmpdir;
298 }
299+ $backup_config_file = $work_dir . '/backup-my.cnf';
300+ $binlog_info = $work_dir . '/xtrabackup_binlog_info';
301+ $galera_info = $work_dir . '/xtrabackup_galera_info';
302+ $slave_info = $work_dir . '/xtrabackup_slave_info';
303 write_backup_config_file($backup_config_file);
304+
305+ foreach (@xb_suspend_files) {
306+ my $suspend_file = $work_dir . "$_";
307+ if ( -e "$suspend_file" ) {
308+ print STDERR
309+ "WARNING : A left over instance of " .
310+ "suspend file '$suspend_file' was found.\n";
311+ unlink "$suspend_file"
312+ || Die "Failed to delete '$suspend_file': $!";
313+ }
314+ }
315+
316 } elsif ($option_copy_back || $option_move_back) {
317 #$backup_config_file = $backup_dir . '/backup-my.cnf';
318 #read_config_file($backup_config_file, \%backup_config);
319 }
320-
321- if ( -e "$suspend_file" ) {
322- print STDERR "WARNING : A left over instance of suspend file '$suspend_file' was found.\n";
323- unlink $suspend_file || Die "Failed to delete '$suspend_file': $!";
324- }
325 }
326
327
328@@ -1778,6 +1822,7 @@
329 'defaults-extra-file=s' => \$option_defaults_extra_file,
330 'incremental' => \$option_incremental,
331 'incremental-basedir=s' => \$option_incremental_basedir,
332+ 'incremental-force-scan' => \$option_incremental_force_scan,
333 'incremental-lsn=s' => \$option_incremental_lsn,
334 'incremental-dir=s' => \$option_incremental_dir,
335 'extra-lsndir=s' => \$option_extra_lsndir,
336@@ -2730,6 +2775,14 @@
337 return $rcode;
338 }
339
340+#
341+# Query the server to find out what backup capabilities it supports.
342+#
343+sub detect_mysql_capabilities_for_backup {
344+ $have_changed_page_bitmaps =
345+ mysql_query($_[0], 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.PLUGINS '.
346+ 'WHERE PLUGIN_NAME LIKE "INNODB_CHANGED_PAGES"')
347+}
348
349 =pod
350
351@@ -2750,7 +2803,7 @@
352 [--databases=LIST] [--no-lock]
353 [--tmpdir=DIRECTORY] [--tables-file=FILE]
354 [--incremental] [--incremental-basedir]
355- [--incremental-dir] [--incremental-lsn]
356+ [--incremental-dir] [--incremental-force-scan] [--incremental-lsn]
357 [--compact]
358 BACKUP-ROOT-DIR
359
360@@ -2918,6 +2971,10 @@
361
362 This option specifies the directory where the incremental backup will be combined with the full backup to make a new full backup. The option accepts a string argument. It is used with the --incremental option.
363
364+=item --incremental-force-scan
365+
366+This options tells xtrabackup to perform full scan of data files for taking an incremental backup even if full changed page bitmap data is available to enable the backup without the full scan.
367+
368 =item --incremental-lsn
369
370 This option specifies the log sequence number (LSN) to use for the incremental backup. The option accepts a string argument. It is used with the --incremental option. It is used instead of specifying --incremental-basedir. For databases created by MySQL and Percona Server 5.0-series versions, specify the LSN as two 32-bit integers in high:low format. For databases created in 5.1 and later, specify the LSN as a single 64-bit integer.
371
372=== modified file 'src/Makefile'
373--- src/Makefile 2013-04-09 07:24:21 +0000
374+++ src/Makefile 2013-04-16 17:44:27 +0000
375@@ -41,7 +41,9 @@
376 datasink.o \
377 xbstream_write.o \
378 quicklz/quicklz.o
379-XTRABACKUPCCOBJS = xtrabackup.o innodb_int.o compact.o fil_cur.o write_filt.o
380+XTRABACKUPCCOBJS = xtrabackup.o innodb_int.o compact.o fil_cur.o write_filt.o \
381+ changed_page_bitmap.o \
382+ read_filt.o
383
384 XBSTREAMOBJS = xbstream.o xbstream_write.o xbstream_read.o
385
386@@ -231,7 +233,14 @@
387 xbcrypt: $(XBCRYPTOBJS) $(MYSQLOBJS)
388 $(CXX) $(CXXFLAGS) $^ $(INC) $(MYSQLOBJS) $(LIBS) -o $@
389
390-xtrabackup.o: xtrabackup.cc xb_regex.h write_filt.h fil_cur.h xtrabackup.h compact.h
391+changed_page_bitmap.o: changed_page_bitmap.cc changed_page_bitmap.h innodb_int.h \
392+ common.h xtrabackup.h
393+
394+read_filt.o: read_filt.cc read_filt.h fil_cur.h xtrabackup.h innodb_int.h \
395+ common.h changed_page_bitmap.h
396+
397+xtrabackup.o: xtrabackup.cc xb_regex.h write_filt.h fil_cur.h xtrabackup.h compact.h \
398+ common.h changed_page_bitmap.h read_filt.h innodb_int.h
399
400 $(TARGET): $(XTRABACKUPCCOBJS) $(XTRABACKUPCOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBARCHIVE_A)
401 $(CXX) $(CFXXLAGS) $(XTRABACKUPCCOBJS) $(XTRABACKUPCOBJS) $(INNODBOBJS) $(MYSQLOBJS) $(LIBS) \
402
403=== added file 'src/changed_page_bitmap.cc'
404--- src/changed_page_bitmap.cc 1970-01-01 00:00:00 +0000
405+++ src/changed_page_bitmap.cc 2013-04-16 17:44:27 +0000
406@@ -0,0 +1,1013 @@
407+/******************************************************
408+XtraBackup: hot backup tool for InnoDB
409+(c) 2009-2012 Percona Inc.
410+Originally Created 3/3/2009 Yasufumi Kinoshita
411+Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
412+Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
413+
414+This program is free software; you can redistribute it and/or modify
415+it under the terms of the GNU General Public License as published by
416+the Free Software Foundation; version 2 of the License.
417+
418+This program is distributed in the hope that it will be useful,
419+but WITHOUT ANY WARRANTY; without even the implied warranty of
420+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
421+GNU General Public License for more details.
422+
423+You should have received a copy of the GNU General Public License
424+along with this program; if not, write to the Free Software
425+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
426+
427+*******************************************************/
428+
429+/* Changed page bitmap implementation */
430+
431+#include "changed_page_bitmap.h"
432+
433+#include "common.h"
434+#include "innodb_int.h"
435+#include "xtrabackup.h"
436+
437+/* TODO: copy-pasted shared definitions from the XtraDB bitmap write code.
438+Remove these on the first opportunity, i.e. single-binary XtraBackup. */
439+
440+/* log0online.h */
441+
442+/** Single bitmap file information */
443+struct log_online_bitmap_file_t {
444+ char name[FN_REFLEN]; /*!< Name with full path */
445+ os_file_t file; /*!< Handle to opened file */
446+ ib_uint64_t size; /*!< Size of the file */
447+ ib_uint64_t offset; /*!< Offset of the next read,
448+ or count of already-read bytes
449+ */
450+};
451+
452+/** A set of bitmap files containing some LSN range */
453+struct log_online_bitmap_file_range_t {
454+ size_t count; /*!< Number of files */
455+ /*!< Dynamically-allocated array of info about individual files */
456+ struct files_t {
457+ char name[FN_REFLEN];/*!< Name of a file */
458+ lsn_t start_lsn; /*!< Starting LSN of data in this
459+ file */
460+ ulong seq_num; /*!< Sequence number of this file */
461+ } *files;
462+};
463+
464+/* log0online.c */
465+
466+/** File name stem for bitmap files. */
467+static const char* bmp_file_name_stem = "ib_modified_log_";
468+
469+/** The bitmap file block size in bytes. All writes will be multiples of this.
470+ */
471+enum {
472+ MODIFIED_PAGE_BLOCK_SIZE = 4096
473+};
474+
475+/** Offsets in a file bitmap block */
476+enum {
477+ MODIFIED_PAGE_IS_LAST_BLOCK = 0,/* 1 if last block in the current
478+ write, 0 otherwise. */
479+ MODIFIED_PAGE_START_LSN = 4, /* The starting tracked LSN of this and
480+ other blocks in the same write */
481+ MODIFIED_PAGE_END_LSN = 12, /* The ending tracked LSN of this and
482+ other blocks in the same write */
483+ MODIFIED_PAGE_SPACE_ID = 20, /* The space ID of tracked pages in
484+ this block */
485+ MODIFIED_PAGE_1ST_PAGE_ID = 24, /* The page ID of the first tracked
486+ page in this block */
487+ MODIFIED_PAGE_BLOCK_UNUSED_1 = 28,/* Unused in order to align the start
488+ of bitmap at 8 byte boundary */
489+ MODIFIED_PAGE_BLOCK_BITMAP = 32,/* Start of the bitmap itself */
490+ MODIFIED_PAGE_BLOCK_UNUSED_2 = MODIFIED_PAGE_BLOCK_SIZE - 8,
491+ /* Unused in order to align the end of
492+ bitmap at 8 byte boundary */
493+ MODIFIED_PAGE_BLOCK_CHECKSUM = MODIFIED_PAGE_BLOCK_SIZE - 4
494+ /* The checksum of the current block */
495+};
496+
497+/** Length of the bitmap data in a block */
498+enum { MODIFIED_PAGE_BLOCK_BITMAP_LEN
499+ = MODIFIED_PAGE_BLOCK_UNUSED_2 - MODIFIED_PAGE_BLOCK_BITMAP };
500+
501+/** Length of the bitmap data in a block in page ids */
502+enum { MODIFIED_PAGE_BLOCK_ID_COUNT = MODIFIED_PAGE_BLOCK_BITMAP_LEN * 8 };
503+
504+typedef ib_uint64_t bitmap_word_t;
505+
506+/****************************************************************//**
507+Calculate a bitmap block checksum. Algorithm borrowed from
508+log_block_calc_checksum.
509+@return checksum */
510+UNIV_INLINE
511+ulint
512+log_online_calc_checksum(
513+/*=====================*/
514+ const byte* block); /*!<in: bitmap block */
515+
516+/****************************************************************//**
517+Provide a comparisson function for the RB-tree tree (space,
518+block_start_page) pairs. Actual implementation does not matter as
519+long as the ordering is full.
520+@return -1 if p1 < p2, 0 if p1 == p2, 1 if p1 > p2
521+*/
522+static
523+int
524+log_online_compare_bmp_keys(
525+/*========================*/
526+ const void* p1, /*!<in: 1st key to compare */
527+ const void* p2) /*!<in: 2nd key to compare */
528+{
529+ const byte *k1 = (const byte *)p1;
530+ const byte *k2 = (const byte *)p2;
531+
532+ ulint k1_space = mach_read_from_4(k1 + MODIFIED_PAGE_SPACE_ID);
533+ ulint k2_space = mach_read_from_4(k2 + MODIFIED_PAGE_SPACE_ID);
534+ if (k1_space == k2_space) {
535+
536+ ulint k1_start_page
537+ = mach_read_from_4(k1 + MODIFIED_PAGE_1ST_PAGE_ID);
538+ ulint k2_start_page
539+ = mach_read_from_4(k2 + MODIFIED_PAGE_1ST_PAGE_ID);
540+ return k1_start_page < k2_start_page
541+ ? -1 : k1_start_page > k2_start_page ? 1 : 0;
542+ }
543+ return k1_space < k2_space ? -1 : 1;
544+}
545+
546+/****************************************************************//**
547+Calculate a bitmap block checksum. Algorithm borrowed from
548+log_block_calc_checksum.
549+@return checksum */
550+UNIV_INLINE
551+ulint
552+log_online_calc_checksum(
553+/*=====================*/
554+ const byte* block) /*!<in: bitmap block */
555+{
556+ ulint sum;
557+ ulint sh;
558+ ulint i;
559+
560+ sum = 1;
561+ sh = 0;
562+
563+ for (i = 0; i < MODIFIED_PAGE_BLOCK_CHECKSUM; i++) {
564+
565+ ulint b = block[i];
566+ sum &= 0x7FFFFFFFUL;
567+ sum += b;
568+ sum += b << sh;
569+ sh++;
570+ if (sh > 24) {
571+
572+ sh = 0;
573+ }
574+ }
575+
576+ return sum;
577+}
578+
579+/****************************************************************//**
580+Read one bitmap data page and check it for corruption.
581+
582+@return TRUE if page read OK, FALSE if I/O error */
583+static
584+ibool
585+log_online_read_bitmap_page(
586+/*========================*/
587+ log_online_bitmap_file_t *bitmap_file, /*!<in/out: bitmap
588+ file */
589+ byte *page, /*!<out: read page. Must be at
590+ least MODIFIED_PAGE_BLOCK_SIZE
591+ bytes long */
592+ ibool *checksum_ok) /*!<out: TRUE if page
593+ checksum OK */
594+{
595+ ulint checksum;
596+ ulint actual_checksum;
597+ ibool success;
598+
599+ ut_a(bitmap_file->size >= MODIFIED_PAGE_BLOCK_SIZE);
600+ ut_a(bitmap_file->offset
601+ <= bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE);
602+ ut_a(bitmap_file->offset % MODIFIED_PAGE_BLOCK_SIZE == 0);
603+
604+ success = xb_os_file_read(bitmap_file->file, page, bitmap_file->offset,
605+ MODIFIED_PAGE_BLOCK_SIZE);
606+
607+ if (UNIV_UNLIKELY(!success)) {
608+
609+ /* The following call prints an error message */
610+ os_file_get_last_error(TRUE);
611+ msg("InnoDB: Warning: failed reading changed page bitmap "
612+ "file \'%s\'\n", bitmap_file->name);
613+ return FALSE;
614+ }
615+
616+ bitmap_file->offset += MODIFIED_PAGE_BLOCK_SIZE;
617+ ut_ad(bitmap_file->offset <= bitmap_file->size);
618+
619+ checksum = mach_read_from_4(page + MODIFIED_PAGE_BLOCK_CHECKSUM);
620+ actual_checksum = log_online_calc_checksum(page);
621+ *checksum_ok = (checksum == actual_checksum);
622+
623+ return TRUE;
624+}
625+
626+/*********************************************************************//**
627+Check the name of a given file if it's a changed page bitmap file and
628+return file sequence and start LSN name components if it is. If is not,
629+the values of output parameters are undefined.
630+
631+@return TRUE if a given file is a changed page bitmap file. */
632+static
633+ibool
634+log_online_is_bitmap_file(
635+/*======================*/
636+ const os_file_stat_t* file_info, /*!<in: file to
637+ check */
638+ ulong* bitmap_file_seq_num, /*!<out: bitmap file
639+ sequence number */
640+ lsn_t* bitmap_file_start_lsn) /*!<out: bitmap file
641+ start LSN */
642+{
643+ char stem[FN_REFLEN];
644+
645+ ut_ad (strlen(file_info->name) < OS_FILE_MAX_PATH);
646+
647+ return ((file_info->type == OS_FILE_TYPE_FILE
648+ || file_info->type == OS_FILE_TYPE_LINK)
649+ && (sscanf(file_info->name, "%[a-z_]%lu_" LSN_PF ".xdb", stem,
650+ bitmap_file_seq_num, bitmap_file_start_lsn) == 3)
651+ && (!strcmp(stem, bmp_file_name_stem)));
652+}
653+
654+/*********************************************************************//**
655+List the bitmap files in srv_data_home and setup their range that contains the
656+specified LSN interval. This range, if non-empty, will start with a file that
657+has the greatest LSN equal to or less than the start LSN and will include all
658+the files up to the one with the greatest LSN less than the end LSN. Caller
659+must free bitmap_files->files when done if bitmap_files set to non-NULL and
660+this function returned TRUE. Field bitmap_files->count might be set to a
661+larger value than the actual count of the files, and space for the unused array
662+slots will be allocated but cleared to zeroes.
663+
664+@return TRUE if succeeded
665+*/
666+static
667+ibool
668+log_online_setup_bitmap_file_range(
669+/*===============================*/
670+ log_online_bitmap_file_range_t *bitmap_files, /*!<in/out: bitmap file
671+ range */
672+ lsn_t range_start, /*!<in: start LSN */
673+ lsn_t range_end) /*!<in: end LSN */
674+{
675+ os_file_dir_t bitmap_dir;
676+ os_file_stat_t bitmap_dir_file_info;
677+ ulong first_file_seq_num = ULONG_MAX;
678+ ulong last_file_seq_num = 0;
679+ lsn_t first_file_start_lsn = 0;
680+
681+ xb_ad(range_end >= range_start);
682+
683+ bitmap_files->count = 0;
684+ bitmap_files->files = NULL;
685+
686+ /* 1st pass: size the info array */
687+
688+ bitmap_dir = os_file_opendir(srv_data_home, FALSE);
689+ if (UNIV_UNLIKELY(!bitmap_dir)) {
690+
691+ msg("InnoDB: Error: failed to open bitmap directory \'%s\'\n",
692+ srv_data_home);
693+ return FALSE;
694+ }
695+
696+ while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
697+ &bitmap_dir_file_info)) {
698+
699+ ulong file_seq_num;
700+ lsn_t file_start_lsn;
701+
702+ if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
703+ &file_seq_num,
704+ &file_start_lsn)
705+ || file_start_lsn >= range_end) {
706+
707+ continue;
708+ }
709+
710+ if (file_seq_num > last_file_seq_num) {
711+
712+ last_file_seq_num = file_seq_num;
713+ }
714+
715+ if (file_start_lsn >= range_start
716+ || file_start_lsn == first_file_start_lsn
717+ || first_file_start_lsn > range_start) {
718+
719+ /* A file that falls into the range */
720+
721+ if (file_start_lsn < first_file_start_lsn) {
722+
723+ first_file_start_lsn = file_start_lsn;
724+ }
725+ if (file_seq_num < first_file_seq_num) {
726+
727+ first_file_seq_num = file_seq_num;
728+ }
729+ } else if (file_start_lsn > first_file_start_lsn) {
730+
731+ /* A file that has LSN closer to the range start
732+ but smaller than it, replacing another such file */
733+ first_file_start_lsn = file_start_lsn;
734+ first_file_seq_num = file_seq_num;
735+ }
736+ }
737+
738+ if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
739+
740+ os_file_get_last_error(TRUE);
741+ msg("InnoDB: Error: cannot close \'%s\'\n",srv_data_home);
742+ return FALSE;
743+ }
744+
745+ if (first_file_seq_num == ULONG_MAX && last_file_seq_num == 0) {
746+
747+ bitmap_files->count = 0;
748+ return TRUE;
749+ }
750+
751+ bitmap_files->count = last_file_seq_num - first_file_seq_num + 1;
752+
753+ /* 2nd pass: get the file names in the file_seq_num order */
754+
755+ bitmap_dir = os_file_opendir(srv_data_home, FALSE);
756+ if (UNIV_UNLIKELY(!bitmap_dir)) {
757+
758+ msg("InnoDB: Error: failed to open bitmap directory \'%s\'\n",
759+ srv_data_home);
760+ return FALSE;
761+ }
762+
763+ bitmap_files->files =
764+ static_cast<log_online_bitmap_file_range_t::files_t *>
765+ (ut_malloc(bitmap_files->count
766+ * sizeof(bitmap_files->files[0])));
767+ memset(bitmap_files->files, 0,
768+ bitmap_files->count * sizeof(bitmap_files->files[0]));
769+
770+ while (!os_file_readdir_next_file(srv_data_home, bitmap_dir,
771+ &bitmap_dir_file_info)) {
772+
773+ ulong file_seq_num;
774+ lsn_t file_start_lsn;
775+ size_t array_pos;
776+
777+ if (!log_online_is_bitmap_file(&bitmap_dir_file_info,
778+ &file_seq_num,
779+ &file_start_lsn)
780+ || file_start_lsn >= range_end
781+ || file_start_lsn < first_file_start_lsn) {
782+
783+ continue;
784+ }
785+
786+ array_pos = file_seq_num - first_file_seq_num;
787+ xb_a(array_pos < bitmap_files->count);
788+ if (file_seq_num > bitmap_files->files[array_pos].seq_num) {
789+
790+ bitmap_files->files[array_pos].seq_num = file_seq_num;
791+ strncpy(bitmap_files->files[array_pos].name,
792+ bitmap_dir_file_info.name, FN_REFLEN);
793+ bitmap_files->files[array_pos].name[FN_REFLEN - 1]
794+ = '\0';
795+ bitmap_files->files[array_pos].start_lsn
796+ = file_start_lsn;
797+ }
798+ }
799+
800+ if (UNIV_UNLIKELY(os_file_closedir(bitmap_dir))) {
801+
802+ os_file_get_last_error(TRUE);
803+ msg("InnoDB: Error: cannot close \'%s\'\n", srv_data_home);
804+ free(bitmap_files->files);
805+ return FALSE;
806+ }
807+
808+#ifdef UNIV_DEBUG
809+ ut_ad(bitmap_files->files[0].seq_num == first_file_seq_num);
810+ ut_ad(bitmap_files->files[0].start_lsn == first_file_start_lsn);
811+
812+ for (size_t i = 1; i < bitmap_files->count; i++) {
813+ if (!bitmap_files->files[i].seq_num) {
814+
815+ break;
816+ }
817+ ut_ad(bitmap_files->files[i].seq_num
818+ > bitmap_files->files[i - 1].seq_num);
819+ ut_ad(bitmap_files->files[i].start_lsn
820+ >= bitmap_files->files[i - 1].start_lsn);
821+ }
822+#endif
823+
824+ return TRUE;
825+}
826+
827+/****************************************************************//**
828+Open a bitmap file for reading.
829+
830+@return TRUE if opened successfully */
831+static
832+ibool
833+log_online_open_bitmap_file_read_only(
834+/*==================================*/
835+ const char* name, /*!<in: bitmap file
836+ name without directory,
837+ which is assumed to be
838+ srv_data_home */
839+ log_online_bitmap_file_t* bitmap_file) /*!<out: opened bitmap
840+ file */
841+{
842+ ibool success = FALSE;
843+
844+ xb_ad(name[0] != '\0');
845+
846+ ut_snprintf(bitmap_file->name, FN_REFLEN, "%s%s", srv_data_home, name);
847+ bitmap_file->file
848+ = xb_file_create_no_error_handling(bitmap_file->name,
849+ OS_FILE_OPEN,
850+ OS_FILE_READ_ONLY,
851+ &success);
852+ if (UNIV_UNLIKELY(!success)) {
853+
854+ /* Here and below assume that bitmap file names do not
855+ contain apostrophes, thus no need for ut_print_filename(). */
856+ msg("InnoDB: Warning: error opening the changed page "
857+ "bitmap \'%s\'\n", bitmap_file->name);
858+ return FALSE;
859+ }
860+
861+ bitmap_file->size = os_file_get_size(bitmap_file->file);
862+ bitmap_file->offset = 0;
863+
864+#ifdef UNIV_LINUX
865+ posix_fadvise(bitmap_file->file, 0, 0, POSIX_FADV_SEQUENTIAL);
866+ posix_fadvise(bitmap_file->file, 0, 0, POSIX_FADV_NOREUSE);
867+#endif
868+
869+ return TRUE;
870+}
871+
872+/****************************************************************//**
873+Diagnose one or both of the following situations if we read close to
874+the end of bitmap file:
875+1) Warn if the remainder of the file is less than one page.
876+2) Error if we cannot read any more full pages but the last read page
877+did not have the last-in-run flag set.
878+
879+@return FALSE for the error */
880+static
881+ibool
882+log_online_diagnose_bitmap_eof(
883+/*===========================*/
884+ const log_online_bitmap_file_t* bitmap_file, /*!< in: bitmap file */
885+ ibool last_page_in_run)/*!< in: "last page in
886+ run" flag value in the
887+ last read page */
888+{
889+ /* Check if we are too close to EOF to read a full page */
890+ if ((bitmap_file->size < MODIFIED_PAGE_BLOCK_SIZE)
891+ || (bitmap_file->offset
892+ > bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE)) {
893+
894+ if (UNIV_UNLIKELY(bitmap_file->offset != bitmap_file->size)) {
895+
896+ /* If we are not at EOF and we have less than one page
897+ to read, it's junk. This error is not fatal in
898+ itself. */
899+
900+ msg("InnoDB: Warning: junk at the end of changed "
901+ "page bitmap file \'%s\'.\n", bitmap_file->name);
902+ }
903+
904+ if (UNIV_UNLIKELY(!last_page_in_run)) {
905+
906+ /* We are at EOF but the last read page did not finish
907+ a run */
908+ /* It's a "Warning" here because it's not a fatal error
909+ for the whole server */
910+ msg("InnoDB: Warning: changed page bitmap "
911+ "file \'%s\' does not contain a complete run "
912+ "at the end.\n", bitmap_file->name);
913+ return FALSE;
914+ }
915+ }
916+ return TRUE;
917+}
918+
919+/* End of copy-pasted definitions */
920+
921+/** Iterator structure over changed page bitmap */
922+struct xb_page_bitmap_range_struct {
923+ const xb_page_bitmap *bitmap; /* Bitmap with data */
924+ ulint space_id; /* Space id for this
925+ iterator */
926+ ulint bit_i; /* Bit index of the iterator
927+ position in the current page */
928+ const ib_rbt_node_t *bitmap_node; /* Current bitmap tree node */
929+ const byte *bitmap_page; /* Current bitmap page */
930+ ulint current_page_id;/* Current page id */
931+};
932+
933+/****************************************************************//**
934+Print a diagnostic message on missing bitmap data for an LSN range. */
935+static __attribute__((cold))
936+void
937+xb_msg_missing_lsn_data(
938+/*====================*/
939+ lsn_t missing_interval_start, /*!<in: interval start */
940+ lsn_t missing_interval_end) /*!<in: interval end */
941+{
942+ msg("xtrabackup: warning: changed page data missing for LSNs between "
943+ LSN_PF " and " LSN_PF "\n", missing_interval_start,
944+ missing_interval_end);
945+}
946+
947+/****************************************************************//**
948+Scan a bitmap file until data for a desired LSN or EOF is found and check that
949+the page before the starting one is not corrupted to ensure that the found page
950+indeed contains the very start of the desired LSN data. The caller must check
951+the page LSN values to determine if the bitmap file was scanned until the data
952+was found or until EOF. Page must be at least MODIFIED_PAGE_BLOCK_SIZE big.
953+
954+@return TRUE if the scan successful without corruption detected
955+*/
956+static
957+ibool
958+xb_find_lsn_in_bitmap_file(
959+/*=======================*/
960+ log_online_bitmap_file_t *bitmap_file, /*!<in/out: bitmap
961+ file */
962+ byte *page, /*!<in/out: last read
963+ bitmap page */
964+ lsn_t *page_end_lsn, /*!<out: end LSN of the
965+ last read page */
966+ lsn_t lsn) /*!<in: LSN to find */
967+{
968+ ibool last_page_ok = TRUE;
969+ ibool next_to_last_page_ok = TRUE;
970+
971+ xb_ad (bitmap_file->size >= MODIFIED_PAGE_BLOCK_SIZE);
972+
973+ *page_end_lsn = 0;
974+
975+ while ((*page_end_lsn <= lsn)
976+ && (bitmap_file->offset
977+ <= bitmap_file->size - MODIFIED_PAGE_BLOCK_SIZE)) {
978+
979+ next_to_last_page_ok = last_page_ok;
980+ if (!log_online_read_bitmap_page(bitmap_file, page,
981+ &last_page_ok)) {
982+
983+ return FALSE;
984+ }
985+
986+ *page_end_lsn = MACH_READ_64(page + MODIFIED_PAGE_END_LSN);
987+ }
988+
989+ /* We check two pages here because the last read page already contains
990+ the required LSN data. If the next to the last one page is corrupted,
991+ then we have no way of telling if that page contained the required LSN
992+ range data too */
993+ return last_page_ok && next_to_last_page_ok;
994+}
995+
996+/****************************************************************//**
997+Read the disk bitmap and build the changed page bitmap tree for the
998+LSN interval incremental_lsn to checkpoint_lsn_start.
999+
1000+@return the built bitmap tree or NULL if unable to read the full interval for
1001+any reason. */
1002+xb_page_bitmap*
1003+xb_page_bitmap_init(void)
1004+/*=====================*/
1005+{
1006+ log_online_bitmap_file_t bitmap_file;
1007+ lsn_t bmp_start_lsn = incremental_lsn;
1008+ lsn_t bmp_end_lsn = checkpoint_lsn_start;
1009+ byte page[MODIFIED_PAGE_BLOCK_SIZE];
1010+ lsn_t current_page_end_lsn;
1011+ xb_page_bitmap *result;
1012+ ibool last_page_in_run= FALSE;
1013+ log_online_bitmap_file_range_t bitmap_files;
1014+ size_t bmp_i;
1015+ ibool last_page_ok = TRUE;
1016+
1017+ if (UNIV_UNLIKELY(bmp_start_lsn > bmp_end_lsn)) {
1018+
1019+ msg("xtrabackup: incremental backup LSN " LSN_PF
1020+ " is larger than than the last checkpoint LSN " LSN_PF
1021+ "\n", bmp_start_lsn, bmp_end_lsn);
1022+ return NULL;
1023+ }
1024+
1025+ if (!log_online_setup_bitmap_file_range(&bitmap_files, bmp_start_lsn,
1026+ bmp_end_lsn)) {
1027+
1028+ return NULL;
1029+ }
1030+
1031+ /* Only accept no bitmap files returned if start LSN == end LSN */
1032+ if (bitmap_files.count == 0 && bmp_end_lsn != bmp_start_lsn) {
1033+
1034+ return NULL;
1035+ }
1036+
1037+ result = rbt_create(MODIFIED_PAGE_BLOCK_SIZE,
1038+ log_online_compare_bmp_keys);
1039+
1040+ if (bmp_start_lsn == bmp_end_lsn) {
1041+
1042+ /* Empty range - empty bitmap */
1043+ return result;
1044+ }
1045+
1046+ bmp_i = 0;
1047+
1048+ if (UNIV_UNLIKELY(bitmap_files.files[bmp_i].start_lsn
1049+ > bmp_start_lsn)) {
1050+
1051+ /* The 1st file does not have the starting LSN data */
1052+ xb_msg_missing_lsn_data(bmp_start_lsn,
1053+ bitmap_files.files[bmp_i].start_lsn);
1054+ rbt_free(result);
1055+ free(bitmap_files.files);
1056+ return NULL;
1057+ }
1058+
1059+ /* Skip any zero-sized files at the start */
1060+ while ((bmp_i < bitmap_files.count - 1)
1061+ && (bitmap_files.files[bmp_i].start_lsn
1062+ == bitmap_files.files[bmp_i + 1].start_lsn)) {
1063+
1064+ bmp_i++;
1065+ }
1066+
1067+ /* Is the 1st bitmap file missing? */
1068+ if (UNIV_UNLIKELY(bitmap_files.files[bmp_i].name[0] == '\0')) {
1069+
1070+ /* TODO: this is not the exact missing range */
1071+ xb_msg_missing_lsn_data(bmp_start_lsn, bmp_end_lsn);
1072+ rbt_free(result);
1073+ free(bitmap_files.files);
1074+ return NULL;
1075+ }
1076+
1077+ /* Open the 1st bitmap file */
1078+ if (UNIV_UNLIKELY(!log_online_open_bitmap_file_read_only(
1079+ bitmap_files.files[bmp_i].name,
1080+ &bitmap_file))) {
1081+
1082+ rbt_free(result);
1083+ free(bitmap_files.files);
1084+ return NULL;
1085+ }
1086+
1087+ /* If the 1st file is truncated, no data. Not merged with the case
1088+ below because zero-length file indicates not a corruption but missing
1089+ subsequent files instead. */
1090+ if (UNIV_UNLIKELY(bitmap_file.size < MODIFIED_PAGE_BLOCK_SIZE)) {
1091+
1092+ xb_msg_missing_lsn_data(bmp_start_lsn, bmp_end_lsn);
1093+ rbt_free(result);
1094+ free(bitmap_files.files);
1095+ os_file_close(bitmap_file.file);
1096+ return NULL;
1097+ }
1098+
1099+ /* Find the start of the required LSN range in the file */
1100+ if (UNIV_UNLIKELY(!xb_find_lsn_in_bitmap_file(&bitmap_file, page,
1101+ &current_page_end_lsn,
1102+ bmp_start_lsn))) {
1103+
1104+ msg("xtrabackup: Warning: changed page bitmap file "
1105+ "\'%s\' corrupted\n", bitmap_file.name);
1106+ rbt_free(result);
1107+ free(bitmap_files.files);
1108+ os_file_close(bitmap_file.file);
1109+ return NULL;
1110+ }
1111+
1112+ last_page_in_run
1113+ = mach_read_from_4(page + MODIFIED_PAGE_IS_LAST_BLOCK);
1114+
1115+ if (UNIV_UNLIKELY(!log_online_diagnose_bitmap_eof(&bitmap_file,
1116+ last_page_in_run))) {
1117+
1118+ rbt_free(result);
1119+ free(bitmap_files.files);
1120+ os_file_close(bitmap_file.file);
1121+ return NULL;
1122+ }
1123+
1124+ if (UNIV_UNLIKELY(current_page_end_lsn < bmp_start_lsn)) {
1125+
1126+ xb_msg_missing_lsn_data(current_page_end_lsn, bmp_start_lsn);
1127+ rbt_free(result);
1128+ free(bitmap_files.files);
1129+ os_file_close(bitmap_file.file);
1130+ return NULL;
1131+ }
1132+
1133+ /* 1st bitmap page found, add it to the tree. */
1134+ rbt_insert(result, page, page);
1135+
1136+ /* Read next pages/files until all required data is read */
1137+ while (last_page_ok
1138+ && (current_page_end_lsn < bmp_end_lsn
1139+ || (current_page_end_lsn == bmp_end_lsn
1140+ && !last_page_in_run))) {
1141+
1142+ ib_rbt_bound_t tree_search_pos;
1143+
1144+ /* If EOF, advance the file skipping over any empty files */
1145+ while (bitmap_file.size < MODIFIED_PAGE_BLOCK_SIZE
1146+ || (bitmap_file.offset
1147+ > bitmap_file.size - MODIFIED_PAGE_BLOCK_SIZE)) {
1148+
1149+ os_file_close(bitmap_file.file);
1150+
1151+ if (UNIV_UNLIKELY(
1152+ !log_online_diagnose_bitmap_eof(
1153+ &bitmap_file, last_page_in_run))) {
1154+
1155+ rbt_free(result);
1156+ free(bitmap_files.files);
1157+ return NULL;
1158+ }
1159+
1160+ bmp_i++;
1161+
1162+ if (UNIV_UNLIKELY(bmp_i == bitmap_files.count
1163+ || (bitmap_files.files[bmp_i].seq_num
1164+ == 0))) {
1165+
1166+ xb_msg_missing_lsn_data(current_page_end_lsn,
1167+ bmp_end_lsn);
1168+ rbt_free(result);
1169+ free(bitmap_files.files);
1170+ return NULL;
1171+ }
1172+
1173+ /* Is the next file missing? */
1174+ if (UNIV_UNLIKELY(bitmap_files.files[bmp_i].name[0]
1175+ == '\0')) {
1176+
1177+ /* TODO: this is not the exact missing range */
1178+ xb_msg_missing_lsn_data(bitmap_files.files
1179+ [bmp_i - 1].start_lsn,
1180+ bmp_end_lsn);
1181+ rbt_free(result);
1182+ free(bitmap_files.files);
1183+ return NULL;
1184+ }
1185+
1186+ if (UNIV_UNLIKELY(
1187+ !log_online_open_bitmap_file_read_only(
1188+ bitmap_files.files[bmp_i].name,
1189+ &bitmap_file))) {
1190+
1191+ rbt_free(result);
1192+ free(bitmap_files.files);
1193+ return NULL;
1194+ }
1195+ }
1196+
1197+ if (UNIV_UNLIKELY(
1198+ !log_online_read_bitmap_page(&bitmap_file, page,
1199+ &last_page_ok))) {
1200+
1201+ rbt_free(result);
1202+ free(bitmap_files.files);
1203+ os_file_close(bitmap_file.file);
1204+ return NULL;
1205+ }
1206+
1207+ if (UNIV_UNLIKELY(!last_page_ok)) {
1208+
1209+ msg("xtrabackup: warning: changed page bitmap file "
1210+ "\'%s\' corrupted.\n", bitmap_file.name);
1211+ rbt_free(result);
1212+ free(bitmap_files.files);
1213+ os_file_close(bitmap_file.file);
1214+ return NULL;
1215+ }
1216+
1217+ /* Merge the current page with an existing page or insert a new
1218+ page into the tree */
1219+
1220+ if (!rbt_search(result, &tree_search_pos, page)) {
1221+
1222+ /* Merge the bitmap pages */
1223+ byte *existing_page
1224+ = rbt_value(byte, tree_search_pos.last);
1225+ bitmap_word_t *bmp_word_1 = (bitmap_word_t *)
1226+ (existing_page + MODIFIED_PAGE_BLOCK_BITMAP);
1227+ bitmap_word_t *bmp_end = (bitmap_word_t *)
1228+ (existing_page + MODIFIED_PAGE_BLOCK_UNUSED_2);
1229+ bitmap_word_t *bmp_word_2 = (bitmap_word_t *)
1230+ (page + MODIFIED_PAGE_BLOCK_BITMAP);
1231+ while (bmp_word_1 < bmp_end) {
1232+
1233+ *bmp_word_1++ |= *bmp_word_2++;
1234+ }
1235+ xb_a (bmp_word_1 == bmp_end);
1236+ } else {
1237+
1238+ /* Add a new page */
1239+ rbt_add_node(result, &tree_search_pos, page);
1240+ }
1241+
1242+ current_page_end_lsn
1243+ = MACH_READ_64(page + MODIFIED_PAGE_END_LSN);
1244+ last_page_in_run
1245+ = mach_read_from_4(page + MODIFIED_PAGE_IS_LAST_BLOCK);
1246+ }
1247+
1248+ xb_a (current_page_end_lsn >= bmp_end_lsn);
1249+
1250+ free(bitmap_files.files);
1251+ os_file_close(bitmap_file.file);
1252+
1253+ return result;
1254+}
1255+
1256+/****************************************************************//**
1257+Free the bitmap tree. */
1258+void
1259+xb_page_bitmap_deinit(
1260+/*==================*/
1261+ xb_page_bitmap* bitmap) /*!<in/out: bitmap tree */
1262+{
1263+ if (bitmap) {
1264+
1265+ rbt_free(bitmap);
1266+ }
1267+}
1268+
1269+/****************************************************************//**
1270+Advance to the next bitmap page or setup the first bitmap page for the
1271+given bitmap range. Assumes that bitmap_range->bitmap_page has been
1272+already found/bumped by rbt_search()/rbt_next().
1273+
1274+@return FALSE if no more bitmap data for the range space ID */
1275+static
1276+ibool
1277+xb_page_bitmap_setup_next_page(
1278+/*===========================*/
1279+ xb_page_bitmap_range* bitmap_range) /*!<in/out: the bitmap range */
1280+{
1281+ ulint new_space_id;
1282+ ulint new_1st_page_id;
1283+
1284+ if (bitmap_range->bitmap_node == NULL) {
1285+
1286+ bitmap_range->current_page_id = ULINT_UNDEFINED;
1287+ return FALSE;
1288+ }
1289+
1290+ bitmap_range->bitmap_page = rbt_value(byte, bitmap_range->bitmap_node);
1291+
1292+ new_space_id = mach_read_from_4(bitmap_range->bitmap_page
1293+ + MODIFIED_PAGE_SPACE_ID);
1294+ if (new_space_id != bitmap_range->space_id) {
1295+
1296+ /* No more data for the current page id. */
1297+ xb_a(new_space_id > bitmap_range->space_id);
1298+ bitmap_range->current_page_id = ULINT_UNDEFINED;
1299+ return FALSE;
1300+ }
1301+
1302+ new_1st_page_id = mach_read_from_4(bitmap_range->bitmap_page +
1303+ MODIFIED_PAGE_1ST_PAGE_ID);
1304+ xb_a (new_1st_page_id >= bitmap_range->current_page_id
1305+ || bitmap_range->current_page_id == ULINT_UNDEFINED);
1306+
1307+ bitmap_range->current_page_id = new_1st_page_id;
1308+ bitmap_range->bit_i = 0;
1309+
1310+ return TRUE;
1311+}
1312+
1313+/****************************************************************//**
1314+Set up a new bitmap range iterator over a given space id changed
1315+pages in a given bitmap.
1316+
1317+@return bitmap range iterator */
1318+xb_page_bitmap_range*
1319+xb_page_bitmap_range_init(
1320+/*======================*/
1321+ xb_page_bitmap* bitmap, /*!< in: bitmap to iterate over */
1322+ ulint space_id) /*!< in: space id */
1323+{
1324+ byte search_page[MODIFIED_PAGE_BLOCK_SIZE];
1325+ xb_page_bitmap_range *result
1326+ = static_cast<xb_page_bitmap_range *>
1327+ (ut_malloc(sizeof(*result)));
1328+
1329+ memset(result, 0, sizeof(*result));
1330+ result->bitmap = bitmap;
1331+ result->space_id = space_id;
1332+ result->current_page_id = ULINT_UNDEFINED;
1333+
1334+ /* Search for the 1st page for the given space id */
1335+ /* This also sets MODIFIED_PAGE_1ST_PAGE_ID to 0, which is what we
1336+ want. */
1337+ memset(search_page, 0, MODIFIED_PAGE_BLOCK_SIZE);
1338+ mach_write_to_4(search_page + MODIFIED_PAGE_SPACE_ID, space_id);
1339+
1340+ result->bitmap_node = rbt_lower_bound(result->bitmap, search_page);
1341+
1342+ xb_page_bitmap_setup_next_page(result);
1343+
1344+ return result;
1345+}
1346+
1347+/****************************************************************//**
1348+Get the value of the bitmap->range->bit_i bitmap bit
1349+
1350+@return the current bit value */
1351+static inline
1352+ibool
1353+is_bit_set(
1354+/*=======*/
1355+ const xb_page_bitmap_range* bitmap_range) /*!< in: bitmap
1356+ range */
1357+{
1358+ return ((*(((bitmap_word_t *)(bitmap_range->bitmap_page
1359+ + MODIFIED_PAGE_BLOCK_BITMAP))
1360+ + (bitmap_range->bit_i >> 6)))
1361+ & (1ULL << (bitmap_range->bit_i & 0x3F))) ? TRUE : FALSE;
1362+}
1363+
1364+/****************************************************************//**
1365+Get the next page id that has its bit set or cleared, i.e. equal to
1366+bit_value.
1367+
1368+@return page id */
1369+ulint
1370+xb_page_bitmap_range_get_next_bit(
1371+/*==============================*/
1372+ xb_page_bitmap_range* bitmap_range, /*!< in/out: bitmap range */
1373+ ibool bit_value) /*!< in: bit value */
1374+{
1375+ if (UNIV_UNLIKELY(bitmap_range->current_page_id
1376+ == ULINT_UNDEFINED)) {
1377+
1378+ return ULINT_UNDEFINED;
1379+ }
1380+
1381+ do {
1382+ while (bitmap_range->bit_i < MODIFIED_PAGE_BLOCK_ID_COUNT) {
1383+
1384+ while (is_bit_set(bitmap_range) != bit_value
1385+ && (bitmap_range->bit_i
1386+ < MODIFIED_PAGE_BLOCK_ID_COUNT)) {
1387+
1388+ bitmap_range->current_page_id++;
1389+ bitmap_range->bit_i++;
1390+ }
1391+
1392+ if (bitmap_range->bit_i
1393+ < MODIFIED_PAGE_BLOCK_ID_COUNT) {
1394+
1395+ ulint result = bitmap_range->current_page_id;
1396+ bitmap_range->current_page_id++;
1397+ bitmap_range->bit_i++;
1398+ return result;
1399+ }
1400+ }
1401+
1402+ bitmap_range->bitmap_node
1403+ = rbt_next(bitmap_range->bitmap,
1404+ bitmap_range->bitmap_node);
1405+
1406+ } while (xb_page_bitmap_setup_next_page(bitmap_range));
1407+
1408+ return ULINT_UNDEFINED;
1409+}
1410+
1411+/****************************************************************//**
1412+Free the bitmap range iterator. */
1413+void
1414+xb_page_bitmap_range_deinit(
1415+/*========================*/
1416+ xb_page_bitmap_range* bitmap_range) /*! in/out: bitmap range */
1417+{
1418+ ut_free(bitmap_range);
1419+}
1420
1421=== added file 'src/changed_page_bitmap.h'
1422--- src/changed_page_bitmap.h 1970-01-01 00:00:00 +0000
1423+++ src/changed_page_bitmap.h 2013-04-16 17:44:27 +0000
1424@@ -0,0 +1,84 @@
1425+/******************************************************
1426+XtraBackup: hot backup tool for InnoDB
1427+(c) 2009-2012 Percona Inc.
1428+Originally Created 3/3/2009 Yasufumi Kinoshita
1429+Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
1430+Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
1431+
1432+This program is free software; you can redistribute it and/or modify
1433+it under the terms of the GNU General Public License as published by
1434+the Free Software Foundation; version 2 of the License.
1435+
1436+This program is distributed in the hope that it will be useful,
1437+but WITHOUT ANY WARRANTY; without even the implied warranty of
1438+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1439+GNU General Public License for more details.
1440+
1441+You should have received a copy of the GNU General Public License
1442+along with this program; if not, write to the Free Software
1443+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1444+
1445+*******************************************************/
1446+
1447+/* Changed page bitmap interface */
1448+
1449+#ifndef XB_CHANGED_PAGE_BITMAP_H
1450+#define XB_CHANGED_PAGE_BITMAP_H
1451+
1452+#include "innodb_int.h"
1453+
1454+/* The changed page bitmap structure */
1455+typedef ib_rbt_t xb_page_bitmap;
1456+
1457+struct xb_page_bitmap_range_struct;
1458+
1459+/* The bitmap range iterator over one space id */
1460+typedef struct xb_page_bitmap_range_struct xb_page_bitmap_range;
1461+
1462+/****************************************************************//**
1463+Read the disk bitmap and build the changed page bitmap tree for the
1464+LSN interval incremental_lsn to checkpoint_lsn_start.
1465+
1466+@return the built bitmap tree */
1467+xb_page_bitmap*
1468+xb_page_bitmap_init(void);
1469+/*=====================*/
1470+
1471+/****************************************************************//**
1472+Free the bitmap tree. */
1473+void
1474+xb_page_bitmap_deinit(
1475+/*==================*/
1476+ xb_page_bitmap* bitmap); /*!<in/out: bitmap tree */
1477+
1478+
1479+/****************************************************************//**
1480+Set up a new bitmap range iterator over a given space id changed
1481+pages in a given bitmap.
1482+
1483+@return bitmap range iterator */
1484+xb_page_bitmap_range*
1485+xb_page_bitmap_range_init(
1486+/*======================*/
1487+ xb_page_bitmap* bitmap, /*!< in: bitmap to iterate over */
1488+ ulint space_id); /*!< in: space id */
1489+
1490+/****************************************************************//**
1491+Get the next page id that has its bit set or cleared, i.e. equal to
1492+bit_value.
1493+
1494+@return page id */
1495+ulint
1496+xb_page_bitmap_range_get_next_bit(
1497+/*==============================*/
1498+ xb_page_bitmap_range* bitmap_range, /*!< in/out: bitmap range */
1499+ ibool bit_value); /*!< in: bit value */
1500+
1501+/****************************************************************//**
1502+Free the bitmap range iterator. */
1503+void
1504+xb_page_bitmap_range_deinit(
1505+/*========================*/
1506+ xb_page_bitmap_range* bitmap_range); /*! in/out: bitmap range */
1507+
1508+#endif
1509
1510=== modified file 'src/compact.cc'
1511--- src/compact.cc 2013-03-22 09:14:31 +0000
1512+++ src/compact.cc 2013-04-16 17:44:27 +0000
1513@@ -443,7 +443,7 @@
1514
1515 ds_set_pipe(ds_buffer, ds_local);
1516
1517- res = xb_fil_cur_open(&cursor, node, 1);
1518+ res = xb_fil_cur_open(&cursor, &rf_pass_through, node, 1);
1519 xb_a(res == XB_FIL_CUR_SUCCESS);
1520
1521 snprintf(tmpfile_path, sizeof(tmpfile_path), "%s%s",
1522
1523=== modified file 'src/fil_cur.cc'
1524--- src/fil_cur.cc 2013-03-22 09:14:31 +0000
1525+++ src/fil_cur.cc 2013-04-16 17:44:27 +0000
1526@@ -26,13 +26,14 @@
1527 #include "innodb_int.h"
1528 #include "fil_cur.h"
1529 #include "common.h"
1530+#include "read_filt.h"
1531 #include "xtrabackup.h"
1532
1533 /* Size of read buffer in pages */
1534 #define XB_FIL_CUR_PAGES 64
1535
1536 /************************************************************************
1537-Open a source file cursor.
1538+Open a source file cursor and initialize the associated read filter.
1539
1540 @return XB_FIL_CUR_SUCCESS on success, XB_FIL_CUR_SKIP if the source file must
1541 be skipped and XB_FIL_CUR_ERROR on error. */
1542@@ -40,6 +41,7 @@
1543 xb_fil_cur_open(
1544 /*============*/
1545 xb_fil_cur_t* cursor, /*!< out: source file cursor */
1546+ xb_read_filt_t* read_filter, /*!< in/out: the read filter */
1547 fil_node_t* node, /*!< in: source tablespace node */
1548 uint thread_n) /*!< thread number for diagnostics */
1549 {
1550@@ -131,7 +133,6 @@
1551 cursor->buf = static_cast<byte *>
1552 (ut_align(cursor->orig_buf, UNIV_PAGE_SIZE));
1553
1554- cursor->offset = 0;
1555 cursor->buf_read = 0;
1556 cursor->buf_npages = 0;
1557 cursor->buf_offset = 0;
1558@@ -140,6 +141,10 @@
1559
1560 cursor->space_size = cursor->statinfo.st_size / page_size;
1561
1562+ cursor->read_filter = read_filter;
1563+ cursor->read_filter->init(&cursor->read_filter_ctxt, cursor,
1564+ node->space->id);
1565+
1566 return(XB_FIL_CUR_SUCCESS);
1567 }
1568
1569@@ -155,17 +160,16 @@
1570 xb_fil_cur_t* cursor) /*!< in/out: source file cursor */
1571 {
1572 ibool success;
1573- ulint page_size;
1574 byte* page;
1575 ulint i;
1576 ulint npages;
1577 ulint retry_count;
1578 xb_fil_cur_result_t ret;
1579+ ib_int64_t offset;
1580 ib_int64_t to_read;
1581
1582- page_size = cursor->page_size;
1583-
1584- to_read = (ib_int64_t) cursor->statinfo.st_size - cursor->offset;
1585+ cursor->read_filter->get_next_batch(&cursor->read_filter_ctxt,
1586+ &offset, &to_read);
1587
1588 if (to_read == 0LL) {
1589 return(XB_FIL_CUR_EOF);
1590@@ -174,8 +178,8 @@
1591 if (to_read > (ib_int64_t) cursor->buf_size) {
1592 to_read = (ib_int64_t) cursor->buf_size;
1593 }
1594- ut_a(to_read > 0 && to_read <= 0xFFFFFFFFLL);
1595- ut_a(to_read % page_size == 0);
1596+ xb_a(to_read > 0 && to_read <= 0xFFFFFFFFLL);
1597+ xb_a(to_read % cursor->page_size == 0);
1598
1599 npages = (ulint) (to_read >> cursor->page_size_shift);
1600
1601@@ -187,19 +191,18 @@
1602
1603 cursor->buf_read = 0;
1604 cursor->buf_npages = 0;
1605- cursor->buf_offset = cursor->offset;
1606- cursor->buf_page_no = (ulint) (cursor->offset >>
1607- cursor->page_size_shift);
1608+ cursor->buf_offset = offset;
1609+ cursor->buf_page_no = (ulint) (offset >> cursor->page_size_shift);
1610
1611- success = xb_os_file_read(cursor->file, cursor->buf, cursor->offset,
1612- to_read);
1613+ success = xb_os_file_read(cursor->file, cursor->buf, offset, to_read);
1614 if (!success) {
1615 return(XB_FIL_CUR_ERROR);
1616 }
1617
1618 /* check pages for corruption and re-read if necessary. i.e. in case of
1619 partially written pages */
1620- for (page = cursor->buf, i = 0; i < npages; page += page_size, i++) {
1621+ for (page = cursor->buf, i = 0; i < npages;
1622+ page += cursor->page_size, i++) {
1623 if (xb_buf_page_is_corrupted(page, cursor->zip_size))
1624 {
1625 ulint page_no = cursor->buf_page_no + i;
1626@@ -208,7 +211,7 @@
1627 page_no >= FSP_EXTENT_SIZE &&
1628 page_no < FSP_EXTENT_SIZE * 3) {
1629 /* skip doublewrite buffer pages */
1630- ut_a(page_size == UNIV_PAGE_SIZE);
1631+ xb_a(cursor->page_size == UNIV_PAGE_SIZE);
1632 msg("[%02u] xtrabackup: "
1633 "Page %lu is a doublewrite buffer page, "
1634 "skipping.\n", cursor->thread_n, page_no);
1635@@ -233,24 +236,25 @@
1636 goto read_retry;
1637 }
1638 }
1639- cursor->buf_read += page_size;
1640+ cursor->buf_read += cursor->page_size;
1641 cursor->buf_npages++;
1642 }
1643
1644 posix_fadvise(cursor->file, 0, 0, POSIX_FADV_DONTNEED);
1645
1646- cursor->offset += page_size * i;
1647-
1648 return(ret);
1649 }
1650
1651 /************************************************************************
1652-Close the source file cursor opened with xb_fil_cur_open(). */
1653+Close the source file cursor opened with xb_fil_cur_open() and its
1654+associated read filter. */
1655 void
1656 xb_fil_cur_close(
1657 /*=============*/
1658 xb_fil_cur_t *cursor) /*!< in/out: source file cursor */
1659 {
1660+ cursor->read_filter->deinit(&cursor->read_filter_ctxt);
1661+
1662 if (cursor->orig_buf != NULL) {
1663 ut_free(cursor->orig_buf);
1664 }
1665
1666=== modified file 'src/fil_cur.h'
1667--- src/fil_cur.h 2013-03-22 09:14:31 +0000
1668+++ src/fil_cur.h 2013-04-16 17:44:27 +0000
1669@@ -27,8 +27,9 @@
1670
1671 #include <my_dir.h>
1672 #include "innodb_int.h"
1673+#include "read_filt.h"
1674
1675-typedef struct {
1676+struct xb_fil_cur_t {
1677 os_file_t file; /*!< source file handle */
1678 char path[FN_REFLEN];/*!< normalized file path */
1679 MY_STAT statinfo; /*!< information about the file */
1680@@ -38,9 +39,11 @@
1681 UNIV_PAGE_SIZE for uncompressed ones */
1682 ulint page_size_shift;/*!< bit shift corresponding to
1683 page_size */
1684- ib_int64_t offset; /*!< current file offset in bytes */
1685 my_bool is_system; /*!< TRUE for system tablespace, FALSE
1686 otherwise */
1687+ xb_read_filt_t* read_filter; /*!< read filter */
1688+ xb_read_filt_ctxt_t read_filter_ctxt;
1689+ /*!< read filter context */
1690 byte* orig_buf; /*!< read buffer */
1691 byte* buf; /*!< aligned pointer for orig_buf */
1692 ulint buf_size; /*!< buffer size in bytes */
1693@@ -55,7 +58,7 @@
1694 uint thread_n; /*!< thread number for diagnostics */
1695 ulint space_id; /*!< ID of tablespace */
1696 ulint space_size; /*!< space size in pages */
1697-} xb_fil_cur_t;
1698+};
1699
1700 typedef enum {
1701 XB_FIL_CUR_SUCCESS,
1702@@ -65,7 +68,7 @@
1703 } xb_fil_cur_result_t;
1704
1705 /************************************************************************
1706-Open a source file cursor.
1707+Open a source file cursor and initialize the associated read filter.
1708
1709 @return XB_FIL_CUR_SUCCESS on success, XB_FIL_CUR_SKIP if the source file must
1710 be skipped and XB_FIL_CUR_ERROR on error. */
1711@@ -73,6 +76,7 @@
1712 xb_fil_cur_open(
1713 /*============*/
1714 xb_fil_cur_t* cursor, /*!< out: source file cursor */
1715+ xb_read_filt_t* read_filter, /*!< in/out: the read filter */
1716 fil_node_t* node, /*!< in: source tablespace node */
1717 uint thread_n); /*!< thread number for diagnostics */
1718
1719@@ -88,7 +92,8 @@
1720 xb_fil_cur_t* cursor); /*!< in/out: source file cursor */
1721
1722 /************************************************************************
1723-Close the source file cursor opened with xb_fil_cur_open(). */
1724+Close the source file cursor opened with xb_fil_cur_open() and its
1725+associated read filter. */
1726 void
1727 xb_fil_cur_close(
1728 /*=============*/
1729
1730=== modified file 'src/innodb_int.cc'
1731--- src/innodb_int.cc 2013-03-22 09:14:31 +0000
1732+++ src/innodb_int.cc 2013-04-16 17:44:27 +0000
1733@@ -199,7 +199,7 @@
1734 const char* name) /*!< in: space name */
1735 {
1736 fil_space_t* space;
1737- ulint fold;
1738+ ulint fold __attribute__((unused));
1739
1740 ut_ad(mutex_own(&fil_system->mutex));
1741
1742
1743=== modified file 'src/innodb_int.h'
1744--- src/innodb_int.h 2013-03-22 09:14:31 +0000
1745+++ src/innodb_int.h 2013-04-16 17:44:27 +0000
1746@@ -85,6 +85,7 @@
1747 #include <ut0crc32.h>
1748 #endif
1749 #include <ut0mem.h>
1750+#include <ut0rbt.h>
1751
1752 #if MYSQL_VERSION_ID < 50600
1753 }
1754@@ -166,6 +167,7 @@
1755 typedef ib_uint64_t lsn_t;
1756 typedef merge_index_def_t index_def_t;
1757 typedef merge_index_field_t index_field_t;
1758+ typedef struct ib_rbt_struct ib_rbt_t;
1759 # define xb_btr_pcur_open_at_index_side(from_left, index, latch_mode, pcur, \
1760 init_pcur, level, mtr) \
1761 btr_pcur_open_at_index_side(from_left, index, latch_mode, pcur, \
1762
1763=== added file 'src/read_filt.cc'
1764--- src/read_filt.cc 1970-01-01 00:00:00 +0000
1765+++ src/read_filt.cc 2013-04-16 17:44:27 +0000
1766@@ -0,0 +1,206 @@
1767+/******************************************************
1768+XtraBackup: hot backup tool for InnoDB
1769+(c) 2009-2012 Percona Inc.
1770+Originally Created 3/3/2009 Yasufumi Kinoshita
1771+Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
1772+Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
1773+
1774+This program is free software; you can redistribute it and/or modify
1775+it under the terms of the GNU General Public License as published by
1776+the Free Software Foundation; version 2 of the License.
1777+
1778+This program is distributed in the hope that it will be useful,
1779+but WITHOUT ANY WARRANTY; without even the implied warranty of
1780+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1781+GNU General Public License for more details.
1782+
1783+You should have received a copy of the GNU General Public License
1784+along with this program; if not, write to the Free Software
1785+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1786+
1787+*******************************************************/
1788+
1789+/* Data file read filter implementation */
1790+
1791+#include "read_filt.h"
1792+#include "common.h"
1793+#include "fil_cur.h"
1794+#include "xtrabackup.h"
1795+
1796+/****************************************************************//**
1797+Perform read filter context initialization that is common to all read
1798+filters. */
1799+static
1800+void
1801+common_init(
1802+/*========*/
1803+ xb_read_filt_ctxt_t* ctxt, /*!<in/out: read filter context */
1804+ const xb_fil_cur_t* cursor) /*!<in: file cursor */
1805+{
1806+ ctxt->offset = 0;
1807+ ctxt->data_file_size = cursor->statinfo.st_size;
1808+ ctxt->buffer_capacity = cursor->buf_size;
1809+ ctxt->page_size = cursor->page_size;
1810+}
1811+
1812+/****************************************************************//**
1813+Initialize the pass-through read filter. */
1814+static
1815+void
1816+rf_pass_through_init(
1817+/*=================*/
1818+ xb_read_filt_ctxt_t* ctxt, /*!<in/out: read filter context */
1819+ const xb_fil_cur_t* cursor, /*!<in: file cursor */
1820+ ulint space_id __attribute__((unused)))
1821+ /*!<in: space id we are reading */
1822+{
1823+ common_init(ctxt, cursor);
1824+}
1825+
1826+/****************************************************************//**
1827+Get the next batch of pages for the pass-through read filter. */
1828+static
1829+void
1830+rf_pass_through_get_next_batch(
1831+/*===========================*/
1832+ xb_read_filt_ctxt_t* ctxt, /*!<in/out: read filter
1833+ context */
1834+ ib_int64_t* read_batch_start, /*!<out: starting read
1835+ offset in bytes for the
1836+ next batch of pages */
1837+ ib_int64_t* read_batch_len) /*!<out: length in
1838+ bytes of the next batch
1839+ of pages */
1840+{
1841+ *read_batch_start = ctxt->offset;
1842+ *read_batch_len = ctxt->data_file_size - ctxt->offset;
1843+
1844+ if (*read_batch_len > ctxt->buffer_capacity) {
1845+ *read_batch_len = ctxt->buffer_capacity;
1846+ }
1847+
1848+ ctxt->offset += *read_batch_len;
1849+}
1850+
1851+/****************************************************************//**
1852+Deinitialize the pass-through read filter. */
1853+static
1854+void
1855+rf_pass_through_deinit(
1856+/*===================*/
1857+ xb_read_filt_ctxt_t* ctxt __attribute__((unused)))
1858+ /*!<in: read filter context */
1859+{
1860+}
1861+
1862+/****************************************************************//**
1863+Initialize the changed page bitmap-based read filter. Assumes that
1864+the bitmap is already set up in changed_page_bitmap. */
1865+static
1866+void
1867+rf_bitmap_init(
1868+/*===========*/
1869+ xb_read_filt_ctxt_t* ctxt, /*!<in/out: read filter
1870+ context */
1871+ const xb_fil_cur_t* cursor, /*!<in: read cursor */
1872+ ulint space_id) /*!<in: space id */
1873+{
1874+ common_init(ctxt, cursor);
1875+ ctxt->bitmap_range = xb_page_bitmap_range_init(changed_page_bitmap,
1876+ space_id);
1877+ ctxt->filter_batch_end = 0;
1878+}
1879+
1880+/****************************************************************//**
1881+Get the next batch of pages for the bitmap read filter. */
1882+static
1883+void
1884+rf_bitmap_get_next_batch(
1885+/*=====================*/
1886+ xb_read_filt_ctxt_t* ctxt, /*!<in/out: read filter
1887+ context */
1888+ ib_int64_t* read_batch_start, /*!<out: starting read
1889+ offset in bytes for the
1890+ next batch of pages */
1891+ ib_int64_t* read_batch_len) /*!<out: length in
1892+ bytes of the next batch
1893+ of pages */
1894+{
1895+ ulint start_page_id;
1896+
1897+ start_page_id = ctxt->offset / ctxt->page_size;
1898+
1899+ xb_a (ctxt->offset % ctxt->page_size == 0);
1900+
1901+ if (start_page_id == ctxt->filter_batch_end) {
1902+
1903+ /* Used up all the previous bitmap range, get some more */
1904+ ulint next_page_id;
1905+
1906+ /* Find the next changed page using the bitmap */
1907+ next_page_id = xb_page_bitmap_range_get_next_bit
1908+ (ctxt->bitmap_range, TRUE);
1909+
1910+ if (next_page_id == ULINT_UNDEFINED) {
1911+ *read_batch_len = 0;
1912+ return;
1913+ }
1914+
1915+ ctxt->offset = next_page_id * ctxt->page_size;
1916+
1917+ /* Find the end of the current changed page block by searching
1918+ for the next cleared bitmap bit */
1919+ ctxt->filter_batch_end
1920+ = xb_page_bitmap_range_get_next_bit(ctxt->bitmap_range,
1921+ FALSE);
1922+ xb_a(next_page_id < ctxt->filter_batch_end);
1923+ }
1924+
1925+ *read_batch_start = ctxt->offset;
1926+ if (ctxt->filter_batch_end == ULINT_UNDEFINED) {
1927+ /* No more cleared bits in the bitmap, need to copy all the
1928+ remaining pages. */
1929+ *read_batch_len = ctxt->data_file_size - ctxt->offset;
1930+ } else {
1931+ *read_batch_len = ctxt->filter_batch_end * ctxt->page_size
1932+ - ctxt->offset;
1933+ }
1934+
1935+ /* If the page block is larger than the buffer capacity, limit it to
1936+ buffer capacity. The subsequent invocations will continue returning
1937+ the current block in buffer-sized pieces until ctxt->filter_batch_end
1938+ is reached, trigerring the next bitmap query. */
1939+ if (*read_batch_len > ctxt->buffer_capacity) {
1940+ *read_batch_len = ctxt->buffer_capacity;
1941+ }
1942+
1943+ ctxt->offset += *read_batch_len;
1944+ xb_a (ctxt->offset % ctxt->page_size == 0);
1945+ xb_a (*read_batch_start % ctxt->page_size == 0);
1946+ xb_a (*read_batch_len % ctxt->page_size == 0);
1947+}
1948+
1949+/****************************************************************//**
1950+Deinitialize the changed page bitmap-based read filter. */
1951+static
1952+void
1953+rf_bitmap_deinit(
1954+/*=============*/
1955+ xb_read_filt_ctxt_t* ctxt) /*!<in/out: read filter context */
1956+{
1957+ xb_page_bitmap_range_deinit(ctxt->bitmap_range);
1958+}
1959+
1960+/* The pass-through read filter */
1961+xb_read_filt_t rf_pass_through = {
1962+ &rf_pass_through_init,
1963+ &rf_pass_through_get_next_batch,
1964+ &rf_pass_through_deinit
1965+};
1966+
1967+/* The changed page bitmap-based read filter */
1968+xb_read_filt_t rf_bitmap = {
1969+ &rf_bitmap_init,
1970+ &rf_bitmap_get_next_batch,
1971+ &rf_bitmap_deinit
1972+};
1973
1974=== added file 'src/read_filt.h'
1975--- src/read_filt.h 1970-01-01 00:00:00 +0000
1976+++ src/read_filt.h 2013-04-16 17:44:27 +0000
1977@@ -0,0 +1,63 @@
1978+/******************************************************
1979+XtraBackup: hot backup tool for InnoDB
1980+(c) 2009-2012 Percona Inc.
1981+Originally Created 3/3/2009 Yasufumi Kinoshita
1982+Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
1983+Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
1984+
1985+This program is free software; you can redistribute it and/or modify
1986+it under the terms of the GNU General Public License as published by
1987+the Free Software Foundation; version 2 of the License.
1988+
1989+This program is distributed in the hope that it will be useful,
1990+but WITHOUT ANY WARRANTY; without even the implied warranty of
1991+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1992+GNU General Public License for more details.
1993+
1994+You should have received a copy of the GNU General Public License
1995+along with this program; if not, write to the Free Software
1996+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1997+
1998+*******************************************************/
1999+
2000+/* Data file read filter interface */
2001+
2002+#ifndef XB_READ_FILT_H
2003+#define XB_READ_FILT_H
2004+
2005+#include "innodb_int.h"
2006+#include "changed_page_bitmap.h"
2007+
2008+struct xb_fil_cur_t;
2009+
2010+/* The read filter context */
2011+struct xb_read_filt_ctxt_t {
2012+ ib_int64_t offset; /*!< current file offset */
2013+ ib_int64_t data_file_size; /*!< data file size */
2014+ ib_int64_t buffer_capacity;/*!< read buffer capacity */
2015+ ulint space_id; /*!< space id */
2016+ /* The following fields used only in bitmap filter */
2017+ /* Move these to union if any other filters are added in future */
2018+ xb_page_bitmap_range *bitmap_range; /*!< changed page bitmap range
2019+ iterator for space_id */
2020+ ulint page_size; /*!< page size */
2021+ ulint filter_batch_end;/*!< the ending page id of the
2022+ current changed page block in
2023+ the bitmap */
2024+};
2025+
2026+/* The read filter */
2027+struct xb_read_filt_t {
2028+ void (*init)(xb_read_filt_ctxt_t* ctxt,
2029+ const xb_fil_cur_t* cursor,
2030+ ulint space_id);
2031+ void (*get_next_batch)(xb_read_filt_ctxt_t* ctxt,
2032+ ib_int64_t* read_batch_start,
2033+ ib_int64_t* read_batch_len);
2034+ void (*deinit)(xb_read_filt_ctxt_t* ctxt);
2035+};
2036+
2037+extern xb_read_filt_t rf_pass_through;
2038+extern xb_read_filt_t rf_bitmap;
2039+
2040+#endif
2041
2042=== modified file 'src/xtrabackup.cc'
2043--- src/xtrabackup.cc 2013-04-09 07:24:21 +0000
2044+++ src/xtrabackup.cc 2013-04-16 17:44:27 +0000
2045@@ -72,6 +72,13 @@
2046 #include "ds_buffer.h"
2047 #include "ds_tmpfile.h"
2048 #include "xbstream.h"
2049+#include "changed_page_bitmap.h"
2050+#include "read_filt.h"
2051+
2052+/* === File name constants === */
2053+#define XB_FN_SUSPENDED_AT_START "xtrabackup_suspended_1"
2054+#define XB_FN_SUSPENDED_AT_END "xtrabackup_suspended_2"
2055+#define XB_FN_LOG_COPIED "xtrabackup_log_copied"
2056
2057 my_bool innodb_inited= 0;
2058
2059@@ -87,6 +94,7 @@
2060 my_bool xtrabackup_export = FALSE;
2061 my_bool xtrabackup_apply_log_only = FALSE;
2062
2063+static my_bool xtrabackup_suspend_at_start = FALSE;
2064 my_bool xtrabackup_suspend_at_end = FALSE;
2065 longlong xtrabackup_use_memory = 100*1024*1024L;
2066 my_bool xtrabackup_create_ib_logfile = FALSE;
2067@@ -99,6 +107,7 @@
2068 lsn_t incremental_lsn;
2069 lsn_t incremental_to_lsn;
2070 lsn_t incremental_last_lsn;
2071+xb_page_bitmap *changed_page_bitmap = NULL;
2072
2073 char *xtrabackup_incremental_basedir = NULL; /* for --backup */
2074 char *xtrabackup_extra_lsndir = NULL; /* for --backup with --extra-lsndir */
2075@@ -256,6 +265,8 @@
2076 static my_bool xtrabackup_compact = FALSE;
2077 static my_bool xtrabackup_rebuild_indexes = FALSE;
2078
2079+static my_bool xtrabackup_incremental_force_scan = FALSE;
2080+
2081 /* Datasinks */
2082 ds_ctxt_t *ds_data = NULL;
2083 ds_ctxt_t *ds_meta = NULL;
2084@@ -356,6 +367,7 @@
2085 OPT_XTRA_EXPORT,
2086 OPT_XTRA_APPLY_LOG_ONLY,
2087 OPT_XTRA_PRINT_PARAM,
2088+ OPT_XTRA_SUSPEND_AT_START,
2089 OPT_XTRA_SUSPEND_AT_END,
2090 OPT_XTRA_USE_MEMORY,
2091 OPT_XTRA_THROTTLE,
2092@@ -430,6 +442,7 @@
2093 OPT_INNODB_CHECKSUM_ALGORITHM,
2094 OPT_UNDO_TABLESPACES,
2095 #endif
2096+ OPT_XTRA_INCREMENTAL_FORCE_SCAN,
2097 OPT_DEFAULTS_GROUP
2098 };
2099
2100@@ -485,9 +498,21 @@
2101 (G_PTR*) &xtrabackup_use_memory, (G_PTR*) &xtrabackup_use_memory,
2102 0, GET_LL, REQUIRED_ARG, 100*1024*1024L, 1024*1024L, LONGLONG_MAX, 0,
2103 1024*1024L, 0},
2104- {"suspend-at-end", OPT_XTRA_SUSPEND_AT_END, "creates a file 'xtrabackup_suspended' and waits until the user deletes that file at the end of '--backup'",
2105+
2106+ {"suspend-at-start", OPT_XTRA_SUSPEND_AT_START,
2107+ "creates a file '" XB_FN_SUSPENDED_AT_START "' and waits until the user "
2108+ "deletes that file after the background log copying thread is started "
2109+ "during backup",
2110+ (G_PTR*) &xtrabackup_suspend_at_start,
2111+ (G_PTR*) &xtrabackup_suspend_at_start, 0, GET_BOOL, NO_ARG,
2112+ 0, 0, 0, 0, 0, 0},
2113+
2114+ {"suspend-at-end", OPT_XTRA_SUSPEND_AT_END, "creates a file '"
2115+ XB_FN_SUSPENDED_AT_END "' and waits until the user deletes that file at "
2116+ "the end of '--backup'",
2117 (G_PTR*) &xtrabackup_suspend_at_end, (G_PTR*) &xtrabackup_suspend_at_end,
2118 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2119+
2120 {"throttle", OPT_XTRA_THROTTLE, "limit count of IO operations (pairs of read&write) per second to IOS values (for '--backup')",
2121 (G_PTR*) &xtrabackup_throttle, (G_PTR*) &xtrabackup_throttle,
2122 0, GET_LONG, REQUIRED_ARG, 0, 0, LONG_MAX, 0, 1, 0},
2123@@ -792,6 +817,13 @@
2124 0, GET_ULONG, REQUIRED_ARG, 0, 0, 126, 0, 1, 0},
2125 #endif
2126
2127+ {"incremental-force-scan", OPT_XTRA_INCREMENTAL_FORCE_SCAN,
2128+ "Perform a full-scan incremental backup even in the presence of changed "
2129+ "page bitmap data",
2130+ (G_PTR*)&xtrabackup_incremental_force_scan,
2131+ (G_PTR*)&xtrabackup_incremental_force_scan, 0, GET_BOOL, NO_ARG,
2132+ 0, 0, 0, 0, 0, 0},
2133+
2134 {"defaults_group", OPT_DEFAULTS_GROUP, "defaults group in config file (default \"mysqld\").",
2135 (G_PTR*) &defaults_group, (G_PTR*) &defaults_group,
2136 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
2137@@ -1747,6 +1779,7 @@
2138 xb_write_filt_t *write_filter = NULL;
2139 xb_write_filt_ctxt_t write_filt_ctxt;
2140 const char *action;
2141+ xb_read_filt_t *read_filter;
2142
2143 if (!trx_sys_sys_space(node->space->id) && /* don't skip system space */
2144 check_if_skip_table(node->name, "ibd")) {
2145@@ -1754,7 +1787,13 @@
2146 return(FALSE);
2147 }
2148
2149- res = xb_fil_cur_open(&cursor, node, thread_n);
2150+ if (!changed_page_bitmap) {
2151+ read_filter = &rf_pass_through;
2152+ }
2153+ else {
2154+ read_filter = &rf_bitmap;
2155+ }
2156+ res = xb_fil_cur_open(&cursor, read_filter, node, thread_n);
2157 if (res == XB_FIL_CUR_SKIP) {
2158 goto skip;
2159 } else if (res == XB_FIL_CUR_ERROR) {
2160@@ -2302,7 +2341,9 @@
2161 ds_data = ds;
2162
2163 if (xtrabackup_stream_fmt != XB_STREAM_FMT_XBSTREAM ||
2164- xtrabackup_suspend_at_end) {
2165+ xtrabackup_suspend_at_end ||
2166+ xtrabackup_suspend_at_start) {
2167+
2168 /* 'xbstream' allow parallel streams, but we
2169 still can't stream directly to stdout when
2170 xtrabackup is invoked from innobackupex
2171@@ -2481,20 +2522,34 @@
2172 }
2173
2174 /***********************************************************************
2175+Prepare a sync file path. */
2176+static void
2177+xb_make_sync_file_name(
2178+/*===================*/
2179+ const char* name, /*!<in: sync file name */
2180+ char* path) /*!<in/out: path to the sync file */
2181+{
2182+ snprintf(path, FN_REFLEN, "%s/%s", xtrabackup_target_dir, name);
2183+ srv_normalize_path_for_win(path);
2184+}
2185+
2186+/***********************************************************************
2187 Create an empty file with a given path and close it.
2188 @return TRUE on succees, FALSE on error. */
2189 static ibool
2190-xb_create_suspend_file(const char *path)
2191+xb_create_sync_file(
2192+/*================*/
2193+ const char* path) /*!<in: path to the sync file */
2194 {
2195- ibool success;
2196- os_file_t suspend_file = XB_FILE_UNDEFINED;
2197+ ibool success;
2198+ os_file_t suspend_file = XB_FILE_UNDEFINED;
2199
2200 /* xb_file_create reads srv_unix_file_flush_method */
2201 suspend_file = xb_file_create(path, OS_FILE_CREATE,
2202 OS_FILE_NORMAL, OS_DATA_FILE,
2203 &success);
2204
2205- if (success && suspend_file != XB_FILE_UNDEFINED) {
2206+ if (UNIV_LIKELY(success && suspend_file != XB_FILE_UNDEFINED)) {
2207
2208 os_file_close(suspend_file);
2209
2210@@ -2641,6 +2696,33 @@
2211 }
2212 }
2213
2214+/***********************************************************************
2215+Create a specified sync file and wait until it's removed. */
2216+static void
2217+xtrabackup_suspend(
2218+/*===============*/
2219+ const char* sync_fn) /*!<in: sync file name */
2220+{
2221+ char suspend_path[FN_REFLEN];
2222+ ibool success = TRUE;
2223+ ibool exists = TRUE;
2224+ os_file_type_t type;
2225+
2226+ xb_make_sync_file_name(sync_fn, suspend_path);
2227+
2228+ if (!xb_create_sync_file(suspend_path)) {
2229+ return;
2230+ }
2231+
2232+ while (exists && success) {
2233+ os_thread_sleep(200000); /*0.2 sec*/
2234+ success = os_file_status(suspend_path, &exists, &type);
2235+ /* success == FALSE if file exists, but stat() failed.
2236+ os_file_status() prints an error message in this case */
2237+ ut_a(success);
2238+ }
2239+}
2240+
2241 static void
2242 xtrabackup_backup_func(void)
2243 {
2244@@ -2650,8 +2732,6 @@
2245 uint count;
2246 os_ib_mutex_t count_mutex;
2247 data_thread_ctxt_t *data_threads;
2248- char suspend_path[FN_REFLEN];
2249- ibool success;
2250
2251 #ifdef USE_POSIX_FADVISE
2252 msg("xtrabackup: uses posix_fadvise().\n");
2253@@ -2939,6 +3019,25 @@
2254
2255 os_thread_create(log_copying_thread, NULL, &log_copying_thread_id);
2256
2257+ /* Suspend at start, for the FLUSH CHANGED_PAGE_BITMAPS call */
2258+ if (xtrabackup_suspend_at_start) {
2259+ xtrabackup_suspend(XB_FN_SUSPENDED_AT_START);
2260+ }
2261+
2262+ if (xtrabackup_incremental) {
2263+ if (!xtrabackup_incremental_force_scan) {
2264+ changed_page_bitmap = xb_page_bitmap_init();
2265+ }
2266+ if (!changed_page_bitmap) {
2267+ msg("xtrabackup: using the full scan for incremental "
2268+ "backup\n");
2269+ } else if (incremental_lsn != checkpoint_lsn_start) {
2270+ /* Do not print that bitmaps are used when dummy bitmap
2271+ is build for an empty LSN range. */
2272+ msg("xtrabackup: using the changed page bitmap\n");
2273+ }
2274+ }
2275+
2276 ut_a(xtrabackup_parallel > 0);
2277
2278 if (xtrabackup_parallel > 1) {
2279@@ -2982,29 +3081,14 @@
2280 ut_free(data_threads);
2281 datafiles_iter_free(it);
2282
2283+ if (changed_page_bitmap) {
2284+ xb_page_bitmap_deinit(changed_page_bitmap);
2285+ }
2286 }
2287
2288 /* suspend-at-end */
2289 if (xtrabackup_suspend_at_end) {
2290- ibool exists;
2291- os_file_type_t type;
2292-
2293- sprintf(suspend_path, "%s%s", xtrabackup_target_dir,
2294- "/xtrabackup_suspended");
2295- srv_normalize_path_for_win(suspend_path);
2296-
2297- if (!xb_create_suspend_file(suspend_path)) {
2298- exit(EXIT_FAILURE);
2299- }
2300-
2301- exists = TRUE;
2302- while (exists) {
2303- os_thread_sleep(200000); /*0.2 sec*/
2304- success = os_file_status(suspend_path, &exists, &type);
2305- /* success == FALSE if file exists, but stat() failed.
2306- os_file_status() prints an error message in this case */
2307- ut_a(success);
2308- }
2309+ xtrabackup_suspend(XB_FN_SUSPENDED_AT_END);
2310 }
2311
2312 /* read the latest checkpoint lsn */
2313@@ -3051,9 +3135,12 @@
2314 /* Signal innobackupex that log copying has stopped and it may now
2315 unlock tables, so we can possibly stream xtrabackup_logfile later
2316 without holding the lock. */
2317- if (xtrabackup_suspend_at_end &&
2318- !xb_create_suspend_file(suspend_path)) {
2319- exit(EXIT_FAILURE);
2320+ if (xtrabackup_suspend_at_end) {
2321+ char log_copied_sync_file[FN_REFLEN];
2322+ xb_make_sync_file_name(XB_FN_LOG_COPIED, log_copied_sync_file);
2323+ if (!xb_create_sync_file(log_copied_sync_file)) {
2324+ exit(EXIT_FAILURE);
2325+ }
2326 }
2327
2328 if(!xtrabackup_incremental) {
2329
2330=== modified file 'src/xtrabackup.h'
2331--- src/xtrabackup.h 2013-03-22 09:14:31 +0000
2332+++ src/xtrabackup.h 2013-04-16 17:44:27 +0000
2333@@ -23,6 +23,7 @@
2334
2335 #include "datasink.h"
2336 #include "innodb_int.h"
2337+#include "changed_page_bitmap.h"
2338
2339 typedef struct {
2340 ulint page_size;
2341@@ -46,6 +47,11 @@
2342 extern ds_ctxt_t *ds_meta;
2343 extern ds_ctxt_t *ds_data;
2344
2345+/* The last checkpoint LSN at the backup startup time */
2346+extern lsn_t checkpoint_lsn_start;
2347+
2348+extern xb_page_bitmap *changed_page_bitmap;
2349+
2350 void xtrabackup_io_throttling(void);
2351 my_bool xb_write_delta_metadata(const char *filename,
2352 const xb_delta_info_t *info);
2353
2354=== modified file 'test/inc/common.sh'
2355--- test/inc/common.sh 2013-03-22 09:14:31 +0000
2356+++ test/inc/common.sh 2013-04-16 17:44:27 +0000
2357@@ -189,6 +189,7 @@
2358 SRV_MYSQLD_DATADIR[$id]="$vardir/data"
2359 SRV_MYSQLD_TMPDIR[$id]="$vardir/tmp"
2360 SRV_MYSQLD_PIDFILE[$id]="${TEST_BASEDIR}/mysqld${id}.pid"
2361+ SRV_MYSQLD_ERRFILE[$id]="$vardir/data/mysqld${id}.err"
2362 SRV_MYSQLD_PORT[$id]=`get_free_port $id`
2363 SRV_MYSQLD_SOCKET[$id]=`mktemp -t xtrabackup.mysql.sock.XXXXXX`
2364 }
2365@@ -211,6 +212,7 @@
2366 SRV_MYSQLD_DATADIR[$id]=
2367 SRV_MYSQLD_TMPDIR[$id]=
2368 SRV_MYSQLD_PIDFILE[$id]=
2369+ SRV_MYSQLD_ERRFILE[$id]=
2370 SRV_MYSQLD_PORT[$id]=
2371 SRV_MYSQLD_SOCKET[$id]=
2372 }
2373@@ -227,11 +229,13 @@
2374 MYSQLD_DATADIR="${SRV_MYSQLD_DATADIR[$id]}"
2375 MYSQLD_TMPDIR="${SRV_MYSQLD_TMPDIR[$id]}"
2376 MYSQLD_PIDFILE="${SRV_MYSQLD_PIDFILE[$id]}"
2377+ MYSQLD_ERRFILE="${SRV_MYSQLD_ERRFILE[$id]}"
2378 MYSQLD_PORT="${SRV_MYSQLD_PORT[$id]}"
2379 MYSQLD_SOCKET="${SRV_MYSQLD_SOCKET[$id]}"
2380
2381 MYSQL_ARGS="--no-defaults --socket=${MYSQLD_SOCKET} --user=root"
2382 MYSQLD_ARGS="--no-defaults --basedir=${MYSQL_BASEDIR} \
2383+--log-error=${MYSQLD_ERRFILE}
2384 --socket=${MYSQLD_SOCKET} --port=${MYSQLD_PORT} --server-id=$id \
2385 --datadir=${MYSQLD_DATADIR} --tmpdir=${MYSQLD_TMPDIR} --log-bin=mysql-bin \
2386 --relay-log=mysql-relay-bin --pid-file=${MYSQLD_PIDFILE} ${MYSQLD_EXTRA_ARGS}"
2387@@ -455,5 +459,62 @@
2388 return ${PIPESTATUS[0]}
2389 }
2390
2391+readonly xb_performed_bmp_inc_backup="xtrabackup: using the full scan for incremental backup"
2392+readonly xb_performed_full_scan_inc_backup="xtrabackup: using the changed page bitmap"
2393+
2394+####################################################
2395+# Helper functions for testing incremental backups #
2396+####################################################
2397+function check_full_scan_inc_backup()
2398+{
2399+ if ! grep -q "$xb_performed_bmp_inc_backup" $OUTFILE ;
2400+ then
2401+ vlog "xtrabackup did not perform a full scan for the incremental backup."
2402+ exit -1
2403+ fi
2404+ if grep -q "$xb_performed_full_scan_inc_backup" $OUTFILE ;
2405+ then
2406+ vlog "xtrabackup appeared to use bitmaps instead of full scan for the incremental backup."
2407+ exit -1
2408+ fi
2409+}
2410+
2411+function check_bitmap_inc_backup()
2412+{
2413+ if ! grep -q "$xb_performed_full_scan_inc_backup" $OUTFILE ;
2414+ then
2415+ vlog "xtrabackup did not use bitmaps for the incremental backup."
2416+ exit -1
2417+ fi
2418+ if grep -q "$xb_performed_bmp_inc_backup" $OUTFILE ;
2419+ then
2420+ vlog "xtrabackup used a full scan instead of bitmaps for the incremental backup."
2421+ exit -1
2422+ fi
2423+}
2424+
2425+##############################################################
2426+# Helper functions for xtrabackup process suspend and resume #
2427+##############################################################
2428+function wait_for_xb_to_suspend()
2429+{
2430+ local file=$1
2431+ local i=0
2432+ echo "Waiting for $file to be created"
2433+ while [ ! -r $file ]
2434+ do
2435+ sleep 1
2436+ i=$((i+1))
2437+ echo "Waited $i seconds for xtrabackup_suspended to be created"
2438+ done
2439+}
2440+
2441+function resume_suspended_xb()
2442+{
2443+ local file=$1
2444+ echo "Removing $file"
2445+ rm -f $file
2446+}
2447+
2448 # To avoid unbound variable error when no server have been started
2449 SRV_MYSQLD_IDS=
2450
2451=== renamed file 'test/t/ib_incremental.sh' => 'test/inc/ib_incremental_common.sh'
2452--- test/t/ib_incremental.sh 2012-10-15 16:14:59 +0000
2453+++ test/inc/ib_incremental_common.sh 2013-04-16 17:44:27 +0000
2454@@ -1,6 +1,16 @@
2455+# Expects the following variable to be set before including:
2456+# mysqld_extra_args: an extra arg to be passed for all mysqld invocations.
2457+# Use this to set special options that influence
2458+# incremental backups, e.g. turns on log archiving or
2459+# changed page bitmap output.
2460+# ib_inc_extra_args: extra args to be passed to innobackup incremental
2461+# backup invocations.
2462+
2463 . inc/common.sh
2464
2465-start_server --innodb_file_per_table
2466+mysqld_extra_args="$mysqld_extra_args --innodb_file_per_table"
2467+
2468+start_server $mysqld_extra_args
2469 load_dbase_schema incremental_sample
2470
2471 # Adding initial rows
2472@@ -35,6 +45,18 @@
2473 ${MYSQL} ${MYSQL_ARGS} -e "insert into t2 values ($count, $numrow);" incremental_sample
2474 let "count=count+1"
2475 done
2476+
2477+# Rotate bitmap file here and force checkpoint at the same time
2478+shutdown_server
2479+start_server $mysqld_extra_args
2480+
2481+i=1001
2482+while [ "$i" -lt "7500" ]
2483+do
2484+ ${MYSQL} ${MYSQL_ARGS} -e "insert into t2 values ($i, repeat(\"ab\", 32500));" incremental_sample
2485+ let "i=i+1"
2486+done
2487+
2488 vlog "Changes done"
2489
2490 # Saving the checksum of original table
2491@@ -51,7 +73,7 @@
2492
2493 # Incremental backup
2494 innobackupex --incremental --incremental-basedir=$full_backup_dir \
2495- $topdir/backup
2496+ $topdir/backup $ib_inc_extra_args
2497 inc_backup_dir=`grep "innobackupex: Backup created in directory" $OUTFILE | tail -n 1 | awk -F\' '{print $2}'`
2498 vlog "Incremental backup done to directory $inc_backup_dir"
2499
2500@@ -87,7 +109,7 @@
2501 innobackupex --copy-back $full_backup_dir
2502 vlog "Data restored"
2503
2504-start_server --innodb_file_per_table
2505+start_server $mysqld_extra_args
2506
2507 vlog "Checking checksums"
2508 checksum_test_b=`checksum_table incremental_sample test`
2509
2510=== modified file 'test/t/bug1007446.sh'
2511--- test/t/bug1007446.sh 2012-12-26 23:48:57 +0000
2512+++ test/t/bug1007446.sh 2013-04-16 17:44:27 +0000
2513@@ -6,7 +6,7 @@
2514
2515 start_server
2516
2517-echo "" > $MYSQLD_TMPDIR/xtrabackup_suspended
2518+echo "" > $MYSQLD_TMPDIR/xtrabackup_suspended_1
2519
2520 mkdir -p $topdir/backup
2521 innobackupex --stream=tar $topdir/backup > /dev/null
2522
2523=== added file 'test/t/ib_incremental_bitmap.sh'
2524--- test/t/ib_incremental_bitmap.sh 1970-01-01 00:00:00 +0000
2525+++ test/t/ib_incremental_bitmap.sh 2013-04-16 17:44:27 +0000
2526@@ -0,0 +1,26 @@
2527+# Test for incremental backups that use changed page bitmaps
2528+
2529+if [ -z "$XTRADB_VERSION" ]; then
2530+ echo "Requires XtraDB" > $SKIPPED_REASON
2531+ exit $SKIPPED_EXIT_CODE
2532+fi
2533+
2534+# The test is disabled for Percona XtraDB Cluster until a version
2535+# with bitmap user requests is released (containing PS 5.5.29-30.0
2536+# or later)
2537+set +e
2538+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2539+probe_result=$?
2540+if [[ "$probe_result" == "0" ]]
2541+ then
2542+ vlog "Incompatible test" > $SKIPPED_REASON
2543+ exit $SKIPPED_EXIT_CODE
2544+fi
2545+set -e
2546+
2547+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2548+ib_inc_extra_args=
2549+
2550+. inc/ib_incremental_common.sh
2551+
2552+check_bitmap_inc_backup
2553
2554=== added file 'test/t/ib_incremental_force_full_scan.sh'
2555--- test/t/ib_incremental_force_full_scan.sh 1970-01-01 00:00:00 +0000
2556+++ test/t/ib_incremental_force_full_scan.sh 2013-04-16 17:44:27 +0000
2557@@ -0,0 +1,27 @@
2558+# Test for incremental backups that use forced full scan even when bitmaps are present
2559+
2560+if [ -z "$XTRADB_VERSION" ]; then
2561+ echo "Requires XtraDB" > $SKIPPED_REASON
2562+ exit $SKIPPED_EXIT_CODE
2563+fi
2564+
2565+# The test is disabled for Percona XtraDB Cluster until a version
2566+# with bitmap user requests is released (containing PS 5.5.29-30.0
2567+# or later)
2568+set +e
2569+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2570+probe_result=$?
2571+if [[ "$probe_result" == "0" ]]
2572+ then
2573+ vlog "Incompatible test" > $SKIPPED_REASON
2574+ exit $SKIPPED_EXIT_CODE
2575+fi
2576+set -e
2577+
2578+
2579+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2580+ib_inc_extra_args=--incremental-force-scan
2581+
2582+. inc/ib_incremental_common.sh
2583+
2584+check_full_scan_inc_backup
2585
2586=== added file 'test/t/ib_incremental_full_scan.sh'
2587--- test/t/ib_incremental_full_scan.sh 1970-01-01 00:00:00 +0000
2588+++ test/t/ib_incremental_full_scan.sh 2013-04-16 17:44:27 +0000
2589@@ -0,0 +1,8 @@
2590+# Test incremental backups that do full data file scans
2591+
2592+mysqld_extra_args=
2593+ib_inc_extra_args=
2594+
2595+. inc/ib_incremental_common.sh
2596+
2597+check_full_scan_inc_backup
2598
2599=== added file 'test/t/xb_incremental_bitmap_misc.sh'
2600--- test/t/xb_incremental_bitmap_misc.sh 1970-01-01 00:00:00 +0000
2601+++ test/t/xb_incremental_bitmap_misc.sh 2013-04-16 17:44:27 +0000
2602@@ -0,0 +1,76 @@
2603+# Test diagnostics for missing bitmap data and --incremental-force-scan option
2604+
2605+if [ -z "$XTRADB_VERSION" ]; then
2606+ echo "Requires XtraDB" > $SKIPPED_REASON
2607+ exit $SKIPPED_EXIT_CODE
2608+fi
2609+
2610+# The test is disabled for Percona XtraDB Cluster until a version
2611+# with bitmap user requests is released (containing PS 5.5.29-30.0
2612+# or later)
2613+set +e
2614+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2615+probe_result=$?
2616+if [[ "$probe_result" == "0" ]]
2617+ then
2618+ vlog "Incompatible test" > $SKIPPED_REASON
2619+ exit $SKIPPED_EXIT_CODE
2620+fi
2621+set -e
2622+
2623+. inc/common.sh
2624+
2625+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2626+
2627+start_server $mysqld_extra_args
2628+${MYSQL} ${MYSQL_ARGS} -e "CREATE TABLE t1 (a INT) ENGINE=INNODB" test
2629+${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO t1 VALUES (1)" test
2630+
2631+# Force checkpoint
2632+shutdown_server
2633+start_server $mysqld_extra_args
2634+
2635+# Full backup
2636+# mkdir $topdir/data/full
2637+# xtrabackup --datadir=$mysql_datadir --backup --target-dir=$topdir/data/inc1 --
2638+
2639+${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO t1 VALUES (2)" test
2640+# Force checkpoint
2641+shutdown_server
2642+start_server $mysqld_extra_args
2643+${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO t1 VALUES (3)" test
2644+# Force checkpoint
2645+shutdown_server
2646+start_server $mysqld_extra_args
2647+${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO t1 VALUES (4)" test
2648+# Force checkpoint
2649+shutdown_server
2650+start_server $mysqld_extra_args
2651+
2652+ls -l $mysql_datadir/*.xdb
2653+
2654+vlog "Test --incremental-force-scan option"
2655+xtrabackup --datadir=$mysql_datadir --backup \
2656+ --target-dir=$topdir/data/inc0 --incremental_lsn=9000 \
2657+ --incremental-force-scan
2658+
2659+vlog "Test nonsensical LSN range (start LSN > end LSN)"
2660+xtrabackup --datadir=$mysql_datadir --backup \
2661+ --target-dir=$topdir/data/inc1 --incremental-lsn=500000000000
2662+
2663+vlog "Test missing bitmap data diagnostics: middle of range"
2664+run_cmd rm $mysql_datadir/ib_modified_log_2*.xdb
2665+xtrabackup --datadir=$mysql_datadir --backup \
2666+ --target-dir=$topdir/data/inc2 --incremental-lsn=9000
2667+
2668+vlog "Test missing bitmap data diagnostics: end of range too"
2669+run_cmd rm $mysql_datadir/ib_modified_log_3*.xdb
2670+xtrabackup --datadir=$mysql_datadir --backup \
2671+ --target-dir=$topdir/data/inc3 --incremental-lsn=9000
2672+
2673+vlog "Test missing bitmap data diagnostics: start of range too"
2674+run_cmd rm $mysql_datadir/ib_modified_log_1*.xdb
2675+xtrabackup --datadir=$mysql_datadir --backup \
2676+ --target-dir=$topdir/data/inc4 --incremental-lsn=9000
2677+
2678+check_full_scan_inc_backup
2679
2680=== modified file 'test/t/xb_incremental_compressed.inc'
2681--- test/t/xb_incremental_compressed.inc 2012-11-08 03:44:14 +0000
2682+++ test/t/xb_incremental_compressed.inc 2013-04-16 17:44:27 +0000
2683@@ -1,13 +1,33 @@
2684 ################################################################################
2685 # Test incremental backup with InnoDB compression
2686 ################################################################################
2687+# Expects the following variable to be set before including:
2688+# mysqld_extra_args: an extra arg to be passed for all mysqld invocations.
2689+# Use this to set special options that influence
2690+# incremental backups, e.g. turns on log archiving or
2691+# changed page bitmap output.
2692+# first_inc_suspend_command: if non-empty string, passes --suspend-at-start
2693+# to incremental XB invocation and executes the
2694+# given command while it's suspended
2695
2696 . inc/common.sh
2697
2698-if [ -z "$INNODB_VERSION" ]; then
2699- echo "Requires InnoDB plugin or XtraDB" >$SKIPPED_REASON
2700- exit $SKIPPED_EXIT_CODE
2701-fi
2702+function add_rows()
2703+{
2704+ local table=$1
2705+ local start=$2
2706+ local limit=$3
2707+
2708+ while [ "$limit" -gt "$start" ]; do
2709+ sql="INSERT INTO $table VALUES ($start, $limit)"
2710+ let "start=start+1"
2711+ for ((i=0; $i<99; i++)); do
2712+ sql="${sql},($start, $limit)"
2713+ let "start=start+1"
2714+ done
2715+ ${MYSQL} ${MYSQL_ARGS} -e "$sql" incremental_sample
2716+ done
2717+}
2718
2719 #
2720 # Test incremental backup of a compressed tablespace with a specific page size
2721@@ -22,9 +42,9 @@
2722
2723 # Use innodb_strict_mode so that failure to use compression results in an
2724 # error rather than a warning
2725- mysqld_additional_args="--innodb_strict_mode --innodb_file_per_table \
2726- --innodb_file_format=Barracuda --innodb_max_dirty_pages_pct=0 \
2727- --innodb_log_file_size=1M"
2728+ mysqld_additional_args="$mysqld_extra_args --innodb_strict_mode \
2729+ --innodb_file_per_table --innodb_file_format=Barracuda \
2730+ --innodb_max_dirty_pages_pct=0 --innodb_log_file_size=1M"
2731
2732 start_server ${mysqld_additional_args}
2733
2734@@ -40,17 +60,7 @@
2735
2736 vlog "Adding initial rows to database..."
2737
2738- numrow=10000
2739- count=0
2740- while [ "$numrow" -gt "$count" ]; do
2741- sql="INSERT INTO test VALUES ($count, $numrow)"
2742- let "count=count+1"
2743- for ((i=0; $i<99; i++)); do
2744- sql="${sql},($count, $numrow)"
2745- let "count=count+1"
2746- done
2747- ${MYSQL} ${MYSQL_ARGS} -e "$sql" incremental_sample
2748- done
2749+ add_rows test 0 10000
2750
2751 rows=`${MYSQL} ${MYSQL_ARGS} -Ns -e "SELECT COUNT(*) FROM test" \
2752 incremental_sample`
2753@@ -84,18 +94,15 @@
2754 number INT(11) DEFAULT NULL) ENGINE=INNODB \
2755 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=$page_size" incremental_sample
2756
2757- let "count=numrow+1"
2758- let "numrow=15000"
2759- while [ "$numrow" -gt "$count" ]; do
2760- sql="VALUES ($count, $numrow)"
2761- let "count=count+1"
2762- for ((i=0; $i<99; i++)); do
2763- sql="${sql},($count, $numrow)"
2764- let "count=count+1"
2765- done
2766- ${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO test $sql" incremental_sample
2767- ${MYSQL} ${MYSQL_ARGS} -e "INSERT INTO t2 $sql" incremental_sample
2768- done
2769+ add_rows test 10001 12500
2770+ add_rows t2 10001 12500
2771+
2772+ # Rotate bitmap file here and force checkpoint at the same time
2773+ shutdown_server
2774+ start_server ${mysqld_additional_args}
2775+
2776+ add_rows test 12501 15000
2777+ add_rows t2 12501 15000
2778
2779 rows=`${MYSQL} ${MYSQL_ARGS} -Ns -e "SELECT COUNT(*) FROM test" \
2780 incremental_sample`
2781@@ -122,10 +129,29 @@
2782
2783 vlog "Making incremental backup"
2784
2785+ if [ -n "$first_inc_suspend_command" ]
2786+ then
2787+ suspend_arg="--suspend_at_start";
2788+ else
2789+ suspend_arg="";
2790+ fi
2791+
2792 # Incremental backup
2793 xtrabackup --datadir=$mysql_datadir --backup \
2794 --target-dir=$topdir/data/delta --incremental-basedir=$topdir/data/full \
2795- ${mysqld_additional_args}
2796+ ${mysqld_additional_args} $suspend_arg &
2797+
2798+ xb_pid=$!
2799+
2800+ if [ -n "$first_inc_suspend_command" ]
2801+ then
2802+ wait_for_xb_to_suspend $topdir/data/delta/xtrabackup_suspended_1
2803+ ${MYSQL} ${MYSQL_ARGS} -e "$first_inc_suspend_command"
2804+
2805+ resume_suspended_xb $topdir/data/delta/xtrabackup_suspended_1
2806+ fi
2807+
2808+ wait $xb_pid
2809
2810 vlog "Incremental backup done"
2811 vlog "Preparing backup"
2812@@ -149,7 +175,8 @@
2813
2814 # Restore backup
2815
2816- stop_server
2817+ shutdown_server
2818+ rm -f $mysql_datadir/ib_modified_log*
2819
2820 vlog "Copying files"
2821
2822
2823=== added file 'test/t/xb_incremental_compressed_bitmap_16kb.sh'
2824--- test/t/xb_incremental_compressed_bitmap_16kb.sh 1970-01-01 00:00:00 +0000
2825+++ test/t/xb_incremental_compressed_bitmap_16kb.sh 2013-04-16 17:44:27 +0000
2826@@ -0,0 +1,28 @@
2827+# Test incremental backups that use bitmaps with 16KB compressed pages
2828+
2829+if [ -z "$XTRADB_VERSION" ]; then
2830+ echo "Requires XtraDB" > $SKIPPED_REASON
2831+ exit $SKIPPED_EXIT_CODE
2832+fi
2833+
2834+# The test is disabled for Percona XtraDB Cluster until a version
2835+# with bitmap user requests is released (containing PS 5.5.29-30.0
2836+# or later)
2837+set +e
2838+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2839+probe_result=$?
2840+if [[ "$probe_result" == "0" ]]
2841+ then
2842+ vlog "Incompatible test" > $SKIPPED_REASON
2843+ exit $SKIPPED_EXIT_CODE
2844+fi
2845+set -e
2846+
2847+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2848+first_inc_suspend_command="FLUSH CHANGED_PAGE_BITMAPS;"
2849+
2850+source t/xb_incremental_compressed.inc
2851+
2852+test_incremental_compressed 16
2853+
2854+check_bitmap_inc_backup
2855
2856=== added file 'test/t/xb_incremental_compressed_bitmap_1kb.sh'
2857--- test/t/xb_incremental_compressed_bitmap_1kb.sh 1970-01-01 00:00:00 +0000
2858+++ test/t/xb_incremental_compressed_bitmap_1kb.sh 2013-04-16 17:44:27 +0000
2859@@ -0,0 +1,28 @@
2860+# Test incremental backups that use bitmaps with 1KB compressed pages
2861+
2862+if [ -z "$XTRADB_VERSION" ]; then
2863+ echo "Requires XtraDB" > $SKIPPED_REASON
2864+ exit $SKIPPED_EXIT_CODE
2865+fi
2866+
2867+# The test is disabled for Percona XtraDB Cluster until a version
2868+# with bitmap user requests is released (containing PS 5.5.29-30.0
2869+# or later)
2870+set +e
2871+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2872+probe_result=$?
2873+if [[ "$probe_result" == "0" ]]
2874+ then
2875+ vlog "Incompatible test" > $SKIPPED_REASON
2876+ exit $SKIPPED_EXIT_CODE
2877+fi
2878+set -e
2879+
2880+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2881+first_inc_suspend_command="FLUSH CHANGED_PAGE_BITMAPS;"
2882+
2883+source t/xb_incremental_compressed.inc
2884+
2885+test_incremental_compressed 1
2886+
2887+check_bitmap_inc_backup
2888
2889=== added file 'test/t/xb_incremental_compressed_bitmap_2kb.sh'
2890--- test/t/xb_incremental_compressed_bitmap_2kb.sh 1970-01-01 00:00:00 +0000
2891+++ test/t/xb_incremental_compressed_bitmap_2kb.sh 2013-04-16 17:44:27 +0000
2892@@ -0,0 +1,28 @@
2893+# Test incremental backups that use bitmaps with 2KB compressed pages
2894+
2895+if [ -z "$XTRADB_VERSION" ]; then
2896+ echo "Requires XtraDB" > $SKIPPED_REASON
2897+ exit $SKIPPED_EXIT_CODE
2898+fi
2899+
2900+# The test is disabled for Percona XtraDB Cluster until a version
2901+# with bitmap user requests is released (containing PS 5.5.29-30.0
2902+# or later)
2903+set +e
2904+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2905+probe_result=$?
2906+if [[ "$probe_result" == "0" ]]
2907+ then
2908+ vlog "Incompatible test" > $SKIPPED_REASON
2909+ exit $SKIPPED_EXIT_CODE
2910+fi
2911+set -e
2912+
2913+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2914+first_inc_suspend_command="FLUSH CHANGED_PAGE_BITMAPS;"
2915+
2916+source t/xb_incremental_compressed.inc
2917+
2918+test_incremental_compressed 2
2919+
2920+check_bitmap_inc_backup
2921
2922=== added file 'test/t/xb_incremental_compressed_bitmap_4kb.sh'
2923--- test/t/xb_incremental_compressed_bitmap_4kb.sh 1970-01-01 00:00:00 +0000
2924+++ test/t/xb_incremental_compressed_bitmap_4kb.sh 2013-04-16 17:44:27 +0000
2925@@ -0,0 +1,28 @@
2926+# Test incremental backups that use bitmaps with 4KB compressed pages
2927+
2928+if [ -z "$XTRADB_VERSION" ]; then
2929+ echo "Requires XtraDB" > $SKIPPED_REASON
2930+ exit $SKIPPED_EXIT_CODE
2931+fi
2932+
2933+# The test is disabled for Percona XtraDB Cluster until a version
2934+# with bitmap user requests is released (containing PS 5.5.29-30.0
2935+# or later)
2936+set +e
2937+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2938+probe_result=$?
2939+if [[ "$probe_result" == "0" ]]
2940+ then
2941+ vlog "Incompatible test" > $SKIPPED_REASON
2942+ exit $SKIPPED_EXIT_CODE
2943+fi
2944+set -e
2945+
2946+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2947+first_inc_suspend_command="FLUSH CHANGED_PAGE_BITMAPS;"
2948+
2949+source t/xb_incremental_compressed.inc
2950+
2951+test_incremental_compressed 4
2952+
2953+check_bitmap_inc_backup
2954
2955=== added file 'test/t/xb_incremental_compressed_bitmap_8kb.sh'
2956--- test/t/xb_incremental_compressed_bitmap_8kb.sh 1970-01-01 00:00:00 +0000
2957+++ test/t/xb_incremental_compressed_bitmap_8kb.sh 2013-04-16 17:44:27 +0000
2958@@ -0,0 +1,28 @@
2959+# Test incremental backups that use bitmaps with 8KB compressed pages
2960+
2961+if [ -z "$XTRADB_VERSION" ]; then
2962+ echo "Requires XtraDB" > $SKIPPED_REASON
2963+ exit $SKIPPED_EXIT_CODE
2964+fi
2965+
2966+# The test is disabled for Percona XtraDB Cluster until a version
2967+# with bitmap user requests is released (containing PS 5.5.29-30.0
2968+# or later)
2969+set +e
2970+${MYSQLD} --basedir=$MYSQL_BASEDIR --user=$USER --help --verbose --wsrep-sst-method=rsync| grep -q wsrep
2971+probe_result=$?
2972+if [[ "$probe_result" == "0" ]]
2973+ then
2974+ vlog "Incompatible test" > $SKIPPED_REASON
2975+ exit $SKIPPED_EXIT_CODE
2976+fi
2977+set -e
2978+
2979+mysqld_extra_args=--innodb-track-changed-pages=TRUE
2980+first_inc_suspend_command="FLUSH CHANGED_PAGE_BITMAPS;"
2981+
2982+source t/xb_incremental_compressed.inc
2983+
2984+test_incremental_compressed 8
2985+
2986+check_bitmap_inc_backup
2987
2988=== renamed file 'test/t/xb_incremental_compressed_16kb.sh' => 'test/t/xb_incremental_compressed_full_scan_16kb.sh'
2989--- test/t/xb_incremental_compressed_16kb.sh 2012-11-08 03:44:14 +0000
2990+++ test/t/xb_incremental_compressed_full_scan_16kb.sh 2013-04-16 17:44:27 +0000
2991@@ -1,2 +1,10 @@
2992+# Test incremental backups that do full data scans with 16KB compressed pages
2993+
2994+mysqld_extra_args=
2995+first_inc_suspend_command=
2996+
2997 source t/xb_incremental_compressed.inc
2998+
2999 test_incremental_compressed 16
3000+
3001+check_full_scan_inc_backup
3002
3003=== renamed file 'test/t/xb_incremental_compressed_1kb.sh' => 'test/t/xb_incremental_compressed_full_scan_1kb.sh'
3004--- test/t/xb_incremental_compressed_1kb.sh 2012-11-08 03:44:14 +0000
3005+++ test/t/xb_incremental_compressed_full_scan_1kb.sh 2013-04-16 17:44:27 +0000
3006@@ -1,2 +1,10 @@
3007+# Test incremental backups that do full data scans with 1KB compressed pages
3008+
3009+mysqld_extra_args=
3010+first_inc_suspend_command=
3011+
3012 source t/xb_incremental_compressed.inc
3013+
3014 test_incremental_compressed 1
3015+
3016+check_full_scan_inc_backup
3017
3018=== renamed file 'test/t/xb_incremental_compressed_2kb.sh' => 'test/t/xb_incremental_compressed_full_scan_2kb.sh'
3019--- test/t/xb_incremental_compressed_2kb.sh 2012-11-08 03:44:14 +0000
3020+++ test/t/xb_incremental_compressed_full_scan_2kb.sh 2013-04-16 17:44:27 +0000
3021@@ -1,2 +1,10 @@
3022+# Test incremental backups that do full data scans with 2KB compressed pages
3023+
3024+mysqld_extra_args=
3025+first_inc_suspend_command=
3026+
3027 source t/xb_incremental_compressed.inc
3028+
3029 test_incremental_compressed 2
3030+
3031+check_full_scan_inc_backup
3032
3033=== renamed file 'test/t/xb_incremental_compressed_4kb.sh' => 'test/t/xb_incremental_compressed_full_scan_4kb.sh'
3034--- test/t/xb_incremental_compressed_4kb.sh 2012-11-08 03:44:14 +0000
3035+++ test/t/xb_incremental_compressed_full_scan_4kb.sh 2013-04-16 17:44:27 +0000
3036@@ -1,2 +1,10 @@
3037+# Test incremental backups that do full data scans with 4KB compressed pages
3038+
3039+mysqld_extra_args=
3040+first_inc_suspend_command=
3041+
3042 source t/xb_incremental_compressed.inc
3043+
3044 test_incremental_compressed 4
3045+
3046+check_full_scan_inc_backup
3047
3048=== renamed file 'test/t/xb_incremental_compressed_8kb.sh' => 'test/t/xb_incremental_compressed_full_scan_8kb.sh'
3049--- test/t/xb_incremental_compressed_8kb.sh 2012-11-08 03:44:14 +0000
3050+++ test/t/xb_incremental_compressed_full_scan_8kb.sh 2013-04-16 17:44:27 +0000
3051@@ -1,2 +1,10 @@
3052+# Test incremental backups that do full data scans with 8KB compressed pages
3053+
3054+mysqld_extra_args=
3055+first_inc_suspend_command=
3056+
3057 source t/xb_incremental_compressed.inc
3058+
3059 test_incremental_compressed 8
3060+
3061+check_full_scan_inc_backup
3062
3063=== modified file 'test/t/xb_log_overwrap.sh'
3064--- test/t/xb_log_overwrap.sh 2013-01-08 10:14:53 +0000
3065+++ test/t/xb_log_overwrap.sh 2013-04-16 17:44:27 +0000
3066@@ -20,14 +20,7 @@
3067
3068 pid_file=$topdir/backup/xtrabackup_debug_sync
3069
3070-# Wait for xtrabackup to suspend
3071-i=0
3072-while [ ! -r "$pid_file" ]
3073-do
3074- sleep 1
3075- i=$((i+1))
3076- echo "Waited $i seconds for $pid_file to be created"
3077-done
3078+wait_for_xb_to_suspend $pid_file
3079
3080 xb_pid=`cat $pid_file`
3081
3082@@ -41,5 +34,7 @@
3083 vlog "Resuming xtrabackup"
3084 kill -SIGCONT $xb_pid
3085
3086+resume_suspended_xb $topdir/backup/xtrabackup_suspended_2
3087+
3088 # wait's return code will be the code returned by the background process
3089 run_cmd wait $job_pid

Subscribers

People subscribed via source and target branches