List:Commits« Previous MessageNext Message »
From:Jonas Oreland Date:September 12 2012 1:37pm
Subject:bzr push into mysql-5.1-telco-7.0-coord branch (jonas.oreland:4963 to 4964)
View as plain text  
 4964 Jonas Oreland	2012-09-12
      add missing file

    added:
      storage/ndb/test/ndbapi/testCoordTrans.cpp
 4963 Jonas Oreland	2012-09-12
      add missing file

    added:
      storage/ndb/src/common/debugger/signaldata/TcCommit.cpp
=== added file 'storage/ndb/test/ndbapi/testCoordTrans.cpp'
--- a/storage/ndb/test/ndbapi/testCoordTrans.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/test/ndbapi/testCoordTrans.cpp	2012-09-12 13:36:13 +0000
@@ -0,0 +1,1872 @@
+/*
+   Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include <NDBT_Test.hpp>
+#include <NDBT_ReturnCodes.h>
+#include <HugoTransactions.hpp>
+#include <UtilTransactions.hpp>
+#include <NdbRestarter.hpp>
+#include <signaldata/DumpStateOrd.hpp>
+
+extern unsigned opt_seed;
+
+/**
+ * adds rows randomly between 0 and records
+ */
+int
+runRandLoadTable(NDBT_Context* ctx, NDBT_Step* step)
+{
+  const int records = ctx->getNumRecords();
+  Ndb * pNdb = GETNDB(step);
+  HugoOperations hugoOps(*ctx->getTab());
+  hugoOps.startTransaction(pNdb);
+
+  int count = 0;
+  int batch = 0;
+  int retryAttempt = 0;
+  int maxRetries = 25;
+  for (int i = 0; i < records; i++)
+  {
+    if ((ndb_rand_r(&opt_seed) % 100) < 50)
+    {
+      int update_no = ndb_rand_r(&opt_seed);
+      hugoOps.pkWriteRecord(pNdb, i, 1, update_no);
+      batch++;
+      count++;
+    }
+
+    if (batch >= 128 || (batch > 0 && i == (records - 1)))
+    {
+      if (hugoOps.execute_Commit(pNdb) != 0)
+      {
+        /**
+         * TODO
+         */
+        NdbError err = hugoOps.getNdbError();
+        ERR(err);
+        if (err.status == NdbError::TemporaryError &&
+            retryAttempt < maxRetries)
+        {
+          i -= batch;
+          count -= batch;
+          retryAttempt++;
+          NdbSleep_MilliSleep(15);
+        }
+      }
+      hugoOps.closeTransaction(pNdb);
+      hugoOps.startTransaction(pNdb);
+      batch = 0;
+    }
+  }
+
+  assert(batch == 0);
+  hugoOps.closeTransaction(pNdb);
+
+  ndbout_c("loaded %u (of %u) rows", count, records);
+
+  return NDBT_OK;
+}
+
+class CoordTransTester
+{
+  /**
+   * This represents a row that we know the value of (i.e have the lock of)
+   */
+  struct Row
+  {
+    Row(int no, int val) {
+      row_no = no;
+      initial_deleted = current_deleted = false;
+      initial_updateno = current_updateno = val;
+    }
+
+    Row() {}
+
+    int row_no;
+
+    /**
+     * initial state of row (i.e for transaction(s) not participating)
+     */
+    bool initial_deleted;
+    int initial_updateno;
+
+    /**
+     * current state of row (i.e for transaction(s) participating)
+     */
+    bool current_deleted;
+    int current_updateno;
+  };
+
+  struct Participant
+  {
+    /**
+     * Does this participants "part" contain only operations
+     *   that does not need commit/rollback
+     *   (ndbapi will optimize away commit/rollback)
+     */
+    bool m_simple;
+
+    /**
+     * Does this participants "part" contain only reads
+     */
+    bool m_read_only;
+
+    Ndb * m_ndb;
+    NdbTransaction * m_trans;
+  };
+
+  unsigned m_seed;
+  unsigned m_max_row;
+  bool m_own_connection;
+
+  const NdbDictionary::Table * m_table;
+  NdbDictionary::Index * m_uk_index;
+  NdbDictionary::Index * m_oi_index;
+  Ndb_cluster_connection * m_cluster_connection;
+
+  char m_transid[NDBAPI_MAX_COORD_TRANSACTION_ID_SIZE];
+  Vector<Participant> m_trans;
+  Vector<Row> m_rows;
+
+  Vector<Uint32> m_actions; // Log of actions...
+public:
+  CoordTransTester (NDBT_Context* ctx,
+                    unsigned seed, int p, int rows, int threads);
+  ~CoordTransTester();
+
+  int setup();
+  int start();
+  int step();
+  int end();
+  int cleanup();
+
+  enum Action
+  {
+    A_BEGIN      = 0
+    ,A_JOIN      = 1
+    ,A_INSERT    = 2
+    ,A_UPDATE    = 3
+    ,A_DELETE    = 4
+    ,A_LOCK      = 5
+    ,A_PKREAD    = 6
+    ,A_UKREAD    = 7
+    ,A_TABLESCAN = 8
+    ,A_INDEXSCAN = 9
+    ,A_COMMIT    = 10
+    ,A_ROLLBACK  = 11
+    ,A_COMMIT_LEAVE = 12
+    ,A_DISCONNECT = 13
+    ,A_NF_COMMIT = 14
+    ,A_NF_ROLLBACK = 15
+
+    ,A_LAST
+  };
+
+  // based on state...returns bitmask of possible actions
+  Uint32 computePossibleActions(Uint32 weight[A_LAST]);
+
+  int do_begin();
+  int do_join();
+  int do_insert();
+  int do_update();
+  int do_delete();
+  int do_lock();
+  int do_pkread();
+  int do_ukread();
+  int do_tablescan();
+  int do_indexscan();
+  int do_commit();
+  int do_rollback();
+  int do_commit_leave();
+  int do_disconnect();
+  int do_nf_commit();
+  int do_nf_rollback();
+
+  void fatal(int from);
+
+  void do_close();
+  Participant * getRandomParticant();
+  Row * searchRow(int row_no);
+
+  unsigned getRandomRowNo() {
+    return (unsigned)ndb_rand_r(&m_seed) % m_max_row;
+  }
+  Row * getRandomRow() {
+    assert(m_rows.size() > 0);
+    int i = ndb_rand_r(&m_seed) % m_rows.size();
+    return &m_rows[i];
+  }
+
+  /**
+   * Distribution of actions
+   */
+  int m_action_pct[A_LAST];
+};
+
+CoordTransTester::CoordTransTester(NDBT_Context * ctx,
+                                   unsigned seed, int p, int rows, int threads)
+{
+  m_max_row = (unsigned)rows;
+  m_seed = seed;
+  m_table = ctx->getTab();
+  m_uk_index = 0;
+  m_oi_index = 0;
+  m_own_connection = (ctx->getProperty("OwnConnection", Uint32(0)) != 0);
+  if (m_own_connection == false)
+  {
+    m_cluster_connection = &ctx->m_cluster_connection;
+  }
+  else
+  {
+    m_cluster_connection = 0;
+  }
+
+  {
+    Participant t;
+    m_trans.fill(p - 1, t);
+  }
+
+  m_action_pct[A_BEGIN]     = 100 ;
+  m_action_pct[A_JOIN]      =  50 ;
+  m_action_pct[A_INSERT]    =  10 ;
+  m_action_pct[A_UPDATE]    =  10 ;
+  m_action_pct[A_DELETE]    =  10 ;
+  m_action_pct[A_LOCK]      =  10 ;
+  m_action_pct[A_PKREAD]    =  10 ;
+  m_action_pct[A_UKREAD]    =  10 ;
+  m_action_pct[A_TABLESCAN] =  10 ;
+  m_action_pct[A_INDEXSCAN] =  10 ;
+  m_action_pct[A_COMMIT]    =   5 ;
+  m_action_pct[A_ROLLBACK]  =   5 ;
+  m_action_pct[A_COMMIT_LEAVE] = 5 ;
+  m_action_pct[A_DISCONNECT] = 2 ;
+  m_action_pct[A_NF_COMMIT] = 0;
+  m_action_pct[A_NF_ROLLBACK] = 0 ;
+
+  const char * str[] = {
+    "BEGIN",
+    "JOIN",
+    "INSERT",
+    "UPDATE",
+    "DELETE",
+    "LOCK",
+    "PKREAD",
+    "UKREAD",
+    "TABLESCAN",
+    "INDEXSCAN",
+    "COMMIT",
+    "ROLLBACK",
+    "COMMIT_LEAVE",
+    "DISCONNECT",
+    "NF_COMMIT",
+    "NF_ROLLBACK",
+    0
+  };
+
+  for (Uint32 i = 0 ; str[i] != 0; i++)
+  {
+    BaseString search;
+    search.assfmt("%s_PCT", str[i]);
+    Uint32 pct = ctx->getProperty(search.c_str(), m_action_pct[i]);
+    m_action_pct[i] = pct;
+  }
+
+  /**
+   * only meaningfull with disconnect testing if using "own" connection
+   */
+  if (m_own_connection == 0)
+  {
+    m_action_pct[A_DISCONNECT] = 0;
+  }
+}
+
+CoordTransTester::~CoordTransTester()
+{
+}
+
+int
+CoordTransTester::setup()
+{
+  if (m_cluster_connection == 0)
+  {
+    for (Uint32 i = 0; i < 3; i++)
+    {
+      Ndb_cluster_connection * xncc = new Ndb_cluster_connection;
+      xncc->set_auto_reconnect(false);
+      if (xncc->connect(30, 1, 0) != 0)
+      {
+        delete xncc;
+        NdbSleep_MilliSleep(200);
+        continue;
+      }
+
+      if (xncc->wait_until_ready(30, 10) != 0)
+      {
+        delete xncc;
+        NdbSleep_MilliSleep(200);
+        continue;
+      }
+
+      m_cluster_connection = xncc;
+      break;
+    }
+
+    if (m_cluster_connection == 0)
+    {
+      return NDBT_FAILED;
+    }
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::start()
+{
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    Ndb* xndb = new Ndb(m_cluster_connection, "TEST_DB");
+    if (xndb == 0)
+    {
+      return NDBT_FAILED;
+    }
+    if (xndb->init(10) != 0)
+    {
+      delete xndb;
+      return NDBT_FAILED;
+    }
+
+    if (xndb->waitUntilReady(0) != 0)
+    {
+      delete xndb;
+      return NDBT_FAILED;
+    }
+
+    m_trans[i].m_simple = true;
+    m_trans[i].m_read_only = true;
+    m_trans[i].m_ndb = xndb;
+    m_trans[i].m_trans = 0;
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::step()
+{
+  Uint32 weights[A_LAST];
+  Uint32 sum = computePossibleActions(weights);
+
+  Uint32 randval = ((Uint32)ndb_rand_r(&m_seed) % sum);
+  Uint32 action = 0;
+  while (randval >= weights[action])
+  {
+    randval -= weights[action];
+    action++;
+  }
+
+  m_actions.push_back(action);
+
+  int res = -1;
+  switch(action){
+  case A_BEGIN:
+    res = do_begin();
+    break;
+  case A_JOIN:
+    res = do_join();
+    break;
+  case A_INSERT:
+    res = do_insert();
+    break;
+  case A_UPDATE:
+    res = do_update();
+    break;
+  case A_DELETE:
+    res = do_delete();
+    break;
+  case A_LOCK:
+    res = do_lock();
+    break;
+  case A_PKREAD:
+    res = do_pkread();
+    break;
+  case A_UKREAD:
+    res = do_ukread();
+    break;
+  case A_TABLESCAN:
+    res = do_tablescan();
+    break;
+  case A_INDEXSCAN:
+    res = do_indexscan();
+    break;
+  case A_COMMIT:
+    res = do_commit();
+    break;
+  case A_ROLLBACK:
+    res = do_rollback();
+    break;
+  case A_COMMIT_LEAVE:
+    res = do_commit_leave();
+    break;
+  case A_DISCONNECT:
+    res = do_disconnect();
+    break;
+  case A_NF_COMMIT:
+    res = do_nf_commit();
+    break;
+  case A_NF_ROLLBACK:
+    res = do_nf_rollback();
+    break;
+  }
+  assert(res != -1);
+
+  return res;
+}
+
+const int retries = 50;
+const int retrysleep = 50; // ms
+
+int
+CoordTransTester::do_begin()
+{
+  assert(m_trans[0].m_trans == 0);
+  for (int i = 0; i < retries; i++)
+  {
+    Participant * p = &m_trans[0];
+    if ((p->m_trans = p->m_ndb->startTransaction()) == 0)
+    {
+      const NdbError err = p->m_ndb->getNdbError();
+      if (err.status != NdbError::TemporaryError)
+      {
+        ERR(err);
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+      NdbSleep_MilliSleep(retrysleep);
+      continue;
+    }
+
+    m_trans[0].m_simple = true;
+    m_trans[0].m_read_only = true;
+    m_trans[0].m_trans->getCoordinatedTransactionId(m_transid,
+                                                    sizeof(m_transid));
+
+    /**
+     * Currently...the transaction is not "usable"/started until
+     *   atleast one operation has been performed on it...
+     *   do a committedread on a random key
+     *
+     * TODO: We could lift this restriction!!
+     */
+    unsigned row_no = getRandomRowNo();
+    HugoOperations hugoOps(* m_table);
+    hugoOps.setTransaction(p->m_trans);
+    hugoOps.pkReadRecord(p->m_ndb, row_no, 1, NdbOperation::LM_CommittedRead);
+    hugoOps.setTransaction(NULL, true);
+
+    const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+    int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+    if (res == 0)
+    {
+      /**
+       * ok...
+       */
+      break;
+    }
+
+    const NdbError opErr = op->getNdbError();
+    const NdbError transErr = p->m_trans->getNdbError();
+    if (opErr.status != NdbError::TemporaryError &&
+        transErr.status != NdbError::TemporaryError)
+    {
+      ERR(opErr);
+      ERR(transErr);
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+
+    p->m_trans->close();
+    p->m_trans = 0;
+    NdbSleep_MilliSleep(retrysleep);
+  }
+
+  if (m_trans[0].m_trans == 0)
+  {
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::do_join()
+{
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans == 0)
+    {
+      /**
+       *
+       */
+      NdbTransaction * pTrans = m_trans[i].m_ndb->joinTransaction(m_transid);
+      if ((m_trans[i].m_trans = pTrans) == 0)
+      {
+        const NdbError err = m_trans[i].m_ndb->getNdbError();
+        if (err.status != NdbError::TemporaryError)
+        {
+          ERR(err);
+          fatal(__LINE__);
+          return NDBT_FAILED;
+        }
+
+        NdbSleep_MilliSleep(retrysleep);
+        return do_rollback();
+      }
+      else
+      {
+        /**
+         * OK
+         */
+        m_trans[i].m_simple = true;
+        m_trans[i].m_read_only = true;
+        return 0;
+      }
+    }
+  }
+  assert(false);
+  return 0;
+}
+
+int
+CoordTransTester::do_insert()
+{
+  unsigned row_no = getRandomRowNo();
+  unsigned update_no = ndb_rand_r(&m_seed);
+
+  Participant * p = getRandomParticant();
+  p->m_simple = false;
+  p->m_read_only = false;
+  Row * r = searchRow(row_no);
+
+  HugoOperations hugoOps(* m_table);
+  hugoOps.setTransaction(p->m_trans);
+  hugoOps.pkWriteRecord(p->m_ndb, row_no, 1, update_no);
+  hugoOps.setTransaction(NULL, true);
+
+  const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+  int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+  if (res == -1)
+  {
+    if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+    {
+      /**
+       * Any trans may get temporary error...
+       */
+      NdbSleep_MilliSleep(retrysleep);
+      return do_rollback();
+    }
+  }
+
+  if (op->getNdbError().status == NdbError::TemporaryError)
+  {
+    /**
+     * Any trans may get temporary error...
+     */
+    NdbSleep_MilliSleep(retrysleep);
+    return do_rollback();
+  }
+
+  if (r != 0)
+  {
+    /**
+     * we "owned" the row...should be ok to WRITE it again
+     */
+    if (res != 0)
+    {
+      ERR(op->getNdbError());
+      ERR(p->m_trans->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+    r->current_deleted = false;
+    r->current_updateno = update_no;
+    return 0;
+  }
+
+  if (res == 0)
+  {
+    if (op->getNdbError().code == 0)
+    {
+      /**
+       * insert ok
+       */
+      Row r(row_no, update_no);
+      m_rows.push_back(r);
+      return 0;
+    }
+    else
+    {
+      /**
+       * insert failed...but transaction is alive => ok too
+       */
+      ERR(op->getNdbError());
+      ERR(p->m_trans->getNdbError());
+      return 0;
+    }
+  }
+  else
+  {
+    ERR(op->getNdbError());
+    ERR(p->m_trans->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  assert(false);
+
+  return 0;
+}
+
+int
+CoordTransTester::do_update()
+{
+  assert(m_rows.size() > 0);
+
+  Row * r = getRandomRow();
+  Participant * p = getRandomParticant();
+  p->m_simple = false;
+  p->m_read_only = false;
+  unsigned update_no = ndb_rand_r(&m_seed);
+
+  HugoOperations hugoOps(* m_table);
+  hugoOps.setTransaction(p->m_trans);
+  hugoOps.pkUpdateRecord(p->m_ndb, r->row_no, 1, update_no);
+  hugoOps.setTransaction(NULL, true);
+
+  const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+  int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+  if (res == -1)
+  {
+    if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+    {
+      /**
+       * Any trans may get temporary error...
+       */
+      NdbSleep_MilliSleep(retrysleep);
+      return do_rollback();
+    }
+  }
+
+  if (res != 0)
+  {
+    ERR(op->getNdbError());
+    ERR(p->m_trans->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  if (op->getNdbError().status == NdbError::TemporaryError)
+  {
+    /**
+     * Any trans may get temporary error...
+     */
+    NdbSleep_MilliSleep(retrysleep);
+    return do_rollback();
+  }
+
+  if (r->current_deleted == true)
+  {
+    /**
+     * this should be a failure...
+     */
+    if (op->getNdbError().status != NdbError::PermanentError)
+    {
+      ERR(op->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+  else
+  {
+    if (op->getNdbError().status != NdbError::Success)
+    {
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+
+  r->current_updateno = update_no;
+  return 0;
+}
+
+int
+CoordTransTester::do_delete()
+{
+  assert(m_rows.size() > 0);
+
+  Row * r = getRandomRow();
+  Participant * p = getRandomParticant();
+  p->m_simple = false;
+  p->m_read_only = false;
+
+  HugoOperations hugoOps(* m_table);
+  hugoOps.setTransaction(p->m_trans);
+  hugoOps.pkDeleteRecord(p->m_ndb, r->row_no, 1);
+  hugoOps.setTransaction(NULL, true);
+
+  const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+  int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+  if (res == -1)
+  {
+    if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+    {
+      /**
+       * Any trans may get temporary error...
+       */
+      NdbSleep_MilliSleep(retrysleep);
+      return do_rollback();
+    }
+  }
+
+  if (res != 0)
+  {
+    ERR(op->getNdbError());
+    ERR(p->m_trans->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  if (op->getNdbError().status == NdbError::TemporaryError)
+  {
+    /**
+     * Any trans may get temporary error...
+     */
+    NdbSleep_MilliSleep(retrysleep);
+    return do_rollback();
+  }
+
+  if (r->current_deleted == true)
+  {
+    /**
+     * this should be a failure...
+     */
+    if (op->getNdbError().status != NdbError::PermanentError)
+    {
+      ERR(op->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+  else
+  {
+    if (op->getNdbError().status != NdbError::Success)
+    {
+      ERR(op->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+
+  r->current_deleted = true;
+  return 0;
+}
+
+int
+CoordTransTester::do_lock()
+{
+  unsigned row_no = getRandomRowNo();
+  Participant * p = getRandomParticant();
+  p->m_simple = false;
+
+  NDBT_ResultRow row(* m_table);
+  HugoCalculator calc(* m_table);
+
+  NdbOperation * pOp = p->m_trans->getNdbOperation(m_table);
+  pOp->readTuple(NdbOperation::LM_Read);
+
+  {
+    HugoOperations ops(* m_table);
+    ops.equalForRow(pOp, row_no);
+  }
+
+  for (int i = 0; i < m_table->getNoOfColumns(); i++)
+  {
+    NdbRecAttr * attr = pOp->getValue(m_table->getColumn(i)->getName());
+    if (attr == 0)
+    {
+      ERR(pOp->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+
+    row.attributeStore(i) = attr;
+  }
+
+  const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+  int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+  if (res == -1)
+  {
+    if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+    {
+      /**
+       * Any trans may get temporary error...
+       */
+      NdbSleep_MilliSleep(retrysleep);
+      return do_rollback();
+    }
+  }
+
+  if (res != 0)
+  {
+    ERR(op->getNdbError());
+    ERR(p->m_trans->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  if (op->getNdbError().status == NdbError::TemporaryError)
+  {
+    /**
+     * Any trans may get temporary error...
+     */
+    NdbSleep_MilliSleep(retrysleep);
+    return do_rollback();
+  }
+
+  if (op->getNdbError().code == 0)
+  {
+    /**
+     * Row round!!
+     */
+    if (calc.verifyRowValues(&row) != 0)
+    {
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+
+    int row_no = calc.getIdValue(&row);
+    Row * r = searchRow(row_no);
+    if (r)
+    {
+      if (r->current_deleted)
+      {
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+      else if (calc.getUpdatesValue(&row) != r->current_updateno)
+      {
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+    }
+    else
+    {
+      /**
+       * We now "own" row
+       */
+      Row r(row_no, calc.getUpdatesValue(&row));
+      m_rows.push_back(r);
+    }
+  }
+  else if (pOp->getNdbError().classification == NdbError::NoDataFound)
+  {
+    /**
+     * Row not found
+     */
+    Row * r = searchRow(row_no);
+    if (r)
+    {
+      if (!r->current_deleted)
+      {
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+    }
+  }
+  else if (pOp->getNdbError().status == NdbError::TemporaryError)
+  {
+    return do_rollback();
+  }
+  else
+  {
+    ERR(op->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::do_pkread()
+{
+  assert(m_rows.size() > 0);
+
+  Row * r = getRandomRow();
+  Participant * p = getRandomParticant();
+
+  HugoOperations hugoOps(* m_table);
+  hugoOps.setTransaction(p->m_trans);
+  hugoOps.pkReadRecord(p->m_ndb, r->row_no, 1, NdbOperation::LM_CommittedRead);
+  hugoOps.setTransaction(NULL, true);
+
+  const NdbOperation * op = p->m_trans->getLastDefinedOperation();
+  int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+  if (res == -1)
+  {
+    if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+    {
+      /**
+       * Any trans may get temporary error...
+       */
+      NdbSleep_MilliSleep(retrysleep);
+      return do_rollback();
+    }
+  }
+
+  if (res != 0)
+  {
+    ERR(op->getNdbError());
+    ERR(p->m_trans->getNdbError());
+    fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  if (r->current_deleted == true)
+  {
+    /**
+     * this should be a failure...
+     */
+    if (op->getNdbError().status != NdbError::PermanentError)
+    {
+      ERR(op->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+  else
+  {
+    if (op->getNdbError().status != NdbError::Success)
+    {
+      ERR(op->getNdbError());
+      fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::do_ukread()
+{
+  assert(m_rows.size() > 0);
+  return 0;
+}
+
+int
+CoordTransTester::do_tablescan()
+{
+  assert(m_rows.size() > 0);
+
+  int line = 0;
+  NDBT_ResultRow row(* m_table);
+  HugoCalculator calc(* m_table);
+
+  Participant * p = getRandomParticant();
+  NdbScanOperation * pOp = 0;
+  do
+  {
+    if ((pOp = p->m_trans->getNdbScanOperation(m_table)) == 0)
+    {
+      line = __LINE__;
+      break;
+    }
+
+    if (pOp->readTuples(NdbOperation::LM_CommittedRead))
+    {
+      line = __LINE__;
+      break;
+    }
+
+    for (int i = 0; i < m_table->getNoOfColumns(); i++)
+    {
+      NdbRecAttr * attr = pOp->getValue(m_table->getColumn(i)->getName());
+      if (attr == 0)
+      {
+        line = __LINE__;
+        goto do_err;
+      }
+
+      row.attributeStore(i) = attr;
+    }
+
+    int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+
+    if (res == -1)
+    {
+      if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+      {
+        /**
+         * Any trans may get temporary error...
+         */
+        NdbSleep_MilliSleep(retrysleep);
+        return do_rollback();
+      }
+      line = __LINE__;
+      break;
+    }
+
+    while ((res = pOp->nextResult()) == 0)
+    {
+      if (calc.verifyRowValues(&row) != 0)
+      {
+        fatal(__LINE__);
+        pOp->close();
+        return NDBT_FAILED;
+      }
+
+      int row_no = calc.getIdValue(&row);
+      Row * r = searchRow(row_no);
+      if (r)
+      {
+        if (r->current_deleted)
+        {
+          fatal(__LINE__);
+          pOp->close();
+          return NDBT_FAILED;
+        }
+        else if (calc.getUpdatesValue(&row) != r->current_updateno)
+        {
+          fatal(__LINE__);
+          pOp->close();
+          return NDBT_FAILED;
+        }
+      }
+    }
+
+    if (res == 1) // EOF
+    {
+      /**
+       * OK
+       */
+      pOp->close();
+      return 0;
+    }
+
+    if (res == -1)
+    {
+      if (p->m_trans->getNdbError().status == NdbError::TemporaryError)
+      {
+        /**
+         * Any trans may get temporary error...
+         */
+        NdbSleep_MilliSleep(retrysleep);
+        return do_rollback();
+      }
+      line = __LINE__;
+      break;
+    }
+
+  } while (0);
+
+do_err:
+  NdbError err;
+  if (pOp)
+  {
+    err = pOp->getNdbError();
+    pOp->close();
+    pOp = 0;
+  }
+  else
+  {
+    err = p->m_trans->getNdbError();
+  }
+
+  if (err.status == NdbError::TemporaryError)
+  {
+    /**
+     * Any trans may get temporary error...
+     */
+    NdbSleep_MilliSleep(retrysleep);
+    return do_rollback();
+  }
+
+  ERR(err);
+  fatal(__LINE__);
+  return NDBT_FAILED;
+}
+
+int
+CoordTransTester::do_indexscan()
+{
+  assert(m_rows.size() > 0);
+  return 0;
+}
+
+static
+void
+commit_callback(int res, NdbTransaction* pTrans, void * callback_obj)
+{
+  int *res_ptr= (int *)callback_obj;
+  *res_ptr= res;
+}
+
+int
+CoordTransTester::do_commit()
+{
+  Vector<int> res;
+  {
+    int tmp = 0;
+    res.fill(m_trans.size() - 1, tmp);
+  }
+
+  // send all
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      Ndb * pNdb = m_trans[i].m_ndb;
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      pTrans->executeAsynchPrepare(Commit,
+                                   commit_callback,
+                                   &res[i]);
+      pNdb->sendPreparedTransactions();
+    }
+  }
+
+  // wait all
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      while(m_trans[i].m_ndb->pollNdb(100000) == 0)
+        ;
+    }
+  }
+
+  do_close();
+  return 0;
+}
+
+int
+CoordTransTester::do_rollback()
+{
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      m_trans[i].m_trans->execute(Rollback);
+    }
+  }
+
+  do_close();
+  return 0;
+}
+
+int
+CoordTransTester::do_commit_leave()
+{
+  // send all
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      int res = pTrans->execute(CommitOrLeave);
+      if (res == -1)
+      {
+        if (pTrans->getNdbError().status == NdbError::TemporaryError)
+        {
+          /**
+           * Any trans may get temporary error...
+           */
+          NdbSleep_MilliSleep(retrysleep);
+          return do_rollback();
+        }
+      }
+    }
+  }
+
+  do_close();
+  return 0;
+}
+
+int
+CoordTransTester::do_disconnect()
+{
+  NdbRestarter res;
+  Uint32 tcNodeId = 0;
+  Uint32 ownNodeId = m_cluster_connection->node_id();
+
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      tcNodeId = pTrans->getConnectedNodeId();
+      break;
+    }
+  }
+
+  int dump[] = { 900, (int)ownNodeId };
+  res.dumpStateOneNode(tcNodeId, dump, 2);
+
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      int res = pTrans->execute(Commit);
+      if (m_trans[i].m_simple)
+      {
+        /**
+         * can be get and not get problem...
+         */
+      }
+      else
+      {
+        if (res != -1)
+        {
+          fatal(__LINE__);
+          return NDBT_FAILED;
+        }
+      }
+    }
+  }
+
+  do_close();
+
+  // delete all Ndb objects
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_ndb != 0)
+    {
+      delete m_trans[i].m_ndb;
+      m_trans[i].m_ndb = 0;
+    }
+  }
+
+  // delete cluster connection
+  delete m_cluster_connection;
+  m_cluster_connection = 0;
+
+  /**
+   * now reconnect
+   */
+  return setup() || start();
+}
+
+CoordTransTester::Participant *
+CoordTransTester::getRandomParticant()
+{
+  unsigned part = (unsigned)ndb_rand_r(&m_seed) % (5 * m_trans.size());
+  for (unsigned i = 0; ; i = (i + 1) % m_trans.size())
+  {
+    if (m_trans[i].m_trans == 0)
+    {
+      continue;
+    }
+    else if (part == 0)
+    {
+      return &m_trans[i];
+    }
+    else
+    {
+      part--;
+    }
+  }
+}
+
+int
+CoordTransTester::do_nf_commit()
+{
+  /**
+   * 1) start committing
+   *    crash TC when receiving COMMITTED
+   *
+   *    this tests take-over
+   *
+   * 2) start committing
+   *    crash non-tc when receiving COMPLETE
+   *
+   *    this tests TC coordinated trans
+   */
+  NdbRestarter res;
+  int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+
+  int tcNodeId = 0;
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      tcNodeId = (int)pTrans->getConnectedNodeId();
+      break;
+    }
+  }
+
+  ndbout_c("error 8020");
+  res.insertErrorInNode(tcNodeId, 8020);
+  if (res.dumpStateOneNode(tcNodeId, val2, 2))
+  {
+    return NDBT_FAILED;
+  }
+
+  Vector<int> ret;
+  {
+    int tmp = 0;
+    ret.fill(m_trans.size() - 1, tmp);
+  }
+
+  // send all
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      Ndb * pNdb = m_trans[i].m_ndb;
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      pTrans->executeAsynchPrepare(Commit,
+                                   commit_callback,
+                                   &ret[i]);
+      pNdb->sendPreparedTransactions();
+    }
+  }
+
+  // wait all
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      while(m_trans[i].m_ndb->pollNdb(100000) == 0)
+        ;
+    }
+  }
+
+  /**
+   * Now check result of transaction...
+   */
+  {
+    Uint32 cnt_rw = 0;
+    Uint32 cnt_simple = 0;
+    Uint32 cnt_started = 0;
+    for (Uint32 i = 0; i < m_trans.size(); i++)
+    {
+      if (m_trans[i].m_trans != 0)
+      {
+        cnt_started++;
+        if (m_trans[i].m_simple == true)
+        {
+          cnt_simple++;
+        }
+        if (m_trans[i].m_read_only == false)
+        {
+          assert(m_trans[i].m_simple == false);
+          cnt_rw++;
+        }
+      }
+    }
+
+    Uint32 cnt_ok = 0;
+    Uint32 cnt_nok = 0;
+    for (Uint32 i = 0; i < ret.size(); i++)
+    {
+      if (ret[i] == -1)
+        cnt_nok++;
+      if (ret[i] == 0)
+        cnt_ok++;
+    }
+
+    ndbout_c("cnt_started: %u cnt_simple: %u cnt_rw: %u cnt_ok: %u cnt_nok: %u",
+             cnt_started, cnt_simple, cnt_rw, cnt_ok, cnt_nok);
+
+    /**
+     * - simple transactions should get OK
+     *   since they don't have any operation outstanding
+     *   and commit then gets optimized away
+     *
+     * - if there is atleast one RW, then one should get OK too
+     *
+     * - if they are all RO...then it's 50%-50% wheather they get OK or not
+     *
+     */
+    if (cnt_rw > 0)
+    {
+      if (! (cnt_ok == cnt_simple + 1))
+      {
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+    }
+    else
+    {
+      if (! (cnt_ok >= cnt_simple))
+      {
+        fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+    }
+  }
+
+  do_close();
+
+  ndbout_c("wait no start");
+  res.waitNodesNoStart(&tcNodeId, 1);
+  ndbout_c("start");
+  res.startNodes(&tcNodeId, 1);
+  ndbout_c("wait cluster started");
+  res.waitClusterStarted();
+  ndbout_c("started ok");
+
+  return 0;
+}
+
+int
+CoordTransTester::do_nf_rollback()
+{
+  /**
+   * 1) node-failure that gives rollback
+   * 2) node-failure during rollback
+   */
+  NdbRestarter res;
+  int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 };
+
+  int tcNodeId = 0;
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      NdbTransaction * pTrans = m_trans[i].m_trans;
+      tcNodeId = (int)pTrans->getConnectedNodeId();
+      break;
+    }
+  }
+
+  unsigned c = (ndb_rand_r(&m_seed) & 64) == 64;
+  if (c && (m_rows.size() > 0))
+  {
+    ndbout_c("error 8029");
+    res.insertErrorInNode(tcNodeId, 8029);
+    if (res.dumpStateOneNode(tcNodeId, val2, 2))
+    {
+      return NDBT_FAILED;
+    }
+
+    /**
+     * run an op...crash TC when receiving LQHKEYCONF
+     */
+    Row * r = getRandomRow();
+    Participant * p = getRandomParticant();
+    HugoOperations hugoOps(* m_table);
+    hugoOps.setTransaction(p->m_trans);
+    hugoOps.pkWriteRecord(p->m_ndb, r->row_no, 1, r->current_updateno);
+    hugoOps.setTransaction(NULL, true);
+
+    int res = p->m_trans->execute(NoCommit, AO_IgnoreError);
+    if (res != -1)
+    {
+      return NDBT_FAILED;
+    }
+  }
+  else
+  {
+    ndbout_c("error 8024");
+    res.insertErrorInNode(tcNodeId, 8024);
+    if (res.dumpStateOneNode(tcNodeId, val2, 2))
+    {
+      return NDBT_FAILED;
+    }
+
+    for (Uint32 i = 0; i < m_trans.size(); i++)
+    {
+      if (m_trans[i].m_trans != 0)
+      {
+        m_trans[i].m_trans->execute(Rollback);
+      }
+    }
+  }
+
+  do_close();
+
+  ndbout_c("wait no start");
+  res.waitNodesNoStart(&tcNodeId, 1);
+  ndbout_c("start");
+  res.startNodes(&tcNodeId, 1);
+  ndbout_c("wait cluster started");
+  res.waitClusterStarted();
+  ndbout_c("started ok");
+
+  return do_rollback();
+}
+
+CoordTransTester::Row *
+CoordTransTester::searchRow(int row_no)
+{
+  for (unsigned i = 0; i < m_rows.size(); i++)
+  {
+    if (m_rows[i].row_no == row_no)
+    {
+      return &m_rows[i];
+    }
+  }
+  return 0;
+}
+
+void
+CoordTransTester::do_close()
+{
+  if (1)
+  {
+    BaseString actions;
+    actions.appfmt("%s", m_transid);
+    for (Uint32 i = 0; i < m_actions.size(); i++)
+    {
+      actions.appfmt(" %u", m_actions[i]);
+    }
+    printf("%s\n", actions.c_str());
+  }
+
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      m_trans[i].m_trans->close();
+      m_trans[i].m_trans = 0;
+    }
+  }
+
+  m_rows.clear();
+  m_actions.clear();
+}
+
+int
+CoordTransTester::end()
+{
+  do_rollback();
+
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_ndb != 0)
+    {
+      delete m_trans[i].m_ndb;
+      m_trans[i].m_ndb = 0;
+    }
+  }
+
+  return 0;
+}
+
+int
+CoordTransTester::cleanup()
+{
+  if (m_own_connection)
+  {
+    if (m_cluster_connection != 0)
+    {
+      delete m_cluster_connection;
+      m_cluster_connection = 0;
+    }
+  }
+  return 0;
+}
+
+Uint32
+CoordTransTester::computePossibleActions(Uint32 weights[A_LAST])
+{
+  bzero(weights, sizeof(weights[0]) * A_LAST);
+  Uint32 sum = 0;
+  Uint32 cnt_rw = 0;
+  Uint32 cnt_started = 0;
+  for (Uint32 i = 0; i < m_trans.size(); i++)
+  {
+    if (m_trans[i].m_trans != 0)
+    {
+      cnt_started++;
+      if (m_trans[i].m_simple == false)
+      {
+        cnt_rw++;
+      }
+    }
+  }
+
+  if (cnt_started == 0)
+  {
+    /**
+     * we must start a transaction first
+     */
+    sum += (weights[A_BEGIN] = m_action_pct[A_BEGIN]);
+    return sum;
+  }
+
+  if (cnt_started < m_trans.size())
+  {
+    /**
+     * Not all started....
+     */
+    sum += (weights[A_JOIN] = m_action_pct[A_JOIN]);
+  }
+
+  /**
+   * If we have transaction...we can always commit/rollback...
+   *   but it's only interesting if we have performed say 3 steps...
+   */
+  if (m_actions.size() > (1 + m_trans.size()))
+  {
+    /**
+     * Let likelihood of commit/rollback increase when size of trans increase
+     */
+    Uint32 k = 10;
+    Uint32 c = 10 * m_actions.size();
+    Uint32 d = 100;
+    sum += (weights[A_COMMIT] = (k + c * m_action_pct[A_COMMIT]) / d);
+    sum += (weights[A_ROLLBACK] = (k + c * m_action_pct[A_ROLLBACK]) / d);
+    sum += (weights[A_COMMIT_LEAVE] = (k + c * m_action_pct[A_COMMIT_LEAVE])/d);
+    if (cnt_rw > 0)
+    {
+      sum += (weights[A_NF_COMMIT] = (k + c * m_action_pct[A_NF_COMMIT])/d);
+      sum += (weights[A_NF_ROLLBACK] = (k + c * m_action_pct[A_NF_ROLLBACK])/d);
+    }
+  }
+
+  /**
+   * We can "always" try to insert...or lock a random row
+   */
+  sum += (weights[A_INSERT] = m_action_pct[A_INSERT]);
+  sum += (weights[A_LOCK] = m_action_pct[A_LOCK]);
+
+  if (m_rows.size())
+  {
+    sum += (weights[A_UPDATE] = m_action_pct[A_UPDATE]);
+    sum += (weights[A_DELETE] = m_action_pct[A_DELETE]);
+    sum += (weights[A_PKREAD] = m_action_pct[A_PKREAD]);
+    if (m_uk_index != 0)
+    {
+      sum += (weights[A_UKREAD] = m_action_pct[A_UKREAD]);
+    }
+    sum += (weights[A_TABLESCAN] = m_action_pct[A_TABLESCAN]);
+
+    if (m_oi_index != 0)
+    {
+      sum += (weights[A_INDEXSCAN] = m_action_pct[A_INDEXSCAN]);
+    }
+  }
+
+  if (cnt_started > 1)
+  {
+    sum += (weights[A_DISCONNECT] = m_action_pct[A_DISCONNECT]);
+  }
+
+  return sum;
+}
+
+void
+CoordTransTester::fatal(int line)
+{
+  BaseString actions;
+  actions.appfmt("%s", m_transid);
+  for (Uint32 i = 0; i < m_actions.size(); i++)
+  {
+    actions.appfmt(" %u", m_actions[i]);
+  }
+  printf("%s\n", actions.c_str());
+
+  printf(">>>abort line: %u : actions: %s<<<\n",
+         line,
+         actions.c_str());
+  fflush(stdout);
+  abort();
+}
+
+
+int
+runCoordTransBasic(NDBT_Context* ctx, NDBT_Step* step)
+{
+  const int nParticipants = ctx->getProperty("Participants", Uint32(2));
+  const int nThreads = ctx->getNoOfRunningSteps();
+  const int nRecords = ctx->getNumRecords();
+  const int nLoops = ctx->getNumLoops();
+  const int stepNo = step->getStepNo();
+  const unsigned seed = opt_seed + (unsigned)(stepNo * 0x37);
+  const int untilStopped = ctx->getProperty("UNTIL_STOPPED", Uint32(0));
+
+  if (ctx->getProperty("NF_COMMIT_PCT", Uint32(0)) != 0)
+  {
+    NdbRestarter res;
+    if (res.getNumDbNodes() < 2)
+    {
+      return NDBT_OK;
+    }
+  }
+
+  CoordTransTester tester(ctx, seed, nParticipants, nRecords, nThreads);
+  if (tester.setup())
+  {
+    tester.fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+
+  for (int i = 0; (i < nLoops || untilStopped) && !ctx->isTestStopped(); i++)
+  {
+    if (tester.start() != 0)
+    {
+      tester.fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+
+    Uint64 start = NdbTick_CurrentMillisecond();
+    do
+    {
+      if (tester.step() != 0)
+      {
+        tester.fatal(__LINE__);
+        return NDBT_FAILED;
+      }
+    } while (NdbTick_CurrentMillisecond() < (start + 6 * 1000) &&
+             !ctx->isTestStopped());
+
+    if (tester.end() != 0)
+    {
+      tester.fatal(__LINE__);
+      return NDBT_FAILED;
+    }
+  }
+
+  if (tester.cleanup() != 0)
+  {
+    tester.fatal(__LINE__);
+    return NDBT_FAILED;
+  }
+  return NDBT_OK;
+}
+
+int
+runRestarter(NDBT_Context* ctx, NDBT_Step* step)
+{
+  int loops = ctx->getNumLoops();
+  int randnode = ctx->getProperty("RandNode", (unsigned)0);
+  const int stepNo = step->getStepNo();
+  unsigned seed = opt_seed + (unsigned)(stepNo * 0x37);
+  NdbRestarter res;
+
+  if (res.getNumDbNodes() < 2)
+  {
+    ctx->stopTest();
+    return NDBT_OK;
+  }
+
+  if (res.waitClusterStarted() != 0)
+  {
+    g_err << "Cluster failed to start" << endl;
+    return NDBT_FAILED;
+  }
+
+  if (loops < res.getNumDbNodes())
+  {
+    loops = res.getNumDbNodes();
+  }
+
+  for (int i = 0; i < loops && !ctx->isTestStopped(); i++)
+  {
+    int id = i % res.getNumDbNodes();
+    if (randnode == 1)
+    {
+      id = ndb_rand_r(&seed) % res.getNumDbNodes();
+    }
+    int nodeId = res.getDbNodeId(id);
+    ndbout << "Restart node " << nodeId << endl;
+    if (res.restartOneDbNode(nodeId, false, true, true) != 0)
+    {
+      g_err << "Failed to restartNextDbNode" << endl;
+      return NDBT_FAILED;
+    }
+
+    if (res.waitNodesNoStart(&nodeId, 1))
+    {
+      g_err << "Failed to waitNodesNoStart" << endl;
+      return NDBT_FAILED;
+    }
+
+    if (res.startNodes(&nodeId, 1))
+    {
+      g_err << "Failed to start node" << endl;
+      return NDBT_FAILED;
+    }
+
+    if(res.waitClusterStarted() != 0)
+    {
+      g_err << "Cluster failed to start" << endl;
+      return NDBT_FAILED;
+    }
+  }
+
+  ctx->stopTest();
+
+  return NDBT_OK;
+}
+
+NDBT_TESTSUITE(testCoordTrans);
+TESTCASE("Basic","")
+{
+  INITIALIZER(runRandLoadTable);
+  STEP(runCoordTransBasic);
+}
+TESTCASE("Multi","")
+{
+  INITIALIZER(runRandLoadTable);
+  STEPS(runCoordTransBasic, 15);
+}
+TESTCASE("BasicAndDisconnect","")
+{
+  TC_PROPERTY("OwnConnection", 1);
+  INITIALIZER(runRandLoadTable);
+  STEPS(runCoordTransBasic, 2); // only 3 default configured api-slots...
+}
+TESTCASE("BasicAndNF","")
+{
+  TC_PROPERTY("COMMIT_PCT", Uint32(0));
+  TC_PROPERTY("ROLLBACK_PCT", Uint32(0));
+  TC_PROPERTY("COMMIT_LEAVE_PCT", Uint32(0));
+  TC_PROPERTY("NF_COMMIT_PCT", 5);
+  TC_PROPERTY("NF_ROLLBACK_PCT", 5);
+  INITIALIZER(runRandLoadTable);
+  STEPS(runCoordTransBasic, 1);
+}
+TESTCASE("MultiNF","")
+{
+  TC_PROPERTY("UNTIL_STOPPED", Uint32(1));
+  INITIALIZER(runRandLoadTable);
+  STEPS(runCoordTransBasic, 10);
+  STEP(runRestarter);
+}
+NDBT_TESTSUITE_END(testCoordTrans);
+
+int
+main(int argc, const char** argv)
+{
+  ndb_init();
+  NDBT_TESTSUITE_INSTANCE(testCoordTrans);
+  return testCoordTrans.execute(argc, argv);
+}
+
+template class Vector<CoordTransTester::Participant>;
+template class Vector<CoordTransTester::Row>;

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.1-telco-7.0-coord branch (jonas.oreland:4963 to 4964) Jonas Oreland13 Sep