~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to lib/GenTest/Reporter/BackupAndRestoreInvariant.pm

initial import from internal tree

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#####################################################################
 
2
#
 
3
# Author: Jorgen Loland
 
4
# Date: April 2009
 
5
#
 
6
# Purpose: Implementation of WL#4218: Test that transactions executed
 
7
# concurrently with backup are either completely restored or not
 
8
# restored at all. No transaction should be partially represented
 
9
# after restore.
 
10
#
 
11
# See further documentation in invariant.yy
 
12
#
 
13
# Associated files: 
 
14
#   mysql-test/gentest/conf/invariant.yy
 
15
#   mysql-test/gentest/conf/invariant.zz
 
16
#   mysql-test/gentest/lib/GenTest/Validator/Invariant.pm
 
17
#   mysql-test/gentest/lib/GenTest/Reporter/BackupAndRestoreInvariant.pm
 
18
#
 
19
#####################################################################
 
20
package GenTest::Reporter::BackupAndRestoreInvariant;
 
21
 
 
22
require Exporter;
 
23
@ISA = qw(GenTest::Reporter);
 
24
 
 
25
use strict;
 
26
use GenTest;
 
27
use GenTest::Reporter;
 
28
use GenTest::Constants;
 
29
 
 
30
my $count = 0;
 
31
my $file = 'rqg_'.time().'.bup';
 
32
my $expected_total=100000;
 
33
 
 
34
sub monitor {
 
35
  my $reporter = shift;
 
36
  my $dsn = $reporter->dsn();
 
37
 
 
38
  # Return if BACKUP has already been executed
 
39
  return STATUS_OK if $count > 0;
 
40
 
 
41
  # Execute BACKUP when 20 seconds remains
 
42
  if (time() > $reporter->testEnd() - 20) {
 
43
      my $dbh = DBI->connect($dsn);
 
44
 
 
45
      unlink($file);
 
46
      say("Executing BACKUP DATABASE.");
 
47
      $dbh->do("BACKUP DATABASE test TO '".$file."'");
 
48
      say("Executing BACKUP DATABASE done.");
 
49
      $count++;
 
50
      if (defined $dbh->err()) {
 
51
          return STATUS_DATABASE_CORRUPTION;
 
52
      }
 
53
  }
 
54
  return STATUS_OK;
 
55
}
 
56
 
 
57
my $tables;
 
58
sub report {
 
59
  my $reporter = shift;
 
60
  my $dsn = $reporter->dsn();
 
61
  my $dbh = DBI->connect($dsn);
 
62
 
 
63
  ###########################
 
64
  # Get data before restore #
 
65
  ###########################
 
66
  $dbh = DBI->connect($dsn) if not defined $dbh;
 
67
  $tables = $dbh->selectcol_arrayref("SHOW TABLES") if not defined $tables;
 
68
  my $results;
 
69
 
 
70
  my $query="SELECT SUM(alltbls.int_not_null) as sum, 
 
71
                    COUNT(alltbls.int_not_null) as count 
 
72
             FROM (";
 
73
 
 
74
  my $tblno=0;
 
75
  foreach my $table (@$tables) {
 
76
    if ($tblno>0) {
 
77
      $query = $query . " UNION ALL ";
 
78
    }
 
79
    $query = $query . "SELECT int_not_null FROM " . $table;
 
80
    $tblno++;
 
81
  }
 
82
  $query = $query . ") AS alltbls";
 
83
 
 
84
  # Execute query
 
85
  my ($sum_before, $count_before) = $dbh->selectrow_array($query);
 
86
 
 
87
  if (($sum_before eq '') || ($count_before eq '')) {
 
88
    # Server probably crashed, the SELECT returned no data
 
89
    say ("Failed to query status before RESTORE");
 
90
    return STATUS_UNKNOWN_ERROR;
 
91
  }
 
92
 
 
93
  if (($sum_before ne $expected_total)) {
 
94
    say("Bad sum before RESTORE: sum: $sum_before; count: $count_before");
 
95
    return STATUS_DATABASE_CORRUPTION;
 
96
  }
 
97
 
 
98
  ##################################
 
99
  # RESTORE the backed up database #
 
100
  ##################################
 
101
 
 
102
  say("Executing RESTORE FROM.");
 
103
  $dbh->do("RESTORE FROM '".$file."' OVERWRITE");
 
104
 
 
105
  if (defined $dbh->err()) {
 
106
    return STATUS_DATABASE_CORRUPTION;
 
107
  }
 
108
 
 
109
  ##########################
 
110
  # Get data after restore #
 
111
  ##########################
 
112
 
 
113
  ######################
 
114
  # Consistency test 1 #
 
115
  ######################
 
116
 
 
117
  # Execute the same query that was executed before RESTORE
 
118
  my ($sum_after, $count_after) = $dbh->selectrow_array($query);
 
119
 
 
120
  if (($sum_after eq '') && ($count_after eq '')) {
 
121
    # Server probably crashed, the SELECT returned no data
 
122
    say ("Failed to query status after RESTORE");
 
123
    return STATUS_UNKNOWN_ERROR;
 
124
  }
 
125
 
 
126
  # The total amount of money across all bank account should not have changed
 
127
  if (($sum_after ne $expected_total)) {
 
128
    say("Bad sum for tables: sum: $sum_after; count: $count_after");
 
129
    return STATUS_DATABASE_CORRUPTION;
 
130
  }
 
131
 
 
132
  say("*** STARTING DATA CONSISTENCY CHECKING ***");
 
133
  say("Invariant (total amount of money is ".$expected_total.") is still still OK after RESTORE");
 
134
 
 
135
  ######################
 
136
  # Consistency test 2 #
 
137
  ######################
 
138
 
 
139
  my @predicates = ("/* No predicate */");
 
140
 
 
141
  push @predicates, (
 
142
    " FORCE INDEX(PRIMARY) WHERE `pk` >= -922337",
 
143
    " FORCE INDEX(PRIMARY) ORDER BY `pk` LIMIT 4294836225",
 
144
  );
 
145
 
 
146
  # Use other access methods than in test 1; access the two tables
 
147
  # independantly and with different predicates. 
 
148
  my $sum=0;
 
149
  my $count=0;
 
150
  foreach my $predicate (@predicates) {
 
151
    foreach my $table (@$tables) {
 
152
      $query="SELECT SUM(int_not_null) as sum, 
 
153
                     COUNT(int_not_null) as count 
 
154
              FROM ".$table.$predicate;
 
155
      my ($res1, $res2) = $dbh->selectrow_array($query);
 
156
      $sum+=$res1;
 
157
      $count+=$res2;
 
158
    }
 
159
 
 
160
    if ($count ne $count_after) {
 
161
      say("Bad count when executing query with predicate: \n\t\"$predicate\"\n\tCount: $count; Count for union: $count_after");
 
162
      return STATUS_DATABASE_CORRUPTION;
 
163
    }
 
164
 
 
165
    if ($sum ne $expected_total) {
 
166
      say("Bad sum when executing query with predicate: \n\t\"$predicate\"\n\tSum: $sum; Sum for union: $sum_after");
 
167
      return STATUS_DATABASE_CORRUPTION;
 
168
    }
 
169
 
 
170
    say("Query OK with predicate: \"$predicate\"");
 
171
 
 
172
    $sum=0;
 
173
    $count=0;
 
174
  }
 
175
 
 
176
  ######################
 
177
  # Consistency test 3 #
 
178
  ######################
 
179
 
 
180
  # Use MySQL consistency testing functions to further exercise the
 
181
  # restored objects
 
182
  my $engine = $reporter->serverVariable('storage_engine');
 
183
  foreach my $table (@$tables) {
 
184
    foreach my $sql (
 
185
      "CHECK TABLE `$table` EXTENDED",
 
186
      "ANALYZE TABLE `$table`",
 
187
      "OPTIMIZE TABLE `$table`",
 
188
      "REPAIR TABLE `$table` EXTENDED",
 
189
      "ALTER TABLE `$table` ENGINE = $engine"
 
190
    ) {
 
191
      say("Executing $sql.");
 
192
      my $sth = $dbh->prepare($sql);
 
193
      $sth->execute() if defined $sth;
 
194
      print Dumper $sth->fetchall_arrayref() if defined $sth && $sth->{NUM_OF_FIELDS} > 0;
 
195
      $sth->finish();
 
196
      return STATUS_DATABASE_CORRUPTION if $dbh->err() == 2013;
 
197
    }
 
198
  }
 
199
 
 
200
  say("*** CONSISTENCY CHECKING DONE: ALL CHECKS PASSED ***");
 
201
 
 
202
  # Cleanup
 
203
  $dbh->do("DROP DATABASE test");
 
204
 
 
205
  return STATUS_OK;
 
206
 
 
207
}
 
208
 
 
209
sub type {
 
210
  return REPORTER_TYPE_PERIODIC | REPORTER_TYPE_SUCCESS;
 
211
}
 
212
 
 
213
1;