mirror of
https://github.com/MariaDB/server.git
synced 2025-01-20 05:52:27 +01:00
d5627f53c3
optimized for fewer timeouts changed for consistency in "time" tests moved SR_UNDO tests to basic added testBackup -n BackupBank
2470 lines
59 KiB
C++
2470 lines
59 KiB
C++
/* Copyright (C) 2003 MySQL AB
|
|
|
|
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; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
#include "Bank.hpp"
|
|
#include <time.h>
|
|
#include <NdbSleep.h>
|
|
#include <UtilTransactions.hpp>
|
|
|
|
Bank::Bank():
|
|
m_ndb("BANK"),
|
|
m_maxAccount(-1),
|
|
m_initialized(false)
|
|
{
|
|
|
|
}
|
|
|
|
int Bank::init(){
|
|
if (m_initialized == true)
|
|
return NDBT_OK;
|
|
|
|
myRandom48Init(NdbTick_CurrentMillisecond());
|
|
|
|
m_ndb.init();
|
|
while (m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
if (getNumAccounts() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int Bank::performTransactions(int maxSleepBetweenTrans, int yield){
|
|
|
|
if (init() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
int transactions = 0;
|
|
|
|
while(1){
|
|
|
|
while(m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
while(performTransaction() != NDBT_FAILED){
|
|
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 != 0){
|
|
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;
|
|
goto retry_transaction;
|
|
break;
|
|
default:
|
|
g_info << "performTransaction returned "<<res << endl;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Perform a transaction in the bank.
|
|
* Ie. transfer money from one account to another.
|
|
*
|
|
* @param
|
|
* @return 0 if successful or an error code
|
|
*/
|
|
int Bank::performTransaction(int fromAccountId,
|
|
int toAccountId,
|
|
int amount ){
|
|
/**
|
|
* 1. Start transaction
|
|
* 2. Check balance on from account, if there is
|
|
* not enough funds abort transaction
|
|
* 3. Update ACCOUNT set balance = balance - amount on
|
|
* from account
|
|
* 4. Insert withdrawal in TRANSACTION
|
|
* 5. Insert deposit in transaction
|
|
* 6. Update ACCOUNT set balance = balance + amount on
|
|
* to account
|
|
* 7. Commit transaction
|
|
*/
|
|
// g_info << "performTransaction " << fromAccountId
|
|
// << ", "<<toAccountId<<", "<<amount << endl;
|
|
|
|
// Call the first implementation of this trans
|
|
// In the future we can have several different versions of this trans
|
|
// and call them randomly
|
|
return performTransactionImpl1(fromAccountId, toAccountId, amount);
|
|
}
|
|
|
|
|
|
int Bank::performTransactionImpl1(int fromAccountId,
|
|
int toAccountId,
|
|
int amount ){
|
|
|
|
int check;
|
|
|
|
// Ok, all clear to do the transaction
|
|
Uint64 transId;
|
|
if (getNextTransactionId(transId) != NDBT_OK){
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pTrans = m_ndb.startTransaction();
|
|
|
|
if( pTrans == NULL ) {
|
|
const NdbError err = m_ndb.getNdbError();
|
|
if (err.status == NdbError::TemporaryError){
|
|
ERR(err);
|
|
return NDBT_TEMPORARY;
|
|
}
|
|
ERR(err);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint64 currTime;
|
|
if (prepareGetCurrTimeOp(pTrans, currTime) != NDBT_OK){
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
* Check balance on from account
|
|
*/
|
|
NdbOperation* pOp = pTrans->getNdbOperation("ACCOUNT");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->readTupleExclusive();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("ACCOUNT_ID", fromAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* balanceFromRec = pOp->getValue("BALANCE");
|
|
if( balanceFromRec ==NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* fromAccountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( fromAccountTypeRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
* Read balance on to account
|
|
*/
|
|
NdbOperation* pOp6 = pTrans->getNdbOperation("ACCOUNT");
|
|
if (pOp6 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp6->readTupleExclusive();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp6->equal("ACCOUNT_ID", toAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* balanceToRec = pOp6->getValue("BALANCE");
|
|
if( balanceToRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* toAccountTypeRec = pOp6->getValue("ACCOUNT_TYPE");
|
|
if( toAccountTypeRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
const NdbError err = pTrans->getNdbError();
|
|
m_ndb.closeTransaction(pTrans);
|
|
if (err.status == NdbError::TemporaryError){
|
|
ERR(err);
|
|
return NDBT_TEMPORARY;
|
|
}
|
|
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) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->updateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->equal("ACCOUNT_ID", fromAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->setValue("BALANCE", balanceFrom - amount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
* Update balance on to account
|
|
*/
|
|
NdbOperation* pOp3 = pTrans->getNdbOperation("ACCOUNT");
|
|
if (pOp3 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->updateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->equal("ACCOUNT_ID", toAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("BALANCE", balanceTo + amount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
* Insert withdrawal transaction
|
|
*/
|
|
NdbOperation* pOp4 = pTrans->getNdbOperation("TRANSACTION");
|
|
if (pOp4 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->insertTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->equal("TRANSACTION_ID", transId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->equal("ACCOUNT", fromAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->setValue("ACCOUNT_TYPE", fromAccountType);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->setValue("OTHER_ACCOUNT", toAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->setValue("TRANSACTION_TYPE", WithDrawal);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->setValue("TIME", currTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp4->setValue("AMOUNT", amount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
* Insert deposit transaction
|
|
*/
|
|
NdbOperation* pOp5 = pTrans->getNdbOperation("TRANSACTION");
|
|
if (pOp5 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->insertTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->equal("TRANSACTION_ID", transId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->equal("ACCOUNT", toAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->setValue("ACCOUNT_TYPE", toAccountType);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->setValue("OTHER_ACCOUNT", fromAccountId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->setValue("TRANSACTION_TYPE", Deposit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->setValue("TIME", currTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp5->setValue("AMOUNT", amount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
const NdbError err = pTrans->getNdbError();
|
|
m_ndb.closeTransaction(pTrans);
|
|
if (err.status == NdbError::TemporaryError){
|
|
ERR(err);
|
|
return NDBT_TEMPORARY;
|
|
}
|
|
ERR(err);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
int Bank::performMakeGLs(int yield){
|
|
int result;
|
|
if (init() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
|
|
int counter, maxCounter;
|
|
int yieldCounter = 0;
|
|
|
|
while (1){
|
|
// Counters to keep tracck of how many
|
|
// GLs should be made before performing a validation
|
|
counter = 0;
|
|
maxCounter = 50 + myRandom48(100);
|
|
|
|
while(m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
/**
|
|
* Validate GLs and Transactions for previous days
|
|
*
|
|
*/
|
|
result = performValidateGLs();
|
|
if (result != NDBT_OK){
|
|
if (result == VERIFICATION_FAILED){
|
|
g_err << "performValidateGLs verification failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
g_info << "performValidateGLs failed" << endl;
|
|
continue;
|
|
}
|
|
|
|
result = performValidatePurged();
|
|
if (result != NDBT_OK){
|
|
if (result == VERIFICATION_FAILED){
|
|
g_err << "performValidatePurged verification failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
g_info << "performValidatePurged failed" << endl;
|
|
continue;
|
|
}
|
|
|
|
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("<<counter<<") > maxCounter("<<maxCounter<<")" << endl;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
;//ndbout << "It's not time to make GL yet" << endl;
|
|
|
|
// ndbout << "Sleeping 1 second" << endl;
|
|
NdbSleep_SecSleep(1);
|
|
|
|
}
|
|
|
|
Uint32 age = 3;
|
|
if (purgeOldGLTransactions(currTime, age) != NDBT_OK){
|
|
g_info << "purgeOldGLTransactions failed" << endl;
|
|
// Break out of inner while loop
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return NDBT_FAILED;
|
|
|
|
}
|
|
|
|
int Bank::performValidateAllGLs(){
|
|
int result;
|
|
if (init() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
|
|
while (1){
|
|
|
|
while(m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
/**
|
|
* Validate GLs and Transactions for previous days
|
|
* Set age so that ALL GL's are validated
|
|
*/
|
|
int age = 100000;
|
|
result = performValidateGLs(age);
|
|
if (result != NDBT_OK){
|
|
if (result == VERIFICATION_FAILED){
|
|
g_err << "performValidateGLs verification failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
g_err << "performValidateGLs failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
result = performValidatePurged();
|
|
if (result != NDBT_OK){
|
|
if (result == VERIFICATION_FAILED){
|
|
g_err << "performValidatePurged verification failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
g_err << "performValidatePurged failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
return NDBT_OK;
|
|
}
|
|
|
|
return NDBT_FAILED;
|
|
|
|
}
|
|
|
|
int Bank::findLastGL(Uint64 &lastTime){
|
|
|
|
int check;
|
|
/**
|
|
* SELECT MAX(time) FROM GL
|
|
*/
|
|
NdbConnection* pScanTrans = m_ndb.startTransaction();
|
|
if (pScanTrans == NULL) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuples();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
eof = rs->nextResult();
|
|
lastTime = 0;
|
|
|
|
while(eof == 0){
|
|
rows++;
|
|
Uint64 t = timeRec->u_32_value();
|
|
|
|
if (t > lastTime)
|
|
lastTime = t;
|
|
|
|
eof = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
|
|
int Bank::performMakeGL(int 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){
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
for (int i = 0; i < getNumAccountTypes(); i++){
|
|
|
|
if (performMakeGLForAccountType(pTrans, time, i) != NDBT_OK){
|
|
g_err << "performMakeGLForAccountType returned NDBT_FAILED"<<endl;
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
// Execute transaction
|
|
if( pTrans->execute(Commit) == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
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) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->insertTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("TIME", glTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("BALANCE", balance);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("DEPOSIT_COUNT", depositCount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("DEPOSIT_SUM", depositSum);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("WITHDRAWAL_COUNT", withdrawalCount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("WITHDRAWAL_SUM", withdrawalSum);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("PURGED", purged);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pOp->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Read previous GL record to get old balance
|
|
NdbOperation* pOp2 = pTrans->getNdbOperation("GL");
|
|
if (pOp2 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->readTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->equal("TIME", glTime-1);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp2->equal("ACCOUNT_TYPE", accountTypeId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* oldBalanceRec = pOp2->getValue("BALANCE");
|
|
if( oldBalanceRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pOp2->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint32 oldBalance = oldBalanceRec->u_32_value();
|
|
// ndbout << "oldBalance = "<<oldBalance<<endl;
|
|
balance = oldBalance;
|
|
// Start a scan transaction to search
|
|
// for TRANSACTION records with TIME = time
|
|
// and ACCOUNT_TYPE = accountTypeId
|
|
// Build sum of all found transactions
|
|
|
|
if (sumTransactionsForGL(glTime,
|
|
accountTypeId,
|
|
balance,
|
|
withdrawalCount,
|
|
withdrawalSum,
|
|
depositSum,
|
|
depositCount,
|
|
countTransactions,
|
|
pTrans) != NDBT_OK){
|
|
return NDBT_FAILED;
|
|
}
|
|
// ndbout << "sumTransactionsForGL completed" << endl;
|
|
// ndbout << "balance="<<balance<<endl
|
|
// << "withdrawalCount="<<withdrawalCount<<endl
|
|
// << "withdrawalSum="<<withdrawalSum<<endl
|
|
// << "depositCount="<<depositCount<<endl
|
|
// << "depositSum="<<depositSum<<endl;
|
|
|
|
|
|
|
|
NdbOperation* pOp3 = pTrans->getNdbOperation("GL");
|
|
if (pOp3 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->updateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->equal("TIME", glTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->equal("ACCOUNT_TYPE", accountTypeId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("BALANCE", balance);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("DEPOSIT_COUNT", depositCount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("DEPOSIT_SUM", depositSum);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("WITHDRAWAL_COUNT", withdrawalCount);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("WITHDRAWAL_SUM", withdrawalSum);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp3->setValue("PURGED", purged);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Execute transaction
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
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) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuplesExclusive();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* transTypeRec = pOp->getValue("TRANSACTION_TYPE");
|
|
if( transTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* amountRec = pOp->getValue("AMOUNT");
|
|
if( amountRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
int rowsFound = 0;
|
|
eof = rs->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 {
|
|
assert(transType == Deposit);
|
|
depositCount++;
|
|
depositSum += amount;
|
|
balance += amount;
|
|
}
|
|
}
|
|
|
|
eof = rs->nextResult();
|
|
|
|
if ((rows % 100) == 0){
|
|
// "refresh" ownner transaction every 100th row
|
|
if (pTrans->refresh() == -1) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
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" << endl;
|
|
return result;
|
|
}
|
|
|
|
glTime--;
|
|
}
|
|
|
|
return NDBT_OK;
|
|
}
|
|
|
|
int Bank::performValidateGL(Uint64 glTime){
|
|
|
|
ndbout << "performValidateGL: " << glTime << endl;
|
|
/**
|
|
* 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) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuples();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* purgedRec = pOp->getValue("PURGED");
|
|
if( purgedRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
|
|
if( balanceRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* depositSumRec = pOp->getValue("DEPOSIT_SUM");
|
|
if( depositSumRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* depositCountRec = pOp->getValue("DEPOSIT_COUNT");
|
|
if( depositCountRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* withdrawalSumRec = pOp->getValue("WITHDRAWAL_SUM");
|
|
if( withdrawalSumRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
NdbRecAttr* withdrawalCountRec = pOp->getValue("WITHDRAWAL_COUNT");
|
|
if( withdrawalCountRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
int countGlRecords = 0;
|
|
int result = NDBT_OK;
|
|
eof = rs->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 << "!="<<b<<endl
|
|
<< "with sum : " << withdrawalSum << "!="<<wsum<<endl
|
|
<< "with count: " << withdrawalCount << "!="<<wcount<<endl
|
|
<< "dep sum : " << depositSum << "!="<<dsum<<endl
|
|
<< "dep count : " << depositCount << "!="<<dcount<<endl;
|
|
result = VERIFICATION_FAILED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
assert(purged == 1);
|
|
// If purged == 1 then there should be NO transactions with
|
|
// TIME == glTime and ACCOUNT_TYPE == account_type
|
|
|
|
if (sumTransactionsForGL(t,
|
|
a,
|
|
balance,
|
|
withdrawalCount,
|
|
withdrawalSum,
|
|
depositSum,
|
|
depositCount,
|
|
countTransactions,
|
|
pScanTrans) != NDBT_OK){
|
|
result = NDBT_FAILED;
|
|
} else {
|
|
if (countTransactions != 0){
|
|
g_err << "performValidateGL, countTransactions("<<countTransactions<<") != 0" << endl;
|
|
result = VERIFICATION_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
eof = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
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) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pTrans->getNdbOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->readTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("TIME", glTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
|
|
if( balanceRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
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 = m_ndb.startTransaction();
|
|
if (pScanTrans == NULL) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuples();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* purgedRec = pOp->getValue("PURGED");
|
|
if( purgedRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
eof = rs->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 = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
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) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuples();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* purgedRec = pOp->getValue("PURGED");
|
|
if( purgedRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
eof = rs->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 = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
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 check;
|
|
NdbConnection* pScanTrans = m_ndb.startTransaction();
|
|
if (pScanTrans == NULL) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuples();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* transactionIdRec = pOp->getValue("TRANSACTION_ID");
|
|
if( transactionIdRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
int found = 0;
|
|
eof = rs->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 = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
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++){
|
|
ndbout << "performValidatePurged: " << i << endl;
|
|
Uint64 oldestGlTime;
|
|
if (getOldestPurgedGL(i, oldestGlTime) != NDBT_OK){
|
|
g_err << "getOldestPurgedGL failed" << endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
int 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 << ", "<<accountTypeId<<endl;
|
|
NdbConnection* pTrans = m_ndb.startTransaction();
|
|
if (pTrans == NULL){
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Start by updating the GL record with purged = 1, use NoCommit
|
|
NdbOperation* pOp = pTrans->getNdbOperation("GL");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->updateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("TIME", glTime);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("ACCOUNT_TYPE", accountTypeId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint32 purged = 1;
|
|
check = pOp->setValue("PURGED", purged);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Execute transaction
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
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);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
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) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("TRANSACTION");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuplesExclusive();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* timeRec = pOp->getValue("TIME");
|
|
if( timeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* accountTypeRec = pOp->getValue("ACCOUNT_TYPE");
|
|
if( accountTypeRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
int rows = 0;
|
|
int rowsFound = 0;
|
|
eof = rs->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 = rs->deleteTuple(pTrans);
|
|
if (check == -1){
|
|
ERR(m_ndb.getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Execute transaction
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
}
|
|
eof = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
// ndbout << rowsFound << " TRANSACTIONS have been deleted" << endl;
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|
|
|
|
|
|
int Bank::performIncreaseTime(int maxSleepBetweenDays, int yield){
|
|
if (init() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
|
|
int yieldCounter = 0;
|
|
|
|
while(1){
|
|
|
|
while(m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
while(1){
|
|
|
|
Uint64 currTime;
|
|
if (incCurrTime(currTime) != NDBT_OK)
|
|
break;
|
|
|
|
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 = m_ndb.startTransaction();
|
|
if (pTrans == NULL){
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (prepareReadSystemValueOp(pTrans, sysValId, value) != NDBT_OK) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
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){
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->insertTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("SYSTEM_VALUES_ID", sysValId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->setValue("VALUE", value);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
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){
|
|
ERR(m_ndb.getNdbError());
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp->readTupleExclusive();
|
|
// check = pOp->readTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp->equal("SYSTEM_VALUES_ID", sysValId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
NdbRecAttr* valueRec = pOp->getValue("VALUE");
|
|
if( valueRec ==NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
value = valueRec->u_64_value();
|
|
value++;
|
|
|
|
NdbOperation* pOp2 = pTrans->getNdbOperation("SYSTEM_VALUES");
|
|
if (pOp2 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp2->updateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp2->equal("SYSTEM_VALUES_ID", sysValId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp2->setValue("VALUE", value);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
NdbOperation* pOp3 = pTrans->getNdbOperation("SYSTEM_VALUES");
|
|
if (pOp3 == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp3->readTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pOp3->equal("SYSTEM_VALUES_ID", sysValId);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
// Read new value
|
|
NdbRecAttr* valueNewRec = pOp3->getValue("VALUE");
|
|
if( valueNewRec ==NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
DBUG_RETURN(NDBT_FAILED);
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
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", value, 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){
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbOperation* pOp = pTrans->getNdbOperation("SYSTEM_VALUES");
|
|
if (pOp == NULL) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpretedUpdateTuple();
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->equal("SYSTEM_VALUES_ID", sysValId );
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
Uint32 valToIncWith = 1;
|
|
check = pOp->incValue("VALUE", valToIncWith);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* valueRec = pOp->getValue("VALUE");
|
|
if( valueRec == NULL ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pTrans->execute(Commit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
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){
|
|
if (init() != NDBT_OK)
|
|
return NDBT_FAILED;
|
|
|
|
int yieldCounter = 0;
|
|
|
|
while (1){
|
|
|
|
while (m_ndb.waitUntilReady(10) != 0)
|
|
ndbout << "Waiting for ndb to be ready" << endl;
|
|
|
|
Uint32 sumAccounts = 0;
|
|
Uint32 numAccounts = 0;
|
|
if (getSumAccounts(sumAccounts, numAccounts) != NDBT_OK){
|
|
g_err << "getSumAccounts FAILED" << endl;
|
|
} else {
|
|
|
|
g_info << "num="<<numAccounts<<", sum=" << sumAccounts << endl;
|
|
|
|
if (sumAccounts != (10000000 + (10000*(numAccounts-1)))){
|
|
g_err << "performSumAccounts FAILED" << endl
|
|
<< " sumAccounts="<<sumAccounts<<endl
|
|
<< " expected ="<<(10000000 + (10000*(numAccounts-1)))<<endl
|
|
<< " numAccounts="<<numAccounts<<endl;
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
if (maxSleepBetweenSums > 0){
|
|
int val = myRandom48(maxSleepBetweenSums);
|
|
NdbSleep_MilliSleep(val);
|
|
}
|
|
}
|
|
|
|
yieldCounter++;
|
|
if (yield != 0 && yieldCounter >= yield)
|
|
return NDBT_OK;
|
|
}
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
|
|
int Bank::getSumAccounts(Uint32 &sumAccounts,
|
|
Uint32 &numAccounts){
|
|
|
|
// SELECT SUM(balance) FROM ACCOUNT
|
|
|
|
int check;
|
|
NdbConnection* pScanTrans = m_ndb.startTransaction();
|
|
if (pScanTrans == NULL) {
|
|
ERR(m_ndb.getNdbError());
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbScanOperation* pOp = pScanTrans->getNdbScanOperation("ACCOUNT");
|
|
if (pOp == NULL) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbResultSet * rs = pOp->readTuplesExclusive();
|
|
if( rs == 0 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pOp->interpret_exit_ok();
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbRecAttr* balanceRec = pOp->getValue("BALANCE");
|
|
if( balanceRec ==NULL ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
check = pScanTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
NdbConnection* pTrans = m_ndb.startTransaction();
|
|
if (pTrans == NULL) {
|
|
ERR(m_ndb.getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
int eof;
|
|
eof = rs->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 = rs->updateTuple(pTrans);
|
|
if (pLockOp == NULL){
|
|
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 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pTrans);
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// Execute transaction
|
|
check = pTrans->execute(NoCommit);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
eof = rs->nextResult();
|
|
}
|
|
if (eof == -1) {
|
|
ERR(pScanTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
// TODO Forget about rolling back, just close pTrans!!
|
|
|
|
// Rollback transaction
|
|
check = pTrans->execute(Rollback);
|
|
if( check == -1 ) {
|
|
ERR(pTrans->getNdbError());
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
m_ndb.closeTransaction(pTrans);
|
|
return NDBT_FAILED;
|
|
}
|
|
|
|
m_ndb.closeTransaction(pScanTrans);
|
|
m_ndb.closeTransaction(pTrans);
|
|
|
|
|
|
return NDBT_OK;
|
|
|
|
}
|