/* Copyright (c) 2005, 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 as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "userInterface.h" #include "dbGenerator.h" #include "ndb_schema.hpp" static int numProcesses; static int numSeconds; static int numWarmSeconds; static int parallellism; static int millisSendPoll; static int minEventSendPoll; static int forceSendPoll; static bool useNdbRecord; static bool useCombUpd; int subscriberCount; static bool robustMode; static ThreadData *data; static Ndb_cluster_connection *g_cluster_connection= 0; static void usage(const char *prog) { const char *progname; /*--------------------------------------------*/ /* Get the name of the program (without path) */ /*--------------------------------------------*/ progname = strrchr(prog, '/'); if (progname == 0) progname = prog; else ++progname; ndbout_c( "Usage: %s [-proc ] [-warm ] [-time ] [ -p ]" "[-t ] [ -e ] [ -f ] [ -ndbrecord ] [ -s ]\n" " -proc Specifies that is the number of\n" " threads. The default is 1.\n" " -time Specifies that the test will run for sec.\n" " The default is 10 sec\n" " -warm Specifies the warm-up/cooldown period of " "sec.\n" " The default is 10 sec\n" " -p The no of parallell transactions started by " "one thread\n" " -e Minimum no of events before wake up in call to " "sendPoll\n" " Default is 1\n" " -f force parameter to sendPoll\n" " Default is 0\n" " -ndbrecord Use NdbRecord Api.\n" " Default is to use old Api\n" " -combupdread Use update pre-read operation where possible\n" " Default is to use separate read+update ops\n" " -s Number of subscribers to operate on, default is %u.\n" " -r Whether to be robust to key errors\n", progname, NO_OF_SUBSCRIBERS); } static int parse_args(int argc, const char **argv) { int i; numProcesses = 1; numSeconds = 10; numWarmSeconds = 10; parallellism = 1; millisSendPoll = 10000; minEventSendPoll = 1; forceSendPoll = 0; useNdbRecord = false; useCombUpd = false; subscriberCount = NO_OF_SUBSCRIBERS; robustMode = false; i = 1; while (i < argc){ if (strcmp("-proc",argv[i]) == 0) { if (i + 1 >= argc) { return 1; } if (sscanf(argv[i+1], "%d", &numProcesses) == -1 || numProcesses <= 0 || numProcesses > 127) { ndbout_c("-proc flag requires a positive integer argument [1..127]"); return 1; } i += 2; } else if (strcmp("-p", argv[i]) == 0){ if(i + 1 >= argc){ usage(argv[0]); return 1; } if (sscanf(argv[i+1], "%d", ¶llellism) == -1 || parallellism <= 0){ ndbout_c("-p flag requires a positive integer argument"); return 1; } i += 2; } else if (strcmp("-time",argv[i]) == 0) { if (i + 1 >= argc) { return 1; } if (sscanf(argv[i+1], "%d", &numSeconds) == -1 || numSeconds < 0) { ndbout_c("-time flag requires a positive integer argument"); return 1; } i += 2; } else if (strcmp("-warm",argv[i]) == 0) { if (i + 1 >= argc) { return 1; } if (sscanf(argv[i+1], "%d", &numWarmSeconds) == -1 || numWarmSeconds < 0) { ndbout_c("-warm flag requires a positive integer argument"); return 1; } i += 2; } else if (strcmp("-e",argv[i]) == 0) { if (i + 1 >= argc) { return 1; } if (sscanf(argv[i+1], "%d", &minEventSendPoll) == -1 || minEventSendPoll < 0) { ndbout_c("-e flag requires a positive integer argument"); return 1; } i += 2; } else if (strcmp("-f",argv[i]) == 0) { if (i + 1 >= argc) { usage(argv[0]); return 1; } if (sscanf(argv[i+1], "%d", &forceSendPoll) == -1 || forceSendPoll < 0) { ndbout_c("-f flag requires a positive integer argument"); return 1; } i += 2; } else if (strcmp("-ndbrecord",argv[i]) == 0) { useNdbRecord= true; i++; } else if (strcmp("-combupdread",argv[i]) == 0) { /* Comb up some dread */ useCombUpd= true; i++; } else if (strcmp("-s", argv[i]) == 0) { if (i + 1 >= argc) { return 1; } if (sscanf(argv[i+1], "%u", &subscriberCount) == -1) { ndbout_c("-s flag requires a positive argument."); return 1; } i+=2; } else if (strcmp("-r", argv[i]) == 0) { robustMode= true; i++; } else { return 1; } } if(minEventSendPoll > parallellism){ ndbout_c("minEventSendPoll(%d) > parallellism(%d)", minEventSendPoll, parallellism); ndbout_c("not very good..."); ndbout_c("very bad..."); ndbout_c("exiting..."); return 1; } if (useNdbRecord && useCombUpd){ ndbout_c("NdbRecord does not currently support combined update " "and read. Using separate read and update ops"); } return 0; } static void print_transaction(const char *header, unsigned long totalCount, TransactionDefinition *trans, unsigned int printBranch, unsigned int printRollback) { double f; ndbout_c(" %s: %d (%.2f%%) " "Latency(ms) avg: %d min: %d max: %d std: %d n: %d", header, trans->count, (double)trans->count / (double)totalCount * 100.0, (int)trans->latency.getMean(), (int)trans->latency.getMin(), (int)trans->latency.getMax(), (int)trans->latency.getStddev(), (int)trans->latency.getCount() ); if( printBranch ){ if( trans->count == 0 ) f = 0.0; else f = (double)trans->branchExecuted / (double)trans->count * 100.0; ndbout_c(" Branches Executed: %d (%.2f%%)", trans->branchExecuted, f); } if( printRollback ){ if( trans->count == 0 ) f = 0.0; else f = (double)trans->rollbackExecuted / (double)trans->count * 100.0; ndbout_c(" Rollback Executed: %d (%.2f%%)",trans->rollbackExecuted,f); } } void print_stats(const char *title, unsigned int length, unsigned int transactionFlag, GeneratorStatistics *gen, int numProc, int parallellism) { int i; char buf[10]; ndbout_c("\n------ %s ------",title); ndbout_c("Length : %d %s", length, transactionFlag ? "Transactions" : "sec"); ndbout_c("Number of Proc: %d",numProc); ndbout_c("Parallellism : %d", parallellism); ndbout_c("UseNdbRecord : %u", useNdbRecord); ndbout_c("\n"); if( gen->totalTransactions == 0 ) { ndbout_c(" No Transactions for this test"); } else { for(i = 0; i < 5; i++) { sprintf(buf, "T%d",i+1); print_transaction(buf, gen->totalTransactions, &gen->transactions[i], i >= 2, i >= 3 ); } ndbout_c("\n"); ndbout_c(" Overall Statistics:"); ndbout_c(" Transactions: %d", gen->totalTransactions); ndbout_c(" Outer : %.0f TPS",gen->outerTps); ndbout_c("\n"); ndbout_c("NDBT_Observation;tps;%.0f", gen->outerTps); } } static void * threadRoutine(void *arg) { int i; ThreadData *data = (ThreadData *)arg; Ndb * pNDB; pNDB = asyncDbConnect(parallellism); /* NdbSleep_MilliSleep(rand() % 10); */ for(i = 0; igetDictionary(); NdbDictionary::RecordSpecification cols[7]; const NdbDictionary::Table* tab= dict->getTable(SUBSCRIBER_TABLE); cols[0].column= tab->getColumn((int) IND_SUBSCRIBER_NUMBER); cols[0].offset= offsetof(TransactionData, number); cols[0].nullbit_byte_offset= 0; cols[0].nullbit_bit_in_byte= 0; cols[1].column= tab->getColumn((int) IND_SUBSCRIBER_NAME); cols[1].offset= offsetof(TransactionData, name); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; cols[2].column= tab->getColumn((int) IND_SUBSCRIBER_GROUP); cols[2].offset= offsetof(TransactionData, group_id); cols[2].nullbit_byte_offset= 0; cols[2].nullbit_bit_in_byte= 0; cols[3].column= tab->getColumn((int) IND_SUBSCRIBER_LOCATION); cols[3].offset= offsetof(TransactionData, location); cols[3].nullbit_byte_offset= 0; cols[3].nullbit_bit_in_byte= 0; cols[4].column= tab->getColumn((int) IND_SUBSCRIBER_SESSIONS); cols[4].offset= offsetof(TransactionData, sessions); cols[4].nullbit_byte_offset= 0; cols[4].nullbit_bit_in_byte= 0; cols[5].column= tab->getColumn((int) IND_SUBSCRIBER_CHANGED_BY); cols[5].offset= offsetof(TransactionData, changed_by); cols[5].nullbit_byte_offset= 0; cols[5].nullbit_bit_in_byte= 0; cols[6].column= tab->getColumn((int) IND_SUBSCRIBER_CHANGED_TIME); cols[6].offset= offsetof(TransactionData, changed_time); cols[6].nullbit_byte_offset= 0; cols[6].nullbit_bit_in_byte= 0; ndbRecordSharedDataPtr->subscriberTableNdbRecord= dict->createRecord(tab, cols, 7, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->subscriberTableNdbRecord == NULL) { ndbout << "Error creating record 1 : " << dict->getNdbError() << endl; return -1; } tab= dict->getTable(GROUP_TABLE); cols[0].column= tab->getColumn((int) IND_GROUP_ID); cols[0].offset= offsetof(TransactionData, group_id); cols[0].nullbit_byte_offset= 0; cols[0].nullbit_bit_in_byte= 0; /* GROUP_NAME not used via NdbRecord */ cols[1].column= tab->getColumn((int) IND_GROUP_ALLOW_READ); cols[1].offset= offsetof(TransactionData, permission); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; ndbRecordSharedDataPtr->groupTableAllowReadNdbRecord= dict->createRecord(tab, cols, 2, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->groupTableAllowReadNdbRecord == NULL) { ndbout << "Error creating record 2.1: " << dict->getNdbError() << endl; return -1; } cols[1].column= tab->getColumn((int) IND_GROUP_ALLOW_INSERT); cols[1].offset= offsetof(TransactionData, permission); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; ndbRecordSharedDataPtr->groupTableAllowInsertNdbRecord= dict->createRecord(tab, cols, 2, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->groupTableAllowInsertNdbRecord == NULL) { ndbout << "Error creating record 2.2: " << dict->getNdbError() << endl; return -1; } cols[1].column= tab->getColumn((int) IND_GROUP_ALLOW_DELETE); cols[1].offset= offsetof(TransactionData, permission); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; ndbRecordSharedDataPtr->groupTableAllowDeleteNdbRecord= dict->createRecord(tab, cols, 2, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->groupTableAllowDeleteNdbRecord == NULL) { ndbout << "Error creating record 2.3: " << dict->getNdbError() << endl; return -1; } tab= dict->getTable(SESSION_TABLE); cols[0].column= tab->getColumn((int) IND_SESSION_SUBSCRIBER); cols[0].offset= offsetof(TransactionData, number); cols[0].nullbit_byte_offset= 0; cols[0].nullbit_bit_in_byte= 0; cols[1].column= tab->getColumn((int) IND_SESSION_SERVER); cols[1].offset= offsetof(TransactionData, server_id); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; cols[2].column= tab->getColumn((int) IND_SESSION_DATA); cols[2].offset= offsetof(TransactionData, session_details); cols[2].nullbit_byte_offset= 0; cols[2].nullbit_bit_in_byte= 0; ndbRecordSharedDataPtr->sessionTableNdbRecord= dict->createRecord(tab, cols, 3, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->sessionTableNdbRecord == NULL) { ndbout << "Error creating record 3 : " << dict->getNdbError() << endl; return -1; } tab= dict->getTable(SERVER_TABLE); cols[0].column= tab->getColumn((int) IND_SERVER_SUBSCRIBER_SUFFIX); cols[0].offset= offsetof(TransactionData, suffix); cols[0].nullbit_byte_offset= 0; cols[0].nullbit_bit_in_byte= 0; cols[1].column= tab->getColumn((int) IND_SERVER_ID); cols[1].offset= offsetof(TransactionData, server_id); cols[1].nullbit_byte_offset= 0; cols[1].nullbit_bit_in_byte= 0; /* SERVER_NAME not used via NdbRecord*/ /* SERVER_READS not used via NdbRecord */ /* SERVER_INSERTS not used via NdbRecord */ /* SERVER_DELETES not used via NdbRecord */ ndbRecordSharedDataPtr->serverTableNdbRecord= dict->createRecord(tab, cols, 2, sizeof(cols[0]), 0); if (ndbRecordSharedDataPtr->serverTableNdbRecord == NULL) { ndbout << "Error creating record 4 : " << dict->getNdbError() << endl; return -1; } /* Create program to increment server reads column */ prog1= new NdbInterpretedCode(tab); if (prog1->add_val(IND_SERVER_READS, (Uint32)1) || prog1->interpret_exit_ok() || prog1->finalise()) { ndbout << "Program 1 definition failed, exiting." << endl; return -1; } prog2= new NdbInterpretedCode(tab); if (prog2->add_val(IND_SERVER_INSERTS, (Uint32)1) || prog2->interpret_exit_ok() || prog2->finalise()) { ndbout << "Program 2 definition failed, exiting." << endl; return -1; } prog3= new NdbInterpretedCode(tab); if (prog3->add_val(IND_SERVER_DELETES, (Uint32)1) || prog3->interpret_exit_ok() || prog3->finalise()) { ndbout << "Program 3 definition failed, exiting." << endl; return -1; } ndbRecordSharedDataPtr->incrServerReadsProg= prog1; ndbRecordSharedDataPtr->incrServerInsertsProg= prog2; ndbRecordSharedDataPtr->incrServerDeletesProg= prog3; asyncDbDisconnect(tempNdb); } for(i = 0; i < numProcesses; i++) { for(j = 0; jpThread = pThread; } else { perror("Failed to create thread"); rc = NDBT_FAILED; } } showTime(); /*--------------------------------*/ /* Wait for all processes to exit */ /*--------------------------------*/ for(i = 0; i < numProcesses; i++) { NdbThread_WaitFor(data[i*parallellism].pThread, &tmp); NdbThread_Destroy(&data[i*parallellism].pThread); } ndbout_c("All threads have finished"); if (useNdbRecord) { free(ndbRecordSharedDataPtr); delete(prog1); delete(prog2); delete(prog3); } /*-------------------------------------------*/ /* Clear all structures for total statistics */ /*-------------------------------------------*/ stats.totalTransactions = 0; stats.outerTps = 0.0; for(i = 0; i < NUM_TRANSACTION_TYPES; i++ ) { stats.transactions[i].count = 0; stats.transactions[i].branchExecuted = 0; stats.transactions[i].rollbackExecuted = 0; stats.transactions[i].latency.reset(); } /*--------------------------------*/ /* Add the values for all Threads */ /*--------------------------------*/ for(i = 0; i < numProcesses; i++) { for(k = 0; ktotalTransactions; stats.outerTps += p->outerTps; for(j = 0; j < NUM_TRANSACTION_TYPES; j++ ) { stats.transactions[j].count += p->transactions[j].count; stats.transactions[j].branchExecuted += p->transactions[j].branchExecuted; stats.transactions[j].rollbackExecuted += p->transactions[j].rollbackExecuted; stats.transactions[j].latency += p->transactions[j].latency; } } } print_stats("Test Results", numSeconds, 0, &stats, numProcesses, parallellism); free(data); NDBT_ProgramExit(rc); } /*************************************************************** * I N C L U D E D F I L E S * ***************************************************************/ #include #include #include #include #include "ndb_error.hpp" #include "userInterface.h" #include #include #include #include #include /*************************************************************** * L O C A L C O N S T A N T S * ***************************************************************/ /*************************************************************** * L O C A L D A T A S T R U C T U R E S * ***************************************************************/ /*************************************************************** * L O C A L F U N C T I O N S * ***************************************************************/ #ifndef NDB_WIN32 #include #endif Ndb* asyncDbConnect(int parallellism){ Ndb * pNDB = new Ndb(g_cluster_connection, "TEST_DB"); pNDB->init(parallellism + 1); while(pNDB->waitUntilReady() != 0){ } return pNDB; } void asyncDbDisconnect(Ndb* pNDB) { delete pNDB; } static NDB_TICKS initTicks; double userGetTime(void) { double timeValue = 0; if ( !NdbTick_IsValid(initTicks)) { initTicks = NdbTick_getCurrentTicks(); timeValue = 0.0; } else { const NDB_TICKS now = NdbTick_getCurrentTicks(); const Uint64 elapsedMicro = NdbTick_Elapsed(initTicks,now).microSec(); timeValue = ((double)elapsedMicro) / 1000000.0; } return timeValue; } void showTime() { char buf[128]; struct tm* tm_now; time_t now; now = ::time((time_t*)NULL); tm_now = ::gmtime(&now); BaseString::snprintf(buf, 128, "%d-%.2d-%.2d %.2d:%.2d:%.2d", tm_now->tm_year + 1900, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec); ndbout_c("Time: %s", buf); }