721 lines
15 KiB
C++
721 lines
15 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <getopt.h>
|
|
#include <memory>
|
|
#include <pthread.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include "../client/dst_cli_sdk.h"
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
|
|
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<std::string> members;
|
|
|
|
struct ThreadContext
|
|
{
|
|
std::vector<KeyValuePair> 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<int>(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<int>(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<std::string> 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<KeyValuePair> &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;
|
|
}
|
|
|
|
|