/* Copyright (c) 2003, 2015, 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, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. 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, version 2.0, 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 "Bank.hpp" #include #include #include Bank::Bank(Ndb_cluster_connection& con, bool _init, const char * dbase): m_ndb(&con, dbase), m_maxAccount(-1), m_initialized(false), m_skip_create(false) { if(_init) init(); } int Bank::init(){ if (m_initialized == true) return NDBT_OK; myRandom48Init((long)NdbTick_CurrentMillisecond()); m_ndb.init(); if (m_ndb.waitUntilReady(30) != 0) { ndbout << "Ndb not ready" << endl; return NDBT_FAILED; } if (getNumAccounts() != NDBT_OK) return NDBT_FAILED; m_initialized = true; return NDBT_OK; } int Bank::performTransactions(int maxSleepBetweenTrans, int yield){ int transactions = 0; while(performTransaction() == NDBT_OK) { transactions++; if (maxSleepBetweenTrans > 0){ int val = myRandom48(maxSleepBetweenTrans); NdbSleep_MilliSleep(val); } if((transactions % 100) == 0) g_info << transactions << endl; if (yield != 0 && transactions >= yield) return NDBT_OK; } return NDBT_FAILED; } int Bank::performTransaction(){ int result = NDBT_OK; if (m_maxAccount <= 0){ g_err << "No accounts in bank" << endl; return NDBT_FAILED; } int fromAccount = myRandom48(m_maxAccount); int toAccount = myRandom48(m_maxAccount); if (fromAccount == toAccount){ // Increase toAccount with 1 toAccount = (toAccount+1)%m_maxAccount; } int maxAmount = getMaxAmount(); int amount = myRandom48(maxAmount); retry_transaction: int res = performTransaction(fromAccount, toAccount, amount); if (res != NDBT_OK){ switch (res){ case NDBT_FAILED: g_err << "performTransaction returned NDBT_FAILED" << endl << " fromAccount = " << fromAccount << endl << " toAccount = " << toAccount << endl << " amount = " << amount << endl; result = NDBT_FAILED; break; case NOT_ENOUGH_FUNDS: // ndbout << "performTransaction returned NOT_ENOUGH_FUNDS" << endl; break; case NDBT_TEMPORARY: g_err << "TEMPORARY_ERRROR retrying" << endl; NdbSleep_MilliSleep(50); goto retry_transaction; break; default: g_info << "performTransaction returned "<getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } /** * Check balance on from account */ NdbOperation* pOp = pTrans->getNdbOperation("ACCOUNT"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->readTupleExclusive(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->equal("ACCOUNT_ID", fromAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } NdbRecAttr* balanceFromRec = pOp->getValue("BALANCE"); if( balanceFromRec ==NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } NdbRecAttr* fromAccountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( fromAccountTypeRec == NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } /** * Read balance on to account */ NdbOperation* pOp6 = pTrans->getNdbOperation("ACCOUNT"); if (pOp6 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp6->readTupleExclusive(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp6->equal("ACCOUNT_ID", toAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } NdbRecAttr* balanceToRec = pOp6->getValue("BALANCE"); if( balanceToRec == NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } NdbRecAttr* toAccountTypeRec = pOp6->getValue("ACCOUNT_TYPE"); if( toAccountTypeRec == NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } Uint32 balanceFrom = balanceFromRec->u_32_value(); // ndbout << "balanceFrom: " << balanceFrom << endl; if (((Int64)balanceFrom - amount) < 0){ m_ndb.closeTransaction(pTrans); //ndbout << "Not enough funds" << endl; return NOT_ENOUGH_FUNDS; } Uint32 fromAccountType = fromAccountTypeRec->u_32_value(); Uint32 balanceTo = balanceToRec->u_32_value(); // ndbout << "balanceTo: " << balanceTo << endl; Uint32 toAccountType = toAccountTypeRec->u_32_value(); /** * Update balance on from account */ NdbOperation* pOp2 = pTrans->getNdbOperation("ACCOUNT"); if (pOp2 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp2->updateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp2->equal("ACCOUNT_ID", fromAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp2->setValue("BALANCE", balanceFrom - amount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } /** * Update balance on to account */ NdbOperation* pOp3 = pTrans->getNdbOperation("ACCOUNT"); if (pOp3 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp3->updateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp3->equal("ACCOUNT_ID", toAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp3->setValue("BALANCE", balanceTo + amount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } /** * Insert withdrawal transaction */ NdbOperation* pOp4 = pTrans->getNdbOperation("TRANSACTION"); if (pOp4 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->insertTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->equal("TRANSACTION_ID", transId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->equal("ACCOUNT", fromAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->setValue("ACCOUNT_TYPE", fromAccountType); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->setValue("OTHER_ACCOUNT", toAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->setValue("TRANSACTION_TYPE", WithDrawal); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->setValue("TIME", currTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp4->setValue("AMOUNT", amount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } /** * Insert deposit transaction */ NdbOperation* pOp5 = pTrans->getNdbOperation("TRANSACTION"); if (pOp5 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->insertTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->equal("TRANSACTION_ID", transId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->equal("ACCOUNT", toAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->setValue("ACCOUNT_TYPE", toAccountType); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->setValue("OTHER_ACCOUNT", fromAccountId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->setValue("TRANSACTION_TYPE", Deposit); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->setValue("TIME", currTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp5->setValue("AMOUNT", amount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pTrans); return NDBT_OK; } int Bank::performMakeGLs(int yield){ int result; int counter, maxCounter; int yieldCounter = 0; while (1){ // Counters to keep track of how many // GLs should be made before performing a validation counter = 0; maxCounter = 50 + myRandom48(100); /** * Validate GLs and Transactions for previous days. * Temporary 'validate' errors are ignored as they * will be retried in next round anyway. * */ result = performValidateGLs(); if (result != NDBT_OK){ if (result == VERIFICATION_FAILED){ g_err << "performValidateGLs verification failed" << endl; return NDBT_FAILED; } else if (result != NDBT_TEMPORARY){ g_err << "performValidateGLs failed: " << result << endl; return NDBT_FAILED; } g_info << "performValidateGLs skipped after temporary failure" << endl; } result = performValidatePurged(); if (result != NDBT_OK){ if (result == VERIFICATION_FAILED){ g_err << "performValidatePurged verification failed" << endl; return NDBT_FAILED; } else if (result != NDBT_TEMPORARY){ g_err << "performValidatePurged failed: " << result << endl; return NDBT_FAILED; } g_info << "performValidatePurged skipped after temporary failure" << endl; } while (1){ yieldCounter++; if (yield != 0 && yieldCounter >= yield) return NDBT_OK; /** * Find last GL time. * ( GL record with highest time value) */ Uint64 lastGLTime; if (findLastGL(lastGLTime) != NDBT_OK){ g_info << "findLastGL failed" << endl; // Break out of inner while loop break; } lastGLTime++; /** * If last GL time + 1 is smaller than current time * perform a GL for that time */ Uint64 currTime; if (getCurrTime(currTime) != NDBT_OK){ g_info << "getCurrTime failed" << endl; // Break out of inner while loop break; } if (lastGLTime < currTime){ counter++; if (performMakeGL(lastGLTime) != NDBT_OK){ g_info << "performMakeGL failed" << endl; // Break out of inner while loop break; } if (counter > maxCounter){ // Break out of inner while loop and // validatePreviousGLs g_info << "counter("< maxCounter("<getNdbScanOperation("GL"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuples() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; eof = pOp->nextResult(); lastTime = 0; while(eof == 0){ rows++; Uint64 t = timeRec->u_32_value(); if (t > lastTime) lastTime = t; eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); return NDBT_OK; } int Bank::performMakeGL(Uint64 time){ g_info << "performMakeGL: " << time << endl; /** * Create one GL record for each account type. * All in the same transaction */ // Start transaction NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL){ const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } for (int i = 0; i < getNumAccountTypes(); i++){ if (performMakeGLForAccountType(pTrans, time, i) != NDBT_OK){ g_err << "performMakeGLForAccountType returned NDBT_FAILED"<execute(Commit, AbortOnError) == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pTrans); return NDBT_OK; } int Bank::performMakeGLForAccountType(NdbConnection* pTrans, Uint64 glTime, Uint32 accountTypeId){ int check; Uint32 balance = 0; Uint32 withdrawalCount = 0; Uint32 withdrawalSum = 0; Uint32 depositSum = 0; Uint32 depositCount = 0; Uint32 countTransactions = 0; Uint32 purged = 0; // Insert record in GL so that we know // that no one else is performing the same task // Set purged = 0 to indicate that TRANSACTION // records still exist NdbOperation* pOp = pTrans->getNdbOperation("GL"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->insertTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("TIME", glTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("ACCOUNT_TYPE", accountTypeId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("BALANCE", balance); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("DEPOSIT_COUNT", depositCount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("DEPOSIT_SUM", depositSum); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("WITHDRAWAL_COUNT", withdrawalCount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("WITHDRAWAL_SUM", withdrawalSum); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->setValue("PURGED", purged); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pOp->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } // Read previous GL record to get old balance NdbOperation* pOp2 = pTrans->getNdbOperation("GL"); if (pOp2 == NULL) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp2->readTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp2->equal("TIME", glTime-1); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp2->equal("ACCOUNT_TYPE", accountTypeId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } NdbRecAttr* oldBalanceRec = pOp2->getValue("BALANCE"); if( oldBalanceRec == NULL ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pOp2->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } Uint32 oldBalance = oldBalanceRec->u_32_value(); // ndbout << "oldBalance = "<getNdbError()); return NDBT_FAILED; } check = pOp3->updateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->equal("TIME", glTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->equal("ACCOUNT_TYPE", accountTypeId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("BALANCE", balance); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("DEPOSIT_COUNT", depositCount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("DEPOSIT_SUM", depositSum); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("WITHDRAWAL_COUNT", withdrawalCount); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("WITHDRAWAL_SUM", withdrawalSum); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp3->setValue("PURGED", purged); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } // Execute transaction check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } return NDBT_OK; } int Bank::sumTransactionsForGL(const Uint64 glTime, const Uint32 accountType, Uint32& balance, Uint32& withdrawalCount, Uint32& withdrawalSum, Uint32& depositSum, Uint32& depositCount, Uint32& transactionsCount, NdbConnection* pTrans){ int check; // g_info << "sumTransactionsForGL: " << glTime << ", " << accountType << endl; NdbConnection* pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuplesExclusive()) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* transTypeRec = pOp->getValue("TRANSACTION_TYPE"); if( transTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* amountRec = pOp->getValue("AMOUNT"); if( amountRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; int rowsFound = 0; eof = pOp->nextResult(); while(eof == 0){ rows++; Uint32 a = accountTypeRec->u_32_value(); Uint64 t = timeRec->u_64_value(); if (a == accountType && t == glTime){ rowsFound++; // One record found int transType = transTypeRec->u_32_value(); int amount = amountRec->u_32_value(); if (transType == WithDrawal){ withdrawalCount++; withdrawalSum += amount; balance -= amount; } else { require(transType == Deposit); depositCount++; depositSum += amount; balance += amount; } } eof = pOp->nextResult(); if ((rows % 100) == 0){ // "refresh" ownner transaction every 100th row if (pTrans->refresh() == -1) { const NdbError err = pTrans->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err) return NDBT_FAILED; } } } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); // ndbout << rows << " TRANSACTIONS have been read" << endl; transactionsCount = rowsFound; return NDBT_OK; } int Bank::performValidateGLs(Uint64 age){ Uint64 currTime; if (getCurrTime(currTime) != NDBT_OK){ return NDBT_FAILED; } Uint64 glTime = currTime - 1; while((glTime > 0) && ((glTime + age) >= currTime)){ int result = performValidateGL(glTime); if (result != NDBT_OK){ g_err << "performValidateGL failed: " << result << endl; return result; } glTime--; } return NDBT_OK; } int Bank::performValidateGL(Uint64 glTime){ /** * Rules: * - There should be zero or NoAccountTypes GL records for each glTime * - If purged == 0, then the TRANSACTION table should be checked * to see that there are: * + DEPOSIT_COUNT deposit transactions with account_type == ACCOUNT_TYPE * and TIME == glTime. The sum of these transactions should be * DEPOSIT_SUM * + WITHDRAWAL_COUNT withdrawal transactions with account_type == * ACCOUNT_TYPE and TIME == glTime. The sum of these transactions * should be WITHDRAWAL_SUM * + BALANCE should be equal to the sum of all transactions plus * the balance of the previous GL record * - If purged == 1 then there should be NO transactions with TIME == glTime * and ACCOUNT_TYPE == account_type * */ int check; /** * SELECT * FROM GL WHERE account_type = @accountType and time = @time */ NdbConnection* pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuples() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* purgedRec = pOp->getValue("PURGED"); if( purgedRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* balanceRec = pOp->getValue("BALANCE"); if( balanceRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* depositSumRec = pOp->getValue("DEPOSIT_SUM"); if( depositSumRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* depositCountRec = pOp->getValue("DEPOSIT_COUNT"); if( depositCountRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* withdrawalSumRec = pOp->getValue("WITHDRAWAL_SUM"); if( withdrawalSumRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* withdrawalCountRec = pOp->getValue("WITHDRAWAL_COUNT"); if( withdrawalCountRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; int countGlRecords = 0; int result = NDBT_OK; eof = pOp->nextResult(); while(eof == 0){ rows++; Uint64 t = timeRec->u_64_value(); if (t == glTime){ countGlRecords++; Uint32 a = accountTypeRec->u_32_value(); Uint32 purged = purgedRec->u_32_value(); Uint32 wsum = withdrawalSumRec->u_32_value(); Uint32 wcount = withdrawalCountRec->u_32_value(); Uint32 dsum = depositSumRec->u_32_value(); Uint32 dcount = depositCountRec->u_32_value(); Uint32 b = balanceRec->u_32_value(); Uint32 balance = 0; Uint32 withdrawalSum = 0; Uint32 withdrawalCount = 0; Uint32 depositSum = 0; Uint32 depositCount = 0; Uint32 countTransactions = 0; if (purged == 0){ // If purged == 0, then the TRANSACTION table should be checked // to see that there are: // + DEPOSIT_COUNT deposit transactions with account_type == ACCOUNT_TYPE // and TIME == glTime. The sum of these transactions should be // DEPOSIT_SUM // + WITHDRAWAL_COUNT withdrawal transactions with account_type == // ACCOUNT_TYPE and TIME == glTime. The sum of these transactions // should be WITHDRAWAL_SUM // + BALANCE should be equal to the sum of all transactions plus // the balance of the previous GL record if (sumTransactionsForGL(t, a, balance, withdrawalCount, withdrawalSum, depositSum, depositCount, countTransactions, pScanTrans) != NDBT_OK){ result = NDBT_FAILED; } else { Uint32 prevBalance = 0; if (getBalanceForGL(t-1, a, prevBalance) != NDBT_OK){ result = NDBT_FAILED; } else if (((prevBalance + balance) != b) || (wsum != withdrawalSum) || (wcount != withdrawalCount) || (dsum != depositSum) || (dcount != depositCount)){ g_err << "performValidateGL, sums and counts failed" << endl << "balance : " << balance+prevBalance << "!="<nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); // - There should be zero or NoAccountTypes GL records for each glTime if ((countGlRecords != 0) && (countGlRecords != getNumAccountTypes())){ g_err << "performValidateGL: " << endl << "countGlRecords = " << countGlRecords << endl; result = VERIFICATION_FAILED; } return result; } int Bank::getBalanceForGL(const Uint64 glTime, const Uint32 accountTypeId, Uint32 &balance){ int check; NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbOperation* pOp = pTrans->getNdbOperation("GL"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->readTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("TIME", glTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("ACCOUNT_TYPE", accountTypeId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } NdbRecAttr* balanceRec = pOp->getValue("BALANCE"); if( balanceRec == NULL ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pTrans); balance = balanceRec->u_32_value(); return NDBT_OK; } int Bank::getOldestPurgedGL(const Uint32 accountType, Uint64 &oldest){ int check; /** * SELECT MAX(time) FROM GL WHERE account_type = @accountType and purged=1 */ NdbConnection* pScanTrans = 0; do { pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuples() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* purgedRec = pOp->getValue("PURGED"); if( purgedRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; eof = pOp->nextResult(); oldest = 0; while(eof == 0){ rows++; Uint32 a = accountTypeRec->u_32_value(); Uint32 p = purgedRec->u_32_value(); if (a == accountType && p == 1){ // One record found Uint64 t = timeRec->u_64_value(); if (t > oldest) oldest = t; } eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } break; } while(true); m_ndb.closeTransaction(pScanTrans); return NDBT_OK; } int Bank::getOldestNotPurgedGL(Uint64 &oldest, Uint32 &accountTypeId, bool &found){ int check; /** * SELECT time, accountTypeId FROM GL * WHERE purged=0 order by time asc */ NdbConnection* pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuples() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* purgedRec = pOp->getValue("PURGED"); if( purgedRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; eof = pOp->nextResult(); oldest = (Uint64)-1; found = false; while(eof == 0){ rows++; Uint32 p = purgedRec->u_32_value(); if (p == 0){ found = true; // One record found Uint32 a = accountTypeRec->u_32_value(); Uint64 t = timeRec->u_64_value(); if (t < oldest){ oldest = t; accountTypeId = a; } } eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); return NDBT_OK; } int Bank::checkNoTransactionsOlderThan(const Uint32 accountType, const Uint64 oldest){ /** * SELECT COUNT(transaction_id) FROM TRANSACTION * WHERE account_type = @accountType and time <= @oldest * */ int loop = 0; int found = 0; NdbConnection* pScanTrans = 0; do { int check; loop++; pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuples() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* transactionIdRec = pOp->getValue("TRANSACTION_ID"); if( transactionIdRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; found = 0; eof = pOp->nextResult(); while(eof == 0){ rows++; Uint32 a = accountTypeRec->u_32_value(); Uint32 t = timeRec->u_32_value(); if (a == accountType && t <= oldest){ // One record found Uint64 ti = transactionIdRec->u_64_value(); g_err << "checkNoTransactionsOlderThan found one record" << endl << " t = " << t << endl << " a = " << a << endl << " ti = " << ti << endl; found++; } eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } break; } while(true); m_ndb.closeTransaction(pScanTrans); if (found == 0) return NDBT_OK; else return VERIFICATION_FAILED; } int Bank::performValidatePurged(){ /** * Make sure there are no TRANSACTIONS older than the oldest * purged GL record * */ for (int i = 0; i < getNumAccountTypes(); i++){ Uint64 oldestGlTime; int result = getOldestPurgedGL(i, oldestGlTime); if (result != NDBT_OK){ g_err << "getOldestPurgedGL failed" << endl; return result; } result = checkNoTransactionsOlderThan(i, oldestGlTime); if (result != NDBT_OK){ g_err << "checkNoTransactionsOlderThan failed" << endl; return result; } } return NDBT_OK; } int Bank::purgeOldGLTransactions(Uint64 currTime, Uint32 age){ /** * For each GL record that are older than age and have purged == 0 * - delete all TRANSACTIONS belonging to the GL and set purged = 1 * * */ bool found; int count = 0; while(1){ count++; if (count > 100) return NDBT_OK; // Search for the oldest GL record with purged == 0 Uint64 oldestGlTime; Uint32 accountTypeId; if (getOldestNotPurgedGL(oldestGlTime, accountTypeId, found) != NDBT_OK){ g_err << "getOldestNotPurgedGL failed" << endl; return NDBT_FAILED; } if (found == false){ // ndbout << "not found" << endl; return NDBT_OK; } // ndbout << "purgeOldGLTransactions" << endl // << " oldestGlTime = " << oldestGlTime << endl // << " currTime = " << currTime << endl // << " age = " << age << endl; // Check if this GL is old enough to be purged if ((currTime < age) || (oldestGlTime > (currTime-age))){ // ndbout << "is not old enough" << endl; return NDBT_OK; } if (purgeTransactions(oldestGlTime, accountTypeId) != NDBT_OK){ g_err << "purgeTransactions failed" << endl; return NDBT_FAILED; } } g_err << "abnormal return" << endl; return NDBT_FAILED; } int Bank::purgeTransactions(const Uint64 glTime, const Uint32 accountTypeId) { int check; g_info << "purgeTransactions: " << glTime << ", "<getNdbOperation("GL"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->updateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("TIME", glTime); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } check = pOp->equal("ACCOUNT_TYPE", accountTypeId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } Uint32 purged = 1; check = pOp->setValue("PURGED", purged); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); return NDBT_FAILED; } // Execute transaction check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } // Find all transactions and take over them for delete if(findTransactionsToPurge(glTime, accountTypeId, pTrans) != NDBT_OK){ g_err << "findTransactionToPurge failed" << endl; m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pTrans); return NDBT_OK; } int Bank::findTransactionsToPurge(const Uint64 glTime, const Uint32 accountType, NdbConnection* pTrans){ int check; NdbConnection* pScanTrans = m_ndb.startTransaction(); if (pScanTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION"); if (pOp == NULL) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuplesExclusive() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* timeRec = pOp->getValue("TIME"); if( timeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE"); if( accountTypeRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; int rows = 0; int rowsFound = 0; eof = pOp->nextResult(); while(eof == 0){ rows++; Uint64 t = timeRec->u_64_value(); Uint32 a = accountTypeRec->u_32_value(); if (a == accountType && t == glTime){ rowsFound++; // One record found check = pOp->deleteCurrentTuple(pTrans); if (check == -1){ NDB_ERR(m_ndb.getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } // Execute transaction check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } } eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); // ndbout << rowsFound << " TRANSACTIONS have been deleted" << endl; return NDBT_OK; } int Bank::performIncreaseTime(int maxSleepBetweenDays, int yield) { int yieldCounter = 0; while(1){ Uint64 currTime; const int res = incCurrTime(currTime); if (res == NDBT_FAILED) break; if (res == NDBT_TEMPORARY) // Retry { NdbSleep_MilliSleep(50); continue; } g_info << "Current time is " << currTime << endl; if (maxSleepBetweenDays > 0){ int val = myRandom48(maxSleepBetweenDays); NdbSleep_SecSleep(val); } yieldCounter++; if (yield != 0 && yieldCounter >= yield) return NDBT_OK; } return NDBT_FAILED; } int Bank::readSystemValue(SystemValueId sysValId, Uint64 & value){ int check; NdbConnection* pTrans = 0; while (true) { pTrans = m_ndb.startTransaction(); if (pTrans == NULL) { const NdbError err = m_ndb.getNdbError(); if(err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } int result; if ((result= prepareReadSystemValueOp(pTrans, sysValId, value)) != NDBT_OK) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return result; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if(err.status == NdbError::TemporaryError) { NDB_ERR(err); NdbSleep_MilliSleep(50); continue; } NDB_ERR(err); return NDBT_FAILED; } break; } m_ndb.closeTransaction(pTrans); return NDBT_OK; } int Bank::prepareReadSystemValueOp(NdbConnection* pTrans, SystemValueId sysValId, Uint64 & value){ int check; NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp == NULL) { return NDBT_FAILED; } check = pOp->readTuple(); if( check == -1 ) { return NDBT_FAILED; } check = pOp->equal("SYSTEM_VALUES_ID", sysValId); if( check == -1 ) { return NDBT_FAILED; } NdbRecAttr* valueRec = pOp->getValue("VALUE", (char *)&value); if( valueRec == NULL ) { return NDBT_FAILED; } return NDBT_OK; } int Bank::writeSystemValue(SystemValueId sysValId, Uint64 value){ int check; NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL){ const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->insertTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->equal("SYSTEM_VALUES_ID", sysValId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->setValue("VALUE", value); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pTrans); return NDBT_OK; } int Bank::getNextTransactionId(Uint64 &value){ return increaseSystemValue2(LastTransactionId, value); } int Bank::incCurrTime(Uint64 &value){ return increaseSystemValue(CurrentTime, value); } int Bank::increaseSystemValue(SystemValueId sysValId, Uint64 &value){ /** * Increase value with one and return * updated value * */ DBUG_ENTER("Bank::increaseSystemValue"); int check; NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL){ const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); DBUG_RETURN(NDBT_TEMPORARY); } NDB_ERR(err); DBUG_RETURN(NDBT_FAILED); } NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp->readTupleExclusive(); // check = pOp->readTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp->equal("SYSTEM_VALUES_ID", sysValId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } NdbRecAttr* valueRec = pOp->getValue("VALUE"); if( valueRec ==NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); DBUG_RETURN(NDBT_TEMPORARY); } NDB_ERR(err); DBUG_RETURN(NDBT_FAILED); } value = valueRec->u_64_value(); value++; NdbOperation* pOp2 = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp2 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp2->updateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp2->equal("SYSTEM_VALUES_ID", sysValId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp2->setValue("VALUE", value); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); DBUG_RETURN(NDBT_TEMPORARY); } NDB_ERR(err); DBUG_RETURN(NDBT_FAILED); } NdbOperation* pOp3 = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp3 == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp3->readTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pOp3->equal("SYSTEM_VALUES_ID", sysValId); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } // Read new value NdbRecAttr* valueNewRec = pOp3->getValue("VALUE"); if( valueNewRec ==NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); DBUG_RETURN(NDBT_TEMPORARY); } NDB_ERR(err); DBUG_RETURN(NDBT_FAILED); } // Check that value updated equals the value we read after the update if (valueNewRec->u_64_value() != value){ printf("value actual=%lld\n", valueNewRec->u_64_value()); printf("value expected=%lld actual=%lld\n", value, valueNewRec->u_64_value()); DBUG_PRINT("info", ("value expected=%ld actual=%ld", (long)value, (long)valueNewRec->u_64_value())); g_err << "getNextTransactionId: value was not updated" << endl; m_ndb.closeTransaction(pTrans); DBUG_RETURN(NDBT_FAILED); } m_ndb.closeTransaction(pTrans); DBUG_RETURN(0); } int Bank::increaseSystemValue2(SystemValueId sysValId, Uint64 &value){ /** * Increase value with one and return * updated value * A more optimized version using interpreted update! * */ int check; NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL){ const NdbError err = m_ndb.getNdbError(); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES"); if (pOp == NULL) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->interpretedUpdateTuple(); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pOp->equal("SYSTEM_VALUES_ID", sysValId ); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } Uint32 valToIncWith = 1; check = pOp->incValue("VALUE", valToIncWith); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } NdbRecAttr* valueRec = pOp->getValue("VALUE"); if( valueRec == NULL ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } check = pTrans->execute(Commit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } value = valueRec->u_64_value(); m_ndb.closeTransaction(pTrans); return 0; } int Bank::getCurrTime(Uint64 &time){ return readSystemValue(CurrentTime, time); } int Bank::prepareGetCurrTimeOp(NdbConnection *pTrans, Uint64 &time){ return prepareReadSystemValueOp(pTrans, CurrentTime, time); } int Bank::performSumAccounts(int maxSleepBetweenSums, int yield){ int yieldCounter = 0; while (1){ Uint32 sumAccounts = 0; Uint32 numAccounts = 0; const int result = getSumAccounts(sumAccounts, numAccounts); if (result != NDBT_OK){ if (result == NDBT_TEMPORARY){ g_info << "getSumAccounts, retry after temporary failure" << endl; continue; } g_err << "getSumAccounts FAILED" << endl; return NDBT_FAILED; } else { g_info << "num="<getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } if( pOp->readTuplesExclusive() ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } NdbRecAttr* balanceRec = pOp->getValue("BALANCE"); if( balanceRec ==NULL ) { NDB_ERR(pScanTrans->getNdbError()); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } check = pScanTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } NdbConnection* pTrans = m_ndb.startTransaction(); if (pTrans == NULL) { const NdbError err = m_ndb.getNdbError(); m_ndb.closeTransaction(pScanTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } int eof; eof = pOp->nextResult(); while(eof == 0){ Uint32 b = balanceRec->u_32_value(); sumAccounts += b; numAccounts++; // ndbout << numAccounts << ": balance =" << b // << ", sum="<< sumAccounts << endl; // Take over the operation so that the lock is kept in db NdbOperation* pLockOp = pOp->updateCurrentTuple(pTrans); if (pLockOp == NULL){ NDB_ERR(m_ndb.getNdbError()); m_ndb.closeTransaction(pScanTrans); m_ndb.closeTransaction(pTrans); return NDBT_FAILED; } Uint32 illegalBalance = 99; check = pLockOp->setValue("BALANCE", illegalBalance); if( check == -1 ) { NDB_ERR(pTrans->getNdbError()); m_ndb.closeTransaction(pTrans); m_ndb.closeTransaction(pScanTrans); return NDBT_FAILED; } // Execute transaction check = pTrans->execute(NoCommit, AbortOnError); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } eof = pOp->nextResult(); } if (eof == -1) { const NdbError err = pScanTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } // TODO Forget about rolling back, just close pTrans!! // Rollback transaction check = pTrans->execute(Rollback); if( check == -1 ) { const NdbError err = pTrans->getNdbError(); m_ndb.closeTransaction(pScanTrans); m_ndb.closeTransaction(pTrans); if (err.status == NdbError::TemporaryError){ NDB_ERR(err); return NDBT_TEMPORARY; } NDB_ERR(err); return NDBT_FAILED; } m_ndb.closeTransaction(pScanTrans); m_ndb.closeTransaction(pTrans); return NDBT_OK; }