MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
testRestartGci.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include "NDBT_Test.hpp"
19 #include "NDBT_ReturnCodes.h"
20 #include "HugoTransactions.hpp"
21 #include "UtilTransactions.hpp"
22 #include "NdbRestarter.hpp"
23 
24 
30 struct SavedRecord {
31  Uint64 m_gci;
32  Uint32 m_author;
33  BaseString m_str;
34  SavedRecord(Uint64 _gci, Uint32 _author, BaseString _str){
35  m_gci = _gci;
36  m_author = _author;
37  m_str.assign(_str);
38  }
39  SavedRecord(){
40  m_gci = 0;
41  m_str = "";
42  };
43 };
44 Vector<SavedRecord> savedRecords;
45 Uint64 highestExpectedGci;
46 
47 #define CHECK(b) if (!(b)) { \
48  ndbout << "ERR: "<< step->getName() \
49  << " failed on line " << __LINE__ << endl; \
50  result = NDBT_FAILED; \
51  break; }
52 
53 static
54 int
55 maybeExtraBits(Ndb* ndb, NdbDictionary::Table& tab, int when, void* arg)
56 {
57  switch(when){
58  case 0: // Before
59  break;
60  case 1: // After
61  return 0;
62  default:
63  return 0;
64  }
65 
66  bool useExtendedBits = ((ndb_rand() % 5) != 0);
67  Uint32 numGciBits= ndb_rand() % 32; /* 0 -> 31 */
68  Uint32 numAuthorBits = ndb_rand() % 32; /* 0 -> 31 */
69 
70  if (useExtendedBits && (numGciBits || numAuthorBits))
71  {
72  ndbout_c("Creating table %s with %u extra Gci and %u extra Author bits",
73  tab.getName(), numGciBits, numAuthorBits);
74  tab.setExtraRowGciBits(numGciBits);
75  tab.setExtraRowAuthorBits(numAuthorBits);
76  }
77  else
78  {
79  ndbout_c("Table has no extra bits");
80  }
81 
82  return 0;
83 }
84 
85 int runDropTable(NDBT_Context* ctx, NDBT_Step* step)
86 {
87  GETNDB(step)->getDictionary()->dropTable(ctx->getTab()->getName());
88  return NDBT_OK;
89 }
90 
91 int runCreateTable(NDBT_Context* ctx, NDBT_Step* step)
92 {
93 
94  runDropTable(ctx, step);
95 
96  /* Use extra proc to control whether we have extra bits */
97  if (NDBT_Tables::createTable(GETNDB(step),
98  ctx->getTab()->getName(),
99  false, false,
100  maybeExtraBits) == NDBT_OK)
101  {
102  ctx->setTab(GETNDB(step)->
103  getDictionary()->
104  getTable(ctx->getTab()->getName()));
105  return NDBT_OK;
106  }
107  return NDBT_FAILED;
108 }
109 
110 int runInsertRememberGci(NDBT_Context* ctx, NDBT_Step* step){
111  int result = NDBT_OK;
112  int records = ctx->getNumRecords();
113  HugoOperations hugoOps(*ctx->getTab());
114  HugoCalculator hugoCalc(*ctx->getTab());
115  Ndb* pNdb = GETNDB(step);
116  int i = 0;
117 
118  ndbout_c("Inserting %u records", records);
119  Uint64 minGci = ~Uint64(0);
120  Uint64 maxGci = 0;
121  Uint32 numAuthorBits = ctx->getTab()->getExtraRowAuthorBits();
122  Uint32 authorMask = (1 << numAuthorBits) -1;
123  ndbout_c("numAuthor bits is %u, mask is %x",
124  numAuthorBits, authorMask);
125 
126  while(i < records){
127  // Insert record and read it in same transaction
128  CHECK(hugoOps.startTransaction(pNdb) == 0);
129  CHECK(hugoOps.pkInsertRecord(pNdb, i) == 0);
130  if (hugoOps.execute_NoCommit(pNdb) != 0){
131  ndbout << "Could not insert record " << i << endl;
132  result = NDBT_FAILED;
133  break;
134  }
135  /* Set the author column (if present) */
136  Uint32 authorVal = 0;
137  if (ctx->getTab()->getExtraRowAuthorBits() > 0)
138  {
139  authorVal = (ndb_rand() & authorMask);
140  /* Pain here due to need to use NdbRecord */
141  char rowBuff[NDB_MAX_TUPLE_SIZE];
142  const NdbDictionary::Table* tab = ctx->getTab();
143  CHECK(hugoCalc.setValues((Uint8*) rowBuff, tab->getDefaultRecord(),
144  i, 0) == 0);
145  NdbOperation::SetValueSpec setValueSpec;
146  setValueSpec.column = NdbDictionary::Column::ROW_AUTHOR;
147  setValueSpec.value = &authorVal;
149  opts.optionsPresent= NdbOperation::OperationOptions::OO_SETVALUE;
150  opts.extraSetValues= &setValueSpec;
151  opts.numExtraSetValues = 1;
152 
153  const NdbOperation* update = hugoOps.getTransaction()->
154  updateTuple(tab->getDefaultRecord(), rowBuff,
155  tab->getDefaultRecord(), rowBuff,
156  NULL, /* mask */
157  &opts,
158  sizeof(opts));
159  CHECK(update != NULL);
160  }
161  /* Read row back */
162  CHECK(hugoOps.pkReadRecord(pNdb, i) == 0);
163  if (hugoOps.execute_Commit(pNdb) != 0){
164  ndbout << "Did not find record in DB " << i << endl;
165  result = NDBT_FAILED;
166  break;
167  }
168  Uint64 gci;
169  CHECK(hugoOps.getTransaction()->getGCI(&gci) == 0);
170 
171  if (gci < minGci)
172  minGci = gci;
173  if (gci > maxGci)
174  maxGci = gci;
175 
176  savedRecords.push_back(SavedRecord(gci,
177  authorVal,
178  hugoOps.getRecordStr(0)));
179 
180  CHECK(hugoOps.closeTransaction(pNdb) == 0);
181  i++;
182  /* Sleep so that records will have > 1 GCI between them */
183  NdbSleep_MilliSleep(10);
184  };
185 
186  ndbout_c(" Inserted records from gci %x/%x to gci %x/%x",
187  (Uint32) (minGci >> 32), (Uint32) (minGci & 0xffffffff),
188  (Uint32) (maxGci >> 32), (Uint32) (maxGci & 0xffffffff));
189 
190  highestExpectedGci = maxGci;
191 
192  return result;
193 }
194 
195 int runRestartAll(NDBT_Context* ctx, NDBT_Step* step){
196  Ndb* pNdb = GETNDB(step);
197  NdbRestarter restarter;
198 
199  ndbout_c("Restart of all nodes");
200 
201  // Restart cluster with abort
202  if (restarter.restartAll(false, false, true) != 0){
203  ctx->stopTest();
204  return NDBT_FAILED;
205  }
206 
207  if (restarter.waitClusterStarted(300) != 0){
208  return NDBT_FAILED;
209  }
210 
211  if (pNdb->waitUntilReady() != 0){
212  return NDBT_FAILED;
213  }
214 
215  return NDBT_OK;
216 }
217 
218 int runRestartOneInitial(NDBT_Context* ctx, NDBT_Step* step){
219  Ndb* pNdb = GETNDB(step);
220  NdbRestarter restarter;
221 
222  if (restarter.getNumDbNodes() < 2)
223  return NDBT_OK;
224 
225  /* We don't restart the Master as we need to know a
226  * non-restarted node to reliably get the restartGci
227  * afterwards!
228  * Should be no real reason not to restart the master.
229  */
230  int node = restarter.getRandomNotMasterNodeId(rand());
231  ndbout_c("Restarting node %u initial", node);
232 
233  if (restarter.restartOneDbNode(node,
234  true, /* Initial */
235  false, /* Nostart */
236  true) /* Abort */
237  != 0)
238  {
239  ctx->stopTest();
240  return NDBT_FAILED;
241  }
242 
243  if (restarter.waitClusterStarted(300) != 0){
244  return NDBT_FAILED;
245  }
246 
247  if (pNdb->waitUntilReady() != 0){
248  return NDBT_FAILED;
249  }
250 
251  return NDBT_OK;
252 }
253 
254 int runRestartGciControl(NDBT_Context* ctx, NDBT_Step* step){
255  int records = ctx->getNumRecords();
256  Ndb* pNdb = GETNDB(step);
257  UtilTransactions utilTrans(*ctx->getTab());
258 
259  // Wait until we have enough records in db
260  int count = 0;
261  while (count < records){
262  if (utilTrans.selectCount(pNdb, 64, &count) != 0){
263  ctx->stopTest();
264  return NDBT_FAILED;
265  }
266  NdbSleep_MilliSleep(10);
267  }
268 
269  return runRestartAll(ctx,step);
270 }
271 
272 int runDetermineRestartGci(NDBT_Context* ctx, NDBT_Step* step)
273 {
274  Ndb* pNdb = GETNDB(step);
275  Uint32 restartGci;
276  int res = pNdb->getDictionary()->getRestartGCI(&restartGci);
277  if (res != 0)
278  {
279  ndbout << "Failed to retrieve restart gci" << endl;
280  ndbout << pNdb->getDictionary()->getNdbError() << endl;
281  return NDBT_FAILED;
282  }
283 
284  ndbout_c("Restart GCI is %u (0x%x)",
285  restartGci, restartGci);
286 
287  ndbout_c("Highest expected GCI was %x/%x",
288  (Uint32) (highestExpectedGci >> 32),
289  (Uint32) (highestExpectedGci & 0xffffffff));
290 
291  highestExpectedGci = ((Uint64) restartGci) << 32 | 0xffffffff;
292  ndbout_c("Resetting Highest expected GCI to align with restart Gci (%x/%x)",
293  (Uint32) (highestExpectedGci >> 32),
294  (Uint32) (highestExpectedGci & 0xffffffff));
295  return NDBT_OK;
296 }
297 
298 int runRequireExact(NDBT_Context* ctx, NDBT_Step* step){
299  ctx->incProperty("ExactGCI");
300  return NDBT_OK;
301 }
302 
303 int runVerifyInserts(NDBT_Context* ctx, NDBT_Step* step){
304  int result = NDBT_OK;
305  Ndb* pNdb = GETNDB(step);
306  UtilTransactions utilTrans(*ctx->getTab());
307  HugoOperations hugoOps(*ctx->getTab());
308  NdbRestarter restarter;
309  Uint32 extraGciBits = ctx->getTab()->getExtraRowGciBits();
310  Uint32 firstSaturatedValue = (1 << extraGciBits) -1;
311 
312  int count = 0;
313  if (utilTrans.selectCount(pNdb, 64, &count) != 0){
314  return NDBT_FAILED;
315  }
316 
317  // RULE1: The vector with saved records should have exactly as many
318  // records with lower or same gci as there are in DB
319  int recordsWithLowerOrSameGci = 0;
320  unsigned i;
321  for (i = 0; i < savedRecords.size(); i++){
322  if (savedRecords[i].m_gci <= highestExpectedGci)
323  recordsWithLowerOrSameGci++;
324  }
325  if (recordsWithLowerOrSameGci != count){
326  ndbout << "ERR: Wrong number of expected records" << endl;
327  result = NDBT_FAILED;
328  }
329 
330  bool exactGCIonly = ctx->getProperty("ExactGCI", (unsigned) 0);
331 
332  // RULE2: The records found in db should have same or lower
333  // gci as in the vector
334  int recordsWithIncorrectGci = 0;
335  int recordsWithRoundedGci = 0;
336  int recordsWithIncorrectAuthor = 0;
337  for (i = 0; i < savedRecords.size(); i++){
338  CHECK(hugoOps.startTransaction(pNdb) == 0);
339  /* First read of row to check contents */
340  CHECK(hugoOps.pkReadRecord(pNdb, i) == 0);
341  /* Second read of row to get GCI */
342  NdbTransaction* trans = hugoOps.getTransaction();
343  NdbOperation* readOp = trans->getNdbOperation(ctx->getTab());
344  CHECK(readOp != NULL);
345  CHECK(readOp->readTuple() == 0);
346  CHECK(hugoOps.equalForRow(readOp, i) == 0);
347  NdbRecAttr* rowGci = readOp->getValue(NdbDictionary::Column::ROW_GCI64);
348  NdbRecAttr* rowAuthor = readOp->getValue(NdbDictionary::Column::ROW_AUTHOR);
349  CHECK(rowGci != NULL);
350  CHECK(rowAuthor != NULL);
351  if (hugoOps.execute_Commit(pNdb) != 0){
352  // Record was not found in db'
353 
354  // Check record gci
355  if (savedRecords[i].m_gci <= highestExpectedGci) {
356  ndbout << "ERR: Record "<<i<<" should have existed" << endl;
357  result = NDBT_FAILED;
358  }
359  else
360  {
361  /* It didn't exist, but that was expected.
362  * Let's disappear it, so that it doesn't cause confusion
363  * after further restarts.
364  */
365  savedRecords[i].m_gci = (Uint64(1) << 63) -1; // Big number
366  }
367  } else {
368  // Record was found in db
369  BaseString str = hugoOps.getRecordStr(0);
370  // Check record string
371  if (!(savedRecords[i].m_str == str)){
372  ndbout << "ERR: Record "<<i<<" str did not match "<< endl;
373  result = NDBT_FAILED;
374  }
375  // Check record gci in range
376  Uint64 expectedRecordGci = savedRecords[i].m_gci;
377  if (expectedRecordGci > highestExpectedGci){
378  ndbout << "ERR: Record "<<i<<" should not have existed" << endl;
379  result = NDBT_FAILED;
380  }
381  bool expectRounding = (expectedRecordGci & 0xffffffff) >= firstSaturatedValue;
382  Uint64 expectedRoundedGci = (expectedRecordGci | 0xffffffff);
383  Uint64 readGci = rowGci->u_64_value();
384  Uint64 expectedRead = (expectRounding)?expectedRoundedGci :
385  expectedRecordGci;
386  // Check record gci is exactly correct
387  if (expectedRead != readGci){
388  if ((!exactGCIonly) &&
389  (expectedRoundedGci == readGci))
390  {
391  /* Record rounded, though bits can be represented
392  * presumably due to Redo gci truncation
393  */
394  recordsWithRoundedGci++;
395  }
396  else
397  {
398  ndbout_c("ERR: Record %u should have GCI %x/%x, but has "
399  "%x/%x.",
400  i,
401  (Uint32) (expectedRead >> 32),
402  (Uint32) (expectedRead & 0xffffffff),
403  (Uint32) (readGci >> 32),
404  (Uint32) (readGci & 0xffffffff));
405  recordsWithIncorrectGci++;
406  result = NDBT_FAILED;
407  }
408  }
409 
410  // Check author value is correct.
411  Uint32 expectedAuthor = savedRecords[i].m_author;
412 
413  if (rowAuthor->u_32_value() != expectedAuthor)
414  {
415  ndbout_c("ERR: Record %u should have Author %d, but has %d.",
416  i,
417  expectedAuthor,
418  rowAuthor->u_32_value());
419  recordsWithIncorrectAuthor++;
420  result = NDBT_FAILED;
421  }
422  }
423 
424  CHECK(hugoOps.closeTransaction(pNdb) == 0);
425  }
426 
427 
428  ndbout << "There are " << count << " records in db" << endl;
429  ndbout << "There are " << savedRecords.size()
430  << " records in vector" << endl;
431 
432  ndbout_c("There are %u records with lower or same gci than %x/%x",
433  recordsWithLowerOrSameGci,
434  (Uint32)(highestExpectedGci >> 32),
435  (Uint32)(highestExpectedGci & 0xffffffff));
436 
437  ndbout_c("There are %u records with rounded Gcis. Exact GCI flag is %u",
438  recordsWithRoundedGci, exactGCIonly);
439 
440  ndbout << "There are " << recordsWithIncorrectGci
441  << " records with incorrect Gci on recovery." << endl;
442 
443  ndbout << "There are " << recordsWithIncorrectAuthor
444  << " records with incorrect Author on recovery." << endl;
445 
446  return result;
447 }
448 
449 int runClearGlobals(NDBT_Context* ctx, NDBT_Step* step){
450  savedRecords.clear();
451  highestExpectedGci = 0;
452  return NDBT_OK;
453 }
454 
455 int runClearTable(NDBT_Context* ctx, NDBT_Step* step){
456  int records = ctx->getNumRecords();
457 
458  UtilTransactions utilTrans(*ctx->getTab());
459  if (utilTrans.clearTable2(GETNDB(step), records, 240) != 0){
460  return NDBT_FAILED;
461  }
462  return NDBT_OK;
463 }
464 
465 
466 int runLoadTable(NDBT_Context* ctx, NDBT_Step* step)
467 {
468  int records = ctx->getNumRecords();
469  HugoTransactions hugoTrans(*ctx->getTab());
470  if (hugoTrans.loadTable(GETNDB(step), records, 512, false, 0, true) != 0){
471  return NDBT_FAILED;
472  }
473  return NDBT_OK;
474 }
475 
476 int runNodeInitialRestarts(NDBT_Context* ctx, NDBT_Step* step)
477 {
478  NdbRestarter restarter;
479  const Uint32 numRestarts = 4;
480  for (Uint32 nr = 0; nr < numRestarts; nr++)
481  {
482  if (ctx->isTestStopped())
483  {
484  return NDBT_OK;
485  }
486  int nodeId = restarter.getNode(NdbRestarter::NS_RANDOM);
487  ndbout_c("Restarting node %u", nodeId);
488 
489  if (restarter.restartOneDbNode(nodeId, NdbRestarter::NRRF_INITIAL) != 0)
490  {
491  ndbout_c("Error restarting node");
492  ctx->stopTest();
493  return NDBT_FAILED;
494  }
495 
496  if (restarter.waitClusterStarted(300) != 0)
497  {
498  ctx->stopTest();
499  return NDBT_FAILED;
500  }
501 
502  if (GETNDB(step)->waitUntilReady() != 0)
503  {
504  ctx->stopTest();
505  return NDBT_FAILED;
506  }
507  }
508 
509  ctx->stopTest();
510 
511  return NDBT_OK;
512 }
513 
514 int runUpdateVerifyGCI(NDBT_Context* ctx, NDBT_Step* step)
515 {
516  HugoOperations hugoOps(*ctx->getTab());
517  HugoCalculator hugoCalc(*ctx->getTab());
518  Ndb* pNdb = GETNDB(step);
519 
520  /* Loop, updating the first record in the table, and checking
521  * that it has the GCI it should
522  */
523  Uint64 loopCount = 0;
524  Uint64 distinctCount = 0;
525  Uint64 expectedGCI = 0;
526  Uint64 lastGoodReadGCI = 0;
527  Uint32 extraGciBits = ctx->getTab()->getExtraRowGciBits();
528  Uint32 firstSaturatedValue = (1 << extraGciBits) -1;
529  ndbout_c("Extra GCI bits : %u, firstSaturatedValue : %u",
530  extraGciBits,
531  firstSaturatedValue);
532  int result = NDBT_OK;
533  while (!ctx->isTestStopped())
534  {
535  CHECK(hugoOps.startTransaction(pNdb) == 0);
536  /* Define a read op to get the 'existing' GCI */
537  NdbTransaction* trans = hugoOps.getTransaction();
538  CHECK(hugoOps.pkReadRecord(pNdb,
539  0,
540  1) == 0);
541  NdbOperation* readOp = trans->getNdbOperation(ctx->getTab());
542  CHECK(readOp != NULL);
543  CHECK(readOp->readTuple() == 0);
544  CHECK(hugoOps.equalForRow(readOp, 0) == 0);
545  NdbRecAttr* rowGci = readOp->getValue(NdbDictionary::Column::ROW_GCI64);
546  CHECK(rowGci != NULL);
547 
548  /* Define an update op to set the next GCI */
549  CHECK(hugoOps.pkUpdateRecord(pNdb, 0, 1, loopCount+1) == 0);
550 
551  if (hugoOps.execute_Commit(pNdb) != 0)
552  {
553  if (hugoOps.getNdbError().classification ==
555  {
556  hugoOps.closeTransaction(pNdb);
557  ndbout_c("Temporary error at loopCount %llu", loopCount);
558  continue;
559  }
560 
561  ndbout << "Error executing : " << hugoOps.getNdbError() << endl;
562  return NDBT_FAILED;
563  }
564 
565  /* First check the data is as expected */
566  CHECK(hugoCalc.verifyRowValues(&hugoOps.get_row(0)) == 0);
567  CHECK((Uint64)hugoCalc.getUpdatesValue(&hugoOps.get_row(0)) == loopCount);
568  //ndbout_c("Updates value is %u", hugoCalc.getUpdatesValue(&hugoOps.get_row(0)));
569 
570  Uint64 committedGCI;
571  CHECK(trans->getGCI(&committedGCI) == 0);
572  Uint32 gci_lo = Uint32(committedGCI & 0xffffffff);
573 
574  Uint64 saturatedCommittedGCI = (gci_lo >= firstSaturatedValue) ?
575  committedGCI | 0xffffffff : committedGCI;
576  Uint64 rowGCI64 = rowGci->u_64_value();
577 
578 // ndbout_c("Read row GCI64 %x/%x. Committed GCI64 : %x/%x. Saturated GCI64 :%x/%x Last good read : %x/%x",
579 // Uint32(rowGCI64 >> 32),
580 // Uint32(rowGCI64 & 0xffffffff),
581 // Uint32(committedGCI >> 32),
582 // Uint32(committedGCI & 0xffffffff),
583 // Uint32(saturatedCommittedGCI >> 32),
584 // Uint32(saturatedCommittedGCI & 0xffffffff),
585 // Uint32(lastGoodReadGCI >> 32),
586 // Uint32(lastGoodReadGCI & 0xffffffff));
587 
588 
589  if (rowGCI64 < lastGoodReadGCI)
590  {
591  ndbout_c("ERROR : Read row GCI value (%x/%x) lower than previous value (%x/%x)",
592  (Uint32) (rowGCI64 >> 32),
593  (Uint32) (rowGCI64 & 0xffffffff),
594  Uint32(lastGoodReadGCI >> 32),
595  Uint32(lastGoodReadGCI & 0xffffffff));
596  }
597  /* We certainly should not read a committed GCI value that's
598  * bigger than the read's commit-point GCI
599  */
600  if (saturatedCommittedGCI < rowGCI64)
601  {
602  ndbout_c("ERROR : Saturated committed GCI (%x/%x) lower than actual read GCI (%x/%x)",
603  Uint32(saturatedCommittedGCI >>32),
604  Uint32(saturatedCommittedGCI & 0xffffffff),
605  (Uint32) (rowGCI64 >> 32),
606  (Uint32) (rowGCI64 & 0xffffffff));
607  }
608  /* If we've read a committed GCI then we should certainly not
609  * be committing at lower values
610  */
611  if (saturatedCommittedGCI < lastGoodReadGCI)
612  {
613  ndbout_c("ERROR : Saturated committed GCI (%x/%x) lower than a previously"
614  "read GCI (%x/%x)",
615  Uint32(saturatedCommittedGCI >>32),
616  Uint32(saturatedCommittedGCI & 0xffffffff),
617  Uint32(lastGoodReadGCI >> 32),
618  Uint32(lastGoodReadGCI & 0xffffffff));
619  };
620  /* If we've previously had a particular committed GCI then we
621  * should certainly not now have a lower committed GCI
622  */
623  if (saturatedCommittedGCI < expectedGCI)
624  {
625  ndbout_c("ERROR : Saturated committed GCI (%x/%x) lower than expected GCI"
626  " (%x/%x)",
627  Uint32(saturatedCommittedGCI >>32),
628  Uint32(saturatedCommittedGCI & 0xffffffff),
629  Uint32(expectedGCI >> 32),
630  Uint32(expectedGCI & 0xffffffff));
631  }
632 
633  if (loopCount > 0)
634  {
635  if (rowGCI64 != expectedGCI)
636  {
637  ndbout_c("MISMATCH : Expected GCI of %x/%x, but found %x/%x",
638  (Uint32) (expectedGCI >> 32),
639  (Uint32) (expectedGCI & 0xffffffff),
640  (Uint32) (rowGCI64 >> 32),
641  (Uint32) (rowGCI64 & 0xffffffff));
642  ndbout_c("At loopcount %llu", loopCount);
643  ndbout_c("Last good read GCI %x/%x",
644  Uint32(lastGoodReadGCI >> 32),
645  Uint32(lastGoodReadGCI & 0xffffffff));
646  ndbout_c("Read committed GCI : %x/%x",
647  Uint32(saturatedCommittedGCI >>32),
648  Uint32(saturatedCommittedGCI & 0xffffffff));
649  ndbout_c("Transaction coordinator node : %u",
650  trans->getConnectedNodeId());
651  return NDBT_FAILED;
652  }
653 
654  if (saturatedCommittedGCI != expectedGCI)
655  {
656  distinctCount++;
657  }
658  }
659 
660  expectedGCI = saturatedCommittedGCI;
661  lastGoodReadGCI = rowGCI64;
662 
663  hugoOps.closeTransaction(pNdb);
664  loopCount++;
665 
666  /* Sleep to avoid excessive updating */
667  NdbSleep_MilliSleep(10);
668  }
669 
670  ndbout_c("%llu updates with %llu distinct GCI values",
671  loopCount,
672  distinctCount);
673 
674  return result;
675 }
676 
677 NDBT_TESTSUITE(testRestartGci);
678 TESTCASE("InsertRestartGci",
679  "Verify that only expected records are still in NDB\n"
680  "after a restart" ){
681  INITIALIZER(runCreateTable);
682  INITIALIZER(runClearGlobals);
683  INITIALIZER(runInsertRememberGci);
684  INITIALIZER(runRestartGciControl);
685  INITIALIZER(runDetermineRestartGci);
686  TC_PROPERTY("ExactGCI", Uint32(0)); /* Recovery from Redo == inexact low word */
687  VERIFIER(runVerifyInserts);
688  /* Restart again - LCP after first restart will mean that this
689  * time we recover from LCP, not Redo
690  */
691  VERIFIER(runRestartAll);
692  VERIFIER(runDetermineRestartGci);
693  VERIFIER(runVerifyInserts); // Check GCIs again
694  /* Restart again - one node, initial. This will check
695  * COPYFRAG behaviour
696  */
697  VERIFIER(runRestartOneInitial);
698  VERIFIER(runVerifyInserts); // Check GCIs again
699  VERIFIER(runClearTable);
700  /* Re-fill table with records, will just be in Redo
701  * Then restart, testing COPYFRAG behaviour with
702  * non #ffff... low word
703  */
704  VERIFIER(runClearGlobals);
705  VERIFIER(runInsertRememberGci);
706  VERIFIER(runRestartOneInitial);
707  /* Require exact GCI match from here - no Redo messing it up */
708  VERIFIER(runRequireExact);
709  VERIFIER(runVerifyInserts);
710  /* Now-restart all nodes - all inserts should be
711  * in LCP, and should be restored correctly
712  */
713  VERIFIER(runRestartAll);
714  VERIFIER(runDetermineRestartGci);
715  VERIFIER(runVerifyInserts);
716  FINALIZER(runClearTable);
717  FINALIZER(runDropTable);
718 }
719 TESTCASE("InitialNodeRestartUpdate",
720  "Check that initial node restart (copyfrag) does "
721  "not affect GCI recording")
722 {
723  INITIALIZER(runCreateTable);
724  INITIALIZER(runLoadTable);
725  STEP(runNodeInitialRestarts);
726  STEP(runUpdateVerifyGCI);
727  FINALIZER(runClearTable);
728  FINALIZER(runDropTable);
729 }
730 NDBT_TESTSUITE_END(testRestartGci);
731 
732 int main(int argc, const char** argv){
733  ndb_init();
734  NDBT_TESTSUITE_INSTANCE(testRestartGci);
735  testRestartGci.setCreateTable(false);
736  return testRestartGci.execute(argc, argv);
737 }
738 
739 template class Vector<SavedRecord>;