#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../client/dst_cli_sdk.h" #include #include #include using namespace alisql; #define PROGRAM_NAME "AliRocks cluster function test" #define PROGRAM_DESCRIPTION "AliRocks cluster function test program" /* Types */ enum TestType { SET_TEST, GET_TEST, GET_ALL_TEST, //verify the data got from all servers are correct DELETE_TEST, NOCAS_TEST, CAS_TEST }; struct KeyValuePair { std::string key; std::string value; }; static unsigned int optExecuteNumber= 0; static unsigned int optConcurrency= 0; static unsigned int optSleep= 0; static char *optServers= NULL; TestType optTest= SET_TEST; #define DEFAULT_EXECUTE_NUMBER 10000 #define DEFAULT_CONCURRENCY 1 /* Global Thread counter */ volatile unsigned int masterWakeup; pthread_mutex_t sleeperMutex; pthread_cond_t sleepThreshhold; std::vector members; struct ThreadContext { std::vector executePairs; TestType test; uint64_t start; uint64_t end; uint32_t threadID; ThreadContext(TestType testArg) : test(testArg), start(0), end(0), threadID(0) { } ~ThreadContext() { } }; struct Conclusions { long int loadTime; long int readTime; unsigned int rowsLoaded; unsigned int rowsRead; Conclusions() : loadTime(0), readTime(0), rowsLoaded(0), rowsRead() { } }; enum RDOptions { OPT_SERVERS= 's', OPT_HELP= 'h', OPT_EXECUTE_NUMBER, OPT_TEST, OPT_CONCURRENCY, OPT_SLEEP }; static const char *lookupHelp(RDOptions option) { switch (option) { case OPT_SERVERS: return("List which servers you wish to connect to."); case OPT_HELP: return("Display this message and then exit."); case OPT_EXECUTE_NUMBER: return("Number of times to execute the given test."); case OPT_TEST: return("Test to run (currently \"get\", \"set\" or \"delete\")."); case OPT_CONCURRENCY: return("Number of users to simulate with load."); case OPT_SLEEP: return ("Sleep # ms before next action."); default: break; }; return "unknown function"; } static void helpCommand(const char *commandName, const char *description, const struct option *longOptions) { unsigned int i; printf("%s v%u.%u\n\n", commandName, 1U, 0U); printf("\t%s\n\n", description); printf("Current options. A '=' means the option takes a value.\n\n"); for (i= 0; longOptions[i].name; i++) { const char *helpMessage; printf("\t --%s%c\n", longOptions[i].name, longOptions[i].has_arg ? '=' : ' '); if ((helpMessage= lookupHelp(RDOptions(longOptions[i].val)))) printf("\t\t%s\n", helpMessage); } printf("\n"); exit(0); } int executeNoCas(DstCliSDK* sdk) { uint64_t i= 0; int success = 0; int rc= -1; std::string key= "cas"; std::string value; char valueBuf[128]= {'\0'}; while (i < optExecuteNumber) { i++; if (optSleep > 0) { usleep(optSleep * 1000); } rc= sdk->get(key, value); if (rc != 0) { printf("fail to get key: %s\n", key.c_str()); return -1; } else { int curVal= boost::lexical_cast(value); curVal += 1; snprintf(valueBuf, sizeof(valueBuf), "%d", curVal); value= valueBuf; rc= sdk->put(key, value); if (rc != 0) { printf("fail to set value: %s\n", value.c_str()); return -1; } } } return rc; } int executeCas(DstCliSDK* sdk, ThreadContext *context) { uint64_t i= 0; int success = 0; int rc= -1; std::string key= "cas"; std::string value; char valueBuf[128]= {'\0'}; while (i < optExecuteNumber) { if (optSleep > 0) { usleep(optSleep * 1000); } rc= sdk->gets(key, value); if (rc != 0) { printf("Thd %d, fail to get key: %s\n", context->threadID, key.c_str()); return -1; } else { uint64_t casUnique= sdk->getCasUnique(); int curVal= boost::lexical_cast(value); curVal += 1; snprintf(valueBuf, sizeof(valueBuf), "%d", curVal); value= valueBuf; sdk->setCasUnique(casUnique); printf("Thd %d, cas unique: %ld, value: %s\n",context->threadID, casUnique, value.c_str()); rc= sdk->cas(key, value); memcached_return_t mrc= sdk->getMemcachedRc(); if (rc != 0) { std::cout << "Thd " << context->threadID << " " << memcached_strerror(NULL, mrc) << std::endl; } else { std::cout << "Thd " << context->threadID << " " << memcached_strerror(NULL, mrc) << std::endl; i++; } } } return rc; } uint64_t executeSet(DstCliSDK* sdk, uint64_t start, uint64_t end) { uint64_t i= 0; int rc= -1; int tryCount= 0; char keyBuf[128]= {'\0'}; char valueBuf[128]= {'\0'}; std::string key; std::string value; for (i= start; i <= end; ++i) { if (optSleep > 0) { usleep(optSleep * 1000); } snprintf(keyBuf, sizeof(keyBuf), "key_%d", i); snprintf(valueBuf, sizeof(valueBuf), "value_%d", i); key= keyBuf; value= valueBuf; rc= sdk->put(key, value); if (rc != 0) { if (tryCount < 100) { i--; printf("try put again: %s\n", key.c_str()); sleep(1); tryCount++; continue; } else { tryCount= 0; printf("fail to put: %s\n", key.c_str()); break; } } if (i % 1000 == 0) { printf("set %s\n", key.c_str()); } } return i; } uint64_t executeGet(DstCliSDK* sdk, uint64_t start, uint64_t end) { uint64_t i= 0; int rc= -1; char keyBuf[128]= {'\0'}; char valueBuf[128]= {'\0'}; std::string key; std::string value; for (i= start; i <= end; ++i) { if (optSleep > 0) { usleep(optSleep * 1000); } snprintf(keyBuf, sizeof(keyBuf), "key_%d", i); snprintf(valueBuf, sizeof(valueBuf), "value_%d", i); key= keyBuf; rc= sdk->get(key, value); if (rc != 0) { printf("fail to get key: %s\n", key.c_str()); } else { if (strcmp(value.c_str(), valueBuf) != 0) { std::cout << "No match, expected value: " << valueBuf << " return value: " << value << std::endl; } } } return i; } uint64_t executeDelete(DstCliSDK* sdk, uint64_t start, uint64_t end) { uint64_t i= 0; int rc= -1; int tryCount= 0; char keyBuf[128]= {'\0'}; char valueBuf[128]= {'\0'}; std::string key; std::string value; for (i= start; i <= end; ++i) { if (optSleep > 0) { usleep(optSleep * 1000); } snprintf(keyBuf, sizeof(keyBuf), "key_%d", i); key= keyBuf; rc= sdk->del(key); if (rc != 0) { if (tryCount < 100) { i--; printf("try delete again: %s\n", key.c_str()); sleep(1); tryCount++; continue; } else { tryCount= 0; printf("fail to delete: %s\n", key.c_str()); break; } } if (i % 1000 == 0) { printf("delete %s\n", key.c_str()); } } return i; } long int timedif(struct timeval a, struct timeval b) { long us, s; us = (int)(a.tv_usec - b.tv_usec); us /= 1000; s = (int)(a.tv_sec - b.tv_sec); s *= 1000; return s + us; } extern "C" { static __attribute__((noreturn)) void *runTask(void *p) { ThreadContext *context= (ThreadContext *)p; pthread_mutex_lock(&sleeperMutex); while (masterWakeup) { pthread_cond_wait(&sleepThreshhold, &sleeperMutex); } pthread_mutex_unlock(&sleeperMutex); DstCliSDK cliSdk(optServers); cliSdk.init(); /* Do Stuff */ switch (context->test) { case SET_TEST: executeSet(&cliSdk, context->start, context->end); break; case GET_TEST: executeGet(&cliSdk, context->start, context->end); break; case DELETE_TEST: executeDelete(&cliSdk, context->start, context->end); break; case GET_ALL_TEST: { std::vector memberList; for (int i= 0; i < members.size(); i++) { memberList.clear(); std::string member= members[i]; memberList.push_back(member); CliSDK sdk(memberList); executeGet(&cliSdk, context->start, context->end); } break; } case NOCAS_TEST: executeNoCas(&cliSdk); break; case CAS_TEST: executeCas(&cliSdk, context); break; default: break; } delete context; pthread_exit(0); } } void conclusionsPrint(Conclusions *conclusion) { printf("\tThreads connecting to servers %u\n", optConcurrency); if (optTest == SET_TEST || optTest == CAS_TEST || optTest == NOCAS_TEST || optTest == DELETE_TEST) printf("\tTook %ld.%03ld seconds to load data\n", conclusion->loadTime / 1000, conclusion->loadTime % 1000); else printf("\tTook %ld.%03ld seconds to read data\n", conclusion->readTime / 1000, conclusion->readTime % 1000); } void optionsParse(int argc, char *argv[]) { static struct option longOptions[]= { {"concurrency", required_argument, NULL, OPT_CONCURRENCY}, {"execute-number", required_argument, NULL, OPT_EXECUTE_NUMBER}, {"help", no_argument, NULL, OPT_HELP}, {"servers", required_argument, NULL, OPT_SERVERS}, {"test", required_argument, NULL, OPT_TEST}, {"sleep", required_argument, NULL, OPT_SLEEP}, {0, 0, 0, 0}, }; bool optHelp= false; int optionIndex= 0; while (1) { int optionRv= getopt_long(argc, argv, "Vhvds:", longOptions, &optionIndex); if (optionRv == -1) break; switch (optionRv) { case 0: break; case OPT_HELP: /* --help or -h */ optHelp= true; break; case OPT_SERVERS: /* --servers or -s */ optServers= strdup(optarg); break; case OPT_TEST: if (strcmp(optarg, "get") == 0) { optTest= GET_TEST ; } else if (strcmp(optarg, "set") == 0) { optTest= SET_TEST; } else if (strcmp(optarg, "delete") == 0) { optTest= DELETE_TEST; } else if (strcmp(optarg, "getall") == 0) { optTest= GET_ALL_TEST; } else if (strcmp(optarg, "cas") == 0) { optTest= CAS_TEST; } else if (strcmp(optarg, "nocas") == 0) { optTest= NOCAS_TEST; } else { fprintf(stderr, "Your test, %s, is not a known test\n", optarg); exit(-1); } break; case OPT_CONCURRENCY: errno= 0; optConcurrency= (unsigned int)strtoul(optarg, (char **)NULL, 10); if (errno != 0) { fprintf(stderr, "Invalid value for concurrency: %s\n", optarg); exit(-1); } break; case OPT_EXECUTE_NUMBER: errno= 0; optExecuteNumber= (unsigned int)strtoul(optarg, (char **)NULL, 10); if (errno != 0) { fprintf(stderr, "Invalid value for execute: %s\n", optarg); exit(-1); } break; case OPT_SLEEP: errno= 0; optSleep= (unsigned int)strtoul(optarg, (char **)NULL, 10); if (errno != 0) { fprintf(stderr, "Invalid value for sleep: %s\n", optarg); exit(-1); } break; case '?': /* getopt_long already printed an error message. */ exit(-1); default: abort(); } } if (optHelp) { helpCommand(PROGRAM_NAME, PROGRAM_DESCRIPTION, longOptions); exit(0); } if (optExecuteNumber == 0) optExecuteNumber= DEFAULT_EXECUTE_NUMBER; if (optConcurrency == 0) optConcurrency= DEFAULT_CONCURRENCY; } KeyValuePair *pairsGenerate(uint64_t start, uint64_t end, std::vector &executePairs) { char keyBuf[128]= {'\0'}; char valueBuf[128]= {'\0'}; for (uint64_t i= start; i <= end; i++) { KeyValuePair pairs; snprintf(keyBuf, sizeof(keyBuf), "key_%d", i); snprintf(valueBuf, sizeof(valueBuf), "value_%d", i); pairs.key= keyBuf; pairs.value= valueBuf; executePairs.push_back(pairs); } } void rdScheduler(Conclusions *conclusion) { struct timeval startTime, endTime; int rc= -1; pthread_mutex_lock(&sleeperMutex); masterWakeup= 1; pthread_mutex_unlock(&sleeperMutex); DstCliSDK cliSdk(optServers); rc= cliSdk.init(); std::string key= "cas"; std::string value= "0"; if (optTest == CAS_TEST || optTest == NOCAS_TEST) { rc= cliSdk.put(key, value); if (rc != 0) { fprintf(stderr,"Fail to init cas value\n"); exit(1); } rc= cliSdk.get(key, value); if (rc != 0) { printf("fail to get key: %s\n", key.c_str()); exit(1); } else { printf("cas init as: %s\n", value.c_str()); } } pthread_t *threads= new (std::nothrow) pthread_t[optConcurrency]; if (threads == NULL) { exit(-1); } uint64_t range= optExecuteNumber / optConcurrency; uint64_t tailer= optExecuteNumber % optConcurrency; for (uint32_t i= 0; i < optConcurrency; i++) { ThreadContext *context= new ThreadContext(optTest); context->test= optTest; uint64_t start= range * i + 1; uint64_t end= range * (i + 1); if (i == optConcurrency - 1) { end += tailer; } context->start= start; context->end= end; context->threadID= i + 1; /* now create the thread */ if (pthread_create(threads + i, NULL, runTask, (void *)context) != 0) { fprintf(stderr,"Could not create thread\n"); exit(1); } } pthread_mutex_lock(&sleeperMutex); masterWakeup= 0; pthread_mutex_unlock(&sleeperMutex); pthread_cond_broadcast(&sleepThreshhold); gettimeofday(&startTime, NULL); for (uint32_t i= 0; i < optConcurrency; i++) { void *retval; pthread_join(threads[i], &retval); } delete [] threads; if (optTest == CAS_TEST || optTest == NOCAS_TEST) { rc= cliSdk.get(key, value); if (rc != 0) { printf("fail to get key: %s\n", key.c_str()); } else { printf("value result: %s\n", value.c_str()); } } gettimeofday(&endTime, NULL); conclusion->loadTime= timedif(endTime, startTime); conclusion->readTime= timedif(endTime, startTime); } int main(int argc, char* argv[]) { Conclusions conclusion; optionsParse(argc, argv); if (optServers == NULL) { std::cerr << "No Servers provided" << std::endl; exit(-1); } // try // { // boost::split(members, optServers, // boost::is_any_of(","), boost::token_compress_on); // } // catch(std::exception& e) // { // printf("Error, No Servers provided\n"); // exit(-1); // } int ret= 0; pthread_mutex_init(&sleeperMutex, NULL); pthread_cond_init(&sleepThreshhold, NULL); try { rdScheduler(&conclusion); } catch(std::exception& e) { std::cerr << "Died with exception: " << e.what() << std::endl; ret= -1; } (void)pthread_mutex_destroy(&sleeperMutex); (void)pthread_cond_destroy(&sleepThreshhold); conclusionsPrint(&conclusion); return ret; }