initial commit
This commit is contained in:
parent
b678a7cb3d
commit
e2f4842849
53
CMakeLists.txt
Normal file
53
CMakeLists.txt
Normal file
@ -0,0 +1,53 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(pingpong)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/msg/msg.pb.cc
|
||||
COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/msg/
|
||||
COMMAND protoc --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/msg/ --proto_path=${CMAKE_CURRENT_SOURCE_DIR}/msg/ msg.proto
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/msg/msg.proto)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(rocksdb rocksdb)
|
||||
pkg_check_modules(protobuf REQUIRED protobuf)
|
||||
|
||||
|
||||
if (${ENABLE_FSTACK} MATCHES "y")
|
||||
pkg_check_modules(dpdk REQUIRED libdpdk)
|
||||
pkg_check_modules(bsdtopo REQUIRED bsdtopo)
|
||||
pkg_check_modules(ssl REQUIRED libssl)
|
||||
include_directories(${dpdk_INCLUDE_DIRS})
|
||||
include_directories(${ssl_INCLUDE_DIRS})
|
||||
include_directories(${bsdtopo_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
include_directories(${rocksdb_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/msg)
|
||||
include_directories(${protobuf_INCLUDE_DIRS})
|
||||
|
||||
set(CFLAGS -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-variable -std=c++17 -O2 -g)
|
||||
|
||||
add_executable(dismember ${CMAKE_CURRENT_SOURCE_DIR}/dismember/dismember.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dismember/Generator.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dismember/reqgen.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dismember/util.cc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/msg/msg.pb.cc)
|
||||
target_link_libraries(dismember ${protobuf_LINK_LIBRARIES} ${rocksdb_LINK_LIBRARIES} bz2 z pthread)
|
||||
target_compile_options(dismember PRIVATE ${CFLAGS})
|
||||
|
||||
add_executable(ppd ${CMAKE_CURRENT_SOURCE_DIR}/ppd/ppd.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ppd/reqproc.cc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/msg/msg.pb.cc)
|
||||
target_link_libraries(ppd ${protobuf_LINK_LIBRARIES} ${rocksdb_LINK_LIBRARIES} bz2 z pthread)
|
||||
target_compile_options(ppd PRIVATE ${CFLAGS})
|
||||
|
||||
if (${ENABLE_FSTACK} MATCHES "y")
|
||||
add_executable(ppd_ff ${CMAKE_CURRENT_SOURCE_DIR}/ppd_ff/ppd.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ppd_ff/reqproc.cc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/msg/msg.pb.cc)
|
||||
target_link_libraries(ppd_ff ${protobuf_LINK_LIBRARIES} fstack ${ssl_LINK_LIBRARIES} bz2 z crypto ${dpdk_LIBRARIES} ${bsdtopo_LIBRARIES} librte_net_bond.a librte_bus_vdev.a)
|
||||
target_link_directories(ppd_ff PRIVATE /usr/local/lib ${dpdk_LIBRARY_DIRS} ${bsdtopo_LIBRARY_DIRS})
|
||||
target_compile_options(ppd_ff PRIVATE ${CFLAGS} ${dpdk_CFLAGS})
|
||||
endif()
|
76
dismember/Generator.cc
Normal file
76
dismember/Generator.cc
Normal file
@ -0,0 +1,76 @@
|
||||
// modified from mutilate
|
||||
|
||||
#include "Generator.h"
|
||||
|
||||
Generator* createFacebookKey() { return new GEV(30.7984, 8.20449, 0.078688); }
|
||||
|
||||
Generator* createFacebookValue() {
|
||||
Generator* g = new GPareto(15.0, 214.476, 0.348238);
|
||||
|
||||
Discrete* d = new Discrete(g);
|
||||
d->add(0.00536, 0.0);
|
||||
d->add(0.00047, 1.0);
|
||||
d->add(0.17820, 2.0);
|
||||
d->add(0.09239, 3.0);
|
||||
d->add(0.00018, 4.0);
|
||||
d->add(0.02740, 5.0);
|
||||
d->add(0.00065, 6.0);
|
||||
d->add(0.00606, 7.0);
|
||||
d->add(0.00023, 8.0);
|
||||
d->add(0.00837, 9.0);
|
||||
d->add(0.00837, 10.0);
|
||||
d->add(0.08989, 11.0);
|
||||
d->add(0.00092, 12.0);
|
||||
d->add(0.00326, 13.0);
|
||||
d->add(0.01980, 14.0);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
Generator* createFacebookIA() { return new GPareto(0, 16.0292, 0.154971); }
|
||||
|
||||
Generator* createGenerator(std::string str) {
|
||||
if (!strcmp(str.c_str(), "fb_key")) return createFacebookKey();
|
||||
else if (!strcmp(str.c_str(), "fb_value")) return createFacebookValue();
|
||||
else if (!strcmp(str.c_str(), "fb_ia")) return createFacebookIA();
|
||||
|
||||
char *s_copy = new char[str.length() + 1];
|
||||
strcpy(s_copy, str.c_str());
|
||||
char *saveptr = NULL;
|
||||
|
||||
if (atoi(s_copy) != 0 || !strcmp(s_copy, "0")) {
|
||||
double v = atof(s_copy);
|
||||
delete[] s_copy;
|
||||
return new Fixed(v);
|
||||
}
|
||||
|
||||
char *t_ptr = strtok_r(s_copy, ":", &saveptr);
|
||||
char *a_ptr = strtok_r(NULL, ":", &saveptr);
|
||||
|
||||
if (t_ptr == NULL) // || a_ptr == NULL)
|
||||
DIE("strtok(.., \":\") failed to parse %s", str.c_str());
|
||||
|
||||
char t = t_ptr[0];
|
||||
|
||||
saveptr = NULL;
|
||||
char *s1 = strtok_r(a_ptr, ",", &saveptr);
|
||||
char *s2 = strtok_r(NULL, ",", &saveptr);
|
||||
char *s3 = strtok_r(NULL, ",", &saveptr);
|
||||
|
||||
double a1 = s1 ? atof(s1) : 0.0;
|
||||
double a2 = s2 ? atof(s2) : 0.0;
|
||||
double a3 = s3 ? atof(s3) : 0.0;
|
||||
|
||||
delete[] s_copy;
|
||||
|
||||
if (strcasestr(str.c_str(), "fixed")) return new Fixed(a1);
|
||||
else if (strcasestr(str.c_str(), "normal")) return new Normal(a1, a2);
|
||||
else if (strcasestr(str.c_str(), "exponential")) return new Exponential(a1);
|
||||
else if (strcasestr(str.c_str(), "pareto")) return new GPareto(a1, a2, a3);
|
||||
else if (strcasestr(str.c_str(), "gev")) return new GEV(a1, a2, a3);
|
||||
else if (strcasestr(str.c_str(), "uniform")) return new Uniform(a1);
|
||||
|
||||
DIE("Unable to create Generator '%s'", str.c_str());
|
||||
|
||||
return NULL;
|
||||
}
|
217
dismember/Generator.h
Normal file
217
dismember/Generator.h
Normal file
@ -0,0 +1,217 @@
|
||||
// modified from mutilate
|
||||
// -*- c++ -*-
|
||||
|
||||
// 1. implement "fixed" generator
|
||||
// 2. implement discrete generator
|
||||
// 3. implement combine generator?
|
||||
|
||||
#ifndef GENERATOR_H
|
||||
#define GENERATOR_H
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "util.h"
|
||||
#define D(fmt, ...)
|
||||
#define DIE(fmt, ...)
|
||||
|
||||
// Generator syntax:
|
||||
//
|
||||
// \d+ == fixed
|
||||
// n[ormal]:mean,sd
|
||||
// e[xponential]:lambda
|
||||
// p[areto]:scale,shape
|
||||
// g[ev]:loc,scale,shape
|
||||
// fb_value, fb_key, fb_rate
|
||||
|
||||
class Generator {
|
||||
public:
|
||||
Generator() {}
|
||||
// Generator(const Generator &g) = delete;
|
||||
// virtual Generator& operator=(const Generator &g) = delete;
|
||||
virtual ~Generator() {}
|
||||
|
||||
virtual double generate(double U = -1.0) = 0;
|
||||
virtual void set_lambda(double lambda) {DIE("set_lambda() not implemented");}
|
||||
protected:
|
||||
std::string type;
|
||||
};
|
||||
|
||||
class Fixed : public Generator {
|
||||
public:
|
||||
Fixed(double _value = 1.0) : value(_value) { D("Fixed(%f)", value); }
|
||||
virtual double generate(double U = -1.0) { return value; }
|
||||
virtual void set_lambda(double lambda) {
|
||||
if (lambda > 0.0) value = 1.0 / lambda;
|
||||
else value = 0.0;
|
||||
}
|
||||
|
||||
private:
|
||||
double value;
|
||||
};
|
||||
|
||||
class Uniform : public Generator {
|
||||
public:
|
||||
Uniform(double _scale) : scale(_scale) { D("Uniform(%f)", scale); }
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
if (U < 0.0) U = drand48();
|
||||
return scale * U;
|
||||
}
|
||||
|
||||
virtual void set_lambda(double lambda) {
|
||||
if (lambda > 0.0) scale = 2.0 / lambda;
|
||||
else scale = 0.0;
|
||||
}
|
||||
|
||||
private:
|
||||
double scale;
|
||||
};
|
||||
|
||||
class Normal : public Generator {
|
||||
public:
|
||||
Normal(double _mean = 1.0, double _sd = 1.0) : mean(_mean), sd(_sd) {
|
||||
D("Normal(mean=%f, sd=%f)", mean, sd);
|
||||
}
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
if (U < 0.0) U = drand48();
|
||||
double V = U; // drand48();
|
||||
double N = sqrt(-2 * log(U)) * cos(2 * M_PI * V);
|
||||
return mean + sd * N;
|
||||
}
|
||||
|
||||
virtual void set_lambda(double lambda) {
|
||||
if (lambda > 0.0) mean = 1.0 / lambda;
|
||||
else mean = 0.0;
|
||||
}
|
||||
|
||||
private:
|
||||
double mean, sd;
|
||||
};
|
||||
|
||||
class Exponential : public Generator {
|
||||
public:
|
||||
Exponential(double _lambda = 1.0) : lambda(_lambda) {
|
||||
D("Exponential(lambda=%f)", lambda);
|
||||
}
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
if (lambda <= 0.0) return 0.0;
|
||||
if (U < 0.0) U = drand48();
|
||||
return -log(U) / lambda;
|
||||
}
|
||||
|
||||
virtual void set_lambda(double lambda) { this->lambda = lambda; }
|
||||
|
||||
private:
|
||||
double lambda;
|
||||
};
|
||||
|
||||
class GPareto : public Generator {
|
||||
public:
|
||||
GPareto(double _loc = 0.0, double _scale = 1.0, double _shape = 1.0) :
|
||||
loc(_loc), scale(_scale), shape(_shape) {
|
||||
assert(shape != 0.0);
|
||||
D("GPareto(loc=%f, scale=%f, shape=%f)", loc, scale, shape);
|
||||
}
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
if (U < 0.0) U = drand48();
|
||||
return loc + scale * (pow(U, -shape) - 1) / shape;
|
||||
}
|
||||
|
||||
virtual void set_lambda(double lambda) {
|
||||
if (lambda <= 0.0) scale = 0.0;
|
||||
else scale = (1 - shape) / lambda - (1 - shape) * loc;
|
||||
}
|
||||
|
||||
private:
|
||||
double loc /* mu */;
|
||||
double scale /* sigma */, shape /* k */;
|
||||
};
|
||||
|
||||
class GEV : public Generator {
|
||||
public:
|
||||
GEV(double _loc = 0.0, double _scale = 1.0, double _shape = 1.0) :
|
||||
e(1.0), loc(_loc), scale(_scale), shape(_shape) {
|
||||
assert(shape != 0.0);
|
||||
D("GEV(loc=%f, scale=%f, shape=%f)", loc, scale, shape);
|
||||
}
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
return loc + scale * (pow(e.generate(U), -shape) - 1) / shape;
|
||||
}
|
||||
|
||||
private:
|
||||
Exponential e;
|
||||
double loc /* mu */, scale /* sigma */, shape /* k */;
|
||||
};
|
||||
|
||||
class Discrete : public Generator {
|
||||
public:
|
||||
~Discrete() { delete def; }
|
||||
Discrete(Generator* _def = NULL) : def(_def) {
|
||||
if (def == NULL) def = new Fixed(0.0);
|
||||
}
|
||||
|
||||
virtual double generate(double U = -1.0) {
|
||||
double Uc = U;
|
||||
if (pv.size() > 0 && U < 0.0) U = drand48();
|
||||
|
||||
double sum = 0;
|
||||
|
||||
for (auto p: pv) {
|
||||
sum += p.first;
|
||||
if (U < sum) return p.second;
|
||||
}
|
||||
|
||||
return def->generate(Uc);
|
||||
}
|
||||
|
||||
void add(double p, double v) {
|
||||
pv.push_back(std::pair<double,double>(p, v));
|
||||
}
|
||||
|
||||
private:
|
||||
Generator *def;
|
||||
std::vector< std::pair<double,double> > pv;
|
||||
};
|
||||
|
||||
class KeyGenerator {
|
||||
public:
|
||||
KeyGenerator(Generator* _g, double _max = 10000) : g(_g), max(_max) {}
|
||||
std::string generate(uint64_t ind) {
|
||||
uint64_t h = fnv_64(ind);
|
||||
double U = (double) h / (double)ULLONG_MAX;
|
||||
double G = g->generate(U);
|
||||
int keylen = MAX(round(G), floor(log10(max)) + 1);
|
||||
char key[256];
|
||||
snprintf(key, 256, "%0*" PRIu64, keylen, ind);
|
||||
|
||||
// D("%d = %s", ind, key);
|
||||
return std::string(key);
|
||||
}
|
||||
private:
|
||||
Generator* g;
|
||||
double max;
|
||||
};
|
||||
|
||||
Generator* createGenerator(std::string str);
|
||||
Generator* createFacebookKey();
|
||||
Generator* createFacebookValue();
|
||||
Generator* createFacebookIA();
|
||||
|
||||
#endif // GENERATOR_H
|
1056
dismember/dismember.cc
Executable file
1056
dismember/dismember.cc
Executable file
File diff suppressed because it is too large
Load Diff
124
dismember/options.h
Normal file
124
dismember/options.h
Normal file
@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
|
||||
#include <const.h>
|
||||
#include <util.h>
|
||||
|
||||
static constexpr const int MAX_GEN_LEN = 31;
|
||||
static constexpr const int DEFAULT_SERVER_CLIENT_CONN_PORT = 9898;
|
||||
static constexpr const int DEFAULT_CLIENT_CTL_PORT = 9901;
|
||||
static constexpr const char* DEFAULT_OUTPUT = "output.sample";
|
||||
static constexpr const int MAX_GEN_PARAMS = 8;
|
||||
static constexpr const int MAX_GEN_PARAMS_LEN = 31;
|
||||
|
||||
struct option;
|
||||
|
||||
extern option options;
|
||||
|
||||
struct option {
|
||||
int verbose;
|
||||
|
||||
enum WORKLOAD_TYPE workload_type;
|
||||
|
||||
const char *output_name;
|
||||
|
||||
int client_num;
|
||||
int client_thread_count;
|
||||
int master_thread_count;
|
||||
|
||||
int client_conn;
|
||||
int master_conn;
|
||||
|
||||
int target_qps;
|
||||
int master_qps;
|
||||
|
||||
int master_mode;
|
||||
int client_mode;
|
||||
|
||||
std::atomic_int global_conn_start_idx;
|
||||
|
||||
char server_ip[INET_ADDRSTRLEN + 1];
|
||||
char generator_name[MAX_GEN_LEN + 1];
|
||||
|
||||
char gen_params[MAX_GEN_PARAMS][MAX_GEN_PARAMS_LEN + 1];
|
||||
int num_gen_params;
|
||||
|
||||
int master_server_ip_given;
|
||||
char master_server_ip[INET_ADDRSTRLEN + 1];
|
||||
int server_port;
|
||||
int depth_limit;
|
||||
|
||||
int warmup;
|
||||
int duration;
|
||||
|
||||
option()
|
||||
{
|
||||
this->verbose = 0;
|
||||
this->depth_limit = 1;
|
||||
this->output_name = DEFAULT_OUTPUT;
|
||||
this->client_thread_count = 1;
|
||||
this->master_thread_count = -1;
|
||||
this->client_conn = 1;
|
||||
this->master_conn = -1;
|
||||
this->target_qps = 0;
|
||||
this->master_qps = -1;
|
||||
this->client_mode = 0;
|
||||
this->master_mode = 0;
|
||||
this->warmup = 0;
|
||||
this->duration = 10;
|
||||
this->server_port = DEFAULT_SERVER_CLIENT_CONN_PORT;
|
||||
this->master_server_ip_given = 0;
|
||||
this->workload_type = WORKLOAD_TYPE::ECHO;
|
||||
this->num_gen_params = 0;
|
||||
this->global_conn_start_idx = 0;
|
||||
|
||||
for(int i = 0; i < MAX_GEN_PARAMS; i++) {
|
||||
memset(gen_params[i], 0, MAX_GEN_LEN + 1);
|
||||
}
|
||||
|
||||
/* set default values */
|
||||
strncpy(this->generator_name, "fb_ia" , MAX_GEN_LEN);
|
||||
strncpy(this->server_ip, "127.0.0.1" , INET_ADDRSTRLEN);
|
||||
strncpy(this->master_server_ip, "127.0.0.1", INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
V ("Configuration:\n"
|
||||
" Connections per thread: %d\n"
|
||||
" Num threads: %d\n"
|
||||
" Target QPS: %d\n"
|
||||
" warmup: %d\n"
|
||||
" duration: %d\n"
|
||||
" master_mode: %d\n"
|
||||
" client_mode: %d\n"
|
||||
" output_file: %s\n"
|
||||
" server_ip: %s\n"
|
||||
" server_port: %d\n"
|
||||
" IA_DIST: %s\n"
|
||||
" master_server_ip: %s\n"
|
||||
" workload_type: %d\n"
|
||||
" num_workload_param: %d\n"
|
||||
" global_conn_idx: %d\n",
|
||||
this->client_conn,
|
||||
this->client_thread_count,
|
||||
this->target_qps,
|
||||
this->warmup,
|
||||
this->duration,
|
||||
this->master_mode,
|
||||
this->client_mode,
|
||||
this->output_name,
|
||||
this->server_ip,
|
||||
this->server_port,
|
||||
this->generator_name,
|
||||
this->master_server_ip,
|
||||
this->workload_type,
|
||||
this->num_gen_params,
|
||||
this->global_conn_start_idx.load());
|
||||
}
|
||||
};
|
||||
/* Don't send god damn vtables */
|
||||
static_assert(std::is_standard_layout<option>(), "struct option must be standard layout");
|
346
dismember/reqgen.cc
Normal file
346
dismember/reqgen.cc
Normal file
@ -0,0 +1,346 @@
|
||||
#include "google/protobuf/stubs/common.h"
|
||||
#include "options.h"
|
||||
#include <msg.pb.h>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <sys/_stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <util.h>
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
#include <rocksdb/db.h>
|
||||
#endif
|
||||
|
||||
#include "reqgen.h"
|
||||
////////////////
|
||||
// TOUCH Generator
|
||||
////////////////
|
||||
|
||||
touch_gen::touch_gen(const int conn_id, std::unordered_map<std::string, std::string>* args) : req_gen(conn_id)
|
||||
{
|
||||
this->ugen = createGenerator("uniform:100");
|
||||
if (this->ugen == NULL) {
|
||||
E("Failed to create ugen for touch_gen\n");
|
||||
}
|
||||
|
||||
if (args->find(PARAM_GEN) == args->end()) {
|
||||
this->wgen = createGenerator(PARAM_GEN_DEFAULT);
|
||||
} else {
|
||||
this->wgen = createGenerator(args->at(PARAM_GEN));
|
||||
}
|
||||
|
||||
if (this->wgen == NULL) {
|
||||
E("Failed to create wgen for touch_gen\n");
|
||||
}
|
||||
|
||||
if (args->find(PARAM_UPDATE) == args->end()) {
|
||||
this->update_ratio = PARAM_UPDATE_DEFAULT;
|
||||
} else {
|
||||
this->update_ratio = atoi(args->at(PARAM_UPDATE).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
touch_gen::~touch_gen()
|
||||
{
|
||||
delete wgen;
|
||||
delete ugen;
|
||||
}
|
||||
|
||||
int touch_gen::send_req(int fd)
|
||||
{
|
||||
ppd_touch_req req;
|
||||
|
||||
if (options.master_mode) {
|
||||
req.set_touch_cnt(0);
|
||||
} else {
|
||||
req.set_touch_cnt(this->wgen->generate());
|
||||
}
|
||||
|
||||
if (this->ugen->generate() < this->update_ratio) {
|
||||
req.set_inc(1);
|
||||
} else {
|
||||
req.set_inc(0);
|
||||
}
|
||||
|
||||
if (!req.SerializeToArray(this->send_buf, MAX_SEND_BUF_SIZE)) {
|
||||
E("Failed to serialize to array for fd %d", fd);
|
||||
}
|
||||
|
||||
return writemsg(fd, this->send_buf, this->MAX_SEND_BUF_SIZE, this->send_buf, req.ByteSizeLong());
|
||||
}
|
||||
|
||||
int touch_gen::read_resp(int fd)
|
||||
{
|
||||
ppd_touch_resp resp;
|
||||
struct ppd_msg * msg = (struct ppd_msg *)this->send_buf;
|
||||
|
||||
if (readmsg(fd, this->send_buf, MAX_SEND_BUF_SIZE) < 0) {
|
||||
E("Readbuf failed for fd %d\n", fd);
|
||||
}
|
||||
|
||||
resp.ParseFromArray(msg->payload, msg->size);
|
||||
|
||||
return resp.status();
|
||||
}
|
||||
|
||||
////////////////
|
||||
// ECHO Generator
|
||||
////////////////
|
||||
int echo_gen::delay_table[DT_SZ];
|
||||
std::atomic<int> echo_gen::delay_table_populated = ATOMIC_VAR_INIT(0);
|
||||
|
||||
void
|
||||
echo_gen::populate_delay_table()
|
||||
{
|
||||
int idx = 0;
|
||||
int expected = 0;
|
||||
|
||||
// hack
|
||||
if (echo_gen::DT_SZ != 100) {
|
||||
E("Delay table size isn't 100");
|
||||
}
|
||||
|
||||
/* 95 + 4 + 1 = 100 */
|
||||
if (!delay_table_populated.compare_exchange_weak(expected, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delay_table[idx++] = 200;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
delay_table[idx++] = 50;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 95; i++) {
|
||||
delay_table[idx++] = 10;
|
||||
}
|
||||
}
|
||||
|
||||
echo_gen::echo_gen(const int conn_id, std::unordered_map<std::string, std::string>* args) : req_gen(conn_id)
|
||||
{
|
||||
|
||||
if (args->find(PARAM_GEN) == args->end()) {
|
||||
this->wgen = createGenerator(PARAM_GEN_DEFAULT);
|
||||
} else {
|
||||
this->wgen = createGenerator(args->at(PARAM_GEN));
|
||||
}
|
||||
|
||||
if (this->wgen == NULL) {
|
||||
E("Failed to create wgen for echo_gen");
|
||||
}
|
||||
|
||||
if (args->find(PARAM_CDELAY) == args->end()) {
|
||||
this->cdelay = PARAM_CDELAY_DEFAULT;
|
||||
} else {
|
||||
this->cdelay = atoi(args->at(PARAM_CDELAY).c_str());
|
||||
}
|
||||
|
||||
if (args->find(PARAM_SIZE) == args->end()) {
|
||||
this->data_sz = PARAM_SIZE_DEFAULT;
|
||||
} else {
|
||||
this->data_sz = atoi(args->at(PARAM_SIZE).c_str());
|
||||
}
|
||||
|
||||
if (this->cdelay) {
|
||||
populate_delay_table();
|
||||
}
|
||||
}
|
||||
|
||||
echo_gen::~echo_gen()
|
||||
{
|
||||
delete wgen;
|
||||
}
|
||||
|
||||
int echo_gen::get_delay()
|
||||
{
|
||||
if (cdelay) {
|
||||
return delay_table[conn_id % DT_SZ];
|
||||
} else {
|
||||
return this->wgen->generate();
|
||||
}
|
||||
}
|
||||
|
||||
int echo_gen::send_req(int fd)
|
||||
{
|
||||
ppd_echo_req req;
|
||||
|
||||
if (options.master_mode) {
|
||||
req.set_enable_delay(0);
|
||||
req.set_data_size(PARAM_SIZE_DEFAULT);
|
||||
} else {
|
||||
req.set_enable_delay(get_delay());
|
||||
req.set_data_size(this->data_sz);
|
||||
}
|
||||
|
||||
if (!req.SerializeToArray(this->send_buf, MAX_SEND_BUF_SIZE)) {
|
||||
E("Failed to serialize to array for fd %d\n", fd);
|
||||
}
|
||||
this->send_sz = req.ByteSizeLong() + sizeof(struct ppd_msg);
|
||||
return writemsg(fd, this->send_buf, MAX_SEND_BUF_SIZE, this->send_buf, req.ByteSizeLong());
|
||||
}
|
||||
|
||||
int echo_gen::read_resp(int fd)
|
||||
{
|
||||
ppd_echo_resp resp;
|
||||
struct ppd_msg * msg = (struct ppd_msg *)this->send_buf;
|
||||
|
||||
if (readmsg(fd, this->send_buf, MAX_SEND_BUF_SIZE) < 0) {
|
||||
E("Readbuf failed for fd %d\n", fd);
|
||||
}
|
||||
|
||||
resp.ParseFromArray(msg->payload, msg->size);
|
||||
this->recv_sz = msg->size + sizeof(struct ppd_msg);
|
||||
return resp.status();
|
||||
}
|
||||
|
||||
int echo_gen::get_send_sz()
|
||||
{
|
||||
return this->send_sz;
|
||||
}
|
||||
|
||||
int echo_gen::get_recv_sz()
|
||||
{
|
||||
return this->recv_sz;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// HTTP Generator
|
||||
////////////////
|
||||
char http_gen::cons_buf[CONS_SZ];
|
||||
|
||||
http_gen::http_gen(const int conn_id, const std::string& host, std::unordered_map<std::string, std::string>* args) : req_gen(conn_id)
|
||||
{
|
||||
// hack
|
||||
method = "GET";
|
||||
headers.insert({"Host", host});
|
||||
headers.insert({"Connection", "keep-alive"});
|
||||
major_ver = 1;
|
||||
minor_ver = 1;
|
||||
uri = "/";
|
||||
}
|
||||
|
||||
http_gen::~http_gen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string
|
||||
http_gen::build_req()
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << method << ' ' \
|
||||
<< uri << ' ' \
|
||||
<< "HTTP/" + std::to_string(major_ver) + "." + std::to_string(minor_ver) \
|
||||
<< "\r\n";
|
||||
|
||||
for(auto &i : headers) {
|
||||
ss << i.first.c_str() << ": " << i.second.c_str() << "\r\n";
|
||||
}
|
||||
|
||||
ss << "\r\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int http_gen::send_req(int fd)
|
||||
{
|
||||
std::string req = build_req();
|
||||
//V("Sending Request: %s\n", req.c_str());
|
||||
return writebuf(fd, (void*)req.c_str(), req.length());
|
||||
}
|
||||
|
||||
int http_gen::read_resp(int fd)
|
||||
{
|
||||
// hack
|
||||
// consume everything
|
||||
return read(fd, cons_buf, CONS_SZ);;
|
||||
}
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
////////////////
|
||||
// RDB Generator
|
||||
////////////////
|
||||
|
||||
rdb_gen::rdb_gen(const int conn_id, std::unordered_map<std::string, std::string>* args) : req_gen(conn_id), rand(1000 + conn_id)
|
||||
{
|
||||
this->key = AllocateKey(&this->key_guard, KEY_SIZE);
|
||||
std::vector<double> ratio {GET_RATIO, PUT_RATIO, SEEK_RATIO};
|
||||
this->query.Initiate(ratio);
|
||||
gen_exp.InitiateExpDistribution(TOTAL_KEYS, KEYRANGE_DIST_A, KEYRANGE_DIST_B, KEYRANGE_DIST_C, KEYRANGE_DIST_D);
|
||||
}
|
||||
|
||||
rdb_gen::~rdb_gen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int rdb_gen::send_req(int fd)
|
||||
{
|
||||
int status;
|
||||
ppd_rdb_req req;
|
||||
|
||||
int64_t ini_rand = GetRandomKey(&this->rand);
|
||||
int64_t key_rand = gen_exp.DistGetKeyID(ini_rand, this->KEYRANGE_DIST_A, this->KEYRANGE_DIST_B);
|
||||
|
||||
int64_t rand_v = ini_rand % TOTAL_KEYS;
|
||||
double u = (double)rand_v / TOTAL_KEYS;
|
||||
|
||||
int query_type = options.master_mode ? 0 : query.GetType(rand_v);
|
||||
|
||||
GenerateKeyFromInt(key_rand, TOTAL_KEYS, KEY_SIZE, &this->key);
|
||||
|
||||
V("SENDING KEY: %s.\n", this->key.data());
|
||||
switch (query_type) {
|
||||
case 0: {
|
||||
// get query
|
||||
req.set_op(PPD_RDB_OP_GET);
|
||||
req.set_key(this->key.data(), this->key.size());
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// put query
|
||||
int val_sz = ParetoCdfInversion(u, VALUE_THETA, VALUE_K, VALUE_SIGMA);
|
||||
rocksdb::Slice val = gen.Generate((unsigned int)val_sz);
|
||||
req.set_op(PPD_RDB_OP_PUT);
|
||||
req.set_key(this->key.data(), this->key.size());
|
||||
req.set_val(val.data(), val.size());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// seek query
|
||||
int64_t scan_length = ParetoCdfInversion(u, ITER_THETA, ITER_K, ITER_SIGMA);
|
||||
req.set_op(PPD_RDB_OP_SEEK);
|
||||
req.set_key(this->key.data(), this->key.size());
|
||||
req.set_optarg(scan_length);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
E("Unsupported query type %d", query_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (!req.SerializeToArray(this->send_buf, MAX_SEND_BUF_SIZE)) {
|
||||
E("Failed to serialize protobuf");
|
||||
}
|
||||
|
||||
status = writemsg(fd, this->send_buf, MAX_SEND_BUF_SIZE, this->send_buf, req.ByteSizeLong());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int rdb_gen::read_resp(int fd)
|
||||
{
|
||||
ppd_rdb_resp resp;
|
||||
struct ppd_msg * msg = (struct ppd_msg *)this->send_buf;
|
||||
|
||||
if (readmsg(fd, this->send_buf, MAX_SEND_BUF_SIZE) < 0) {
|
||||
E("Readbuf failed for fd %d", fd);
|
||||
}
|
||||
|
||||
resp.ParseFromArray(msg->payload, msg->size);
|
||||
|
||||
return resp.status();
|
||||
}
|
||||
#endif
|
622
dismember/reqgen.h
Normal file
622
dismember/reqgen.h
Normal file
@ -0,0 +1,622 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <random>
|
||||
#ifdef WITH_ROCKSDB
|
||||
#include <rocksdb/db.h>
|
||||
#endif
|
||||
#include "Generator.h"
|
||||
#include "options.h"
|
||||
#include <msg.pb.h>
|
||||
|
||||
#define DISABLE_EVIL_CONSTRUCTORS(name) \
|
||||
name(const name&) = delete; \
|
||||
void operator=(const name) = delete
|
||||
|
||||
class req_gen {
|
||||
protected:
|
||||
const int conn_id;
|
||||
char * send_buf;
|
||||
constexpr static int MAX_SEND_BUF_SIZE = 1024 * 1024;
|
||||
public:
|
||||
req_gen(const int id) : conn_id(id) { this->send_buf = new char[MAX_SEND_BUF_SIZE]; };
|
||||
virtual ~req_gen() { delete[] send_buf; };
|
||||
virtual int send_req(int fd) = 0;
|
||||
virtual int read_resp(int fd) = 0;
|
||||
};
|
||||
|
||||
class touch_gen : public req_gen
|
||||
{
|
||||
private:
|
||||
static constexpr const char* PARAM_GEN = "GEN";
|
||||
static constexpr const char* PARAM_GEN_DEFAULT = "fixed:64";
|
||||
static constexpr const char* PARAM_UPDATE = "UPDATE";
|
||||
static constexpr const int PARAM_UPDATE_DEFAULT = 0;
|
||||
Generator *wgen;
|
||||
Generator *ugen;
|
||||
int update_ratio;
|
||||
public:
|
||||
touch_gen(const int conn_id, std::unordered_map<std::string, std::string>* args);
|
||||
touch_gen() = delete;
|
||||
~touch_gen();
|
||||
DISABLE_EVIL_CONSTRUCTORS(touch_gen);
|
||||
int send_req(int fd);
|
||||
int read_resp(int fd);
|
||||
};
|
||||
|
||||
class echo_gen : public req_gen
|
||||
{
|
||||
private:
|
||||
static constexpr const char* PARAM_GEN = "GEN";
|
||||
static constexpr const char* PARAM_GEN_DEFAULT = "fixed:0";
|
||||
static constexpr const char* PARAM_CDELAY = "CDELAY";
|
||||
static constexpr const char* PARAM_SIZE = "SIZE";
|
||||
static constexpr const int PARAM_SIZE_DEFAULT = 0;
|
||||
static constexpr const int PARAM_CDELAY_DEFAULT = 0;
|
||||
static constexpr const int DT_SZ = 100;
|
||||
static int delay_table[DT_SZ];
|
||||
static void populate_delay_table();
|
||||
static std::atomic<int> delay_table_populated;
|
||||
|
||||
Generator *wgen;
|
||||
int cdelay;
|
||||
int data_sz;
|
||||
int recv_sz;
|
||||
int send_sz;
|
||||
int get_delay();
|
||||
public:
|
||||
echo_gen(const int conn_id, std::unordered_map<std::string, std::string>* args);
|
||||
echo_gen() = delete;
|
||||
~echo_gen();
|
||||
DISABLE_EVIL_CONSTRUCTORS(echo_gen);
|
||||
int send_req(int fd);
|
||||
int read_resp(int fd);
|
||||
int get_send_sz();
|
||||
int get_recv_sz();
|
||||
};
|
||||
|
||||
class http_gen : public req_gen
|
||||
{
|
||||
private:
|
||||
std::string build_req();
|
||||
std::string method;
|
||||
std::unordered_map<std::string, std::string> headers;
|
||||
std::string uri;
|
||||
int major_ver;
|
||||
int minor_ver;
|
||||
static constexpr const int CONS_SZ = 1024 * 1024 * 4;
|
||||
static char cons_buf[CONS_SZ];
|
||||
public:
|
||||
http_gen(const int conn_id, const std::string& host, std::unordered_map<std::string, std::string>* args);
|
||||
http_gen() = delete;
|
||||
~http_gen();
|
||||
DISABLE_EVIL_CONSTRUCTORS(http_gen);
|
||||
int send_req(int fd);
|
||||
int read_resp(int fd);
|
||||
};
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
class rdb_gen : public req_gen
|
||||
{
|
||||
private:
|
||||
enum DistributionType : unsigned char {
|
||||
kFixed = 0,
|
||||
kUniform,
|
||||
kNormal
|
||||
};
|
||||
|
||||
constexpr static int64_t TOTAL_KEYS = 50000000;
|
||||
constexpr static double GET_RATIO = 0.83;
|
||||
constexpr static double PUT_RATIO = 0.14;
|
||||
constexpr static double SEEK_RATIO = 0.03;
|
||||
constexpr static int KEYRANGE_NUM = 30;
|
||||
constexpr static double KEYRANGE_DIST_A = 14.18;
|
||||
constexpr static double KEYRANGE_DIST_B = -2.917;
|
||||
constexpr static double KEYRANGE_DIST_C = 0.0164;
|
||||
constexpr static double KEYRANGE_DIST_D = -0.08082;
|
||||
constexpr static double VALUE_THETA = 0;
|
||||
constexpr static double VALUE_K = 0.2615;
|
||||
constexpr static double VALUE_SIGMA = 25.45;
|
||||
constexpr static int VALUESIZE_MIN = 100;
|
||||
constexpr static int VALUESIZE_MAX = 102400;
|
||||
constexpr static DistributionType VALUESIZE_DIST = kFixed;
|
||||
constexpr static int KEY_SIZE = 48;
|
||||
constexpr static double ITER_THETA = 0;
|
||||
constexpr static double ITER_K = 2.517;
|
||||
constexpr static double ITER_SIGMA = 14.236;
|
||||
constexpr static double READ_RANDOM_EXP_RANGE = 0.0;
|
||||
constexpr static bool IS_LITTLE_ENDIAN = true;
|
||||
constexpr static int FIXED_VALUE_SIZE = 100;
|
||||
// A good 64-bit random number generator based on std::mt19937_64
|
||||
class Random64 {
|
||||
private:
|
||||
std::mt19937_64 generator_;
|
||||
|
||||
public:
|
||||
explicit Random64(uint64_t s) : generator_(s) { }
|
||||
|
||||
// Generates the next random number
|
||||
uint64_t Next() { return generator_(); }
|
||||
|
||||
// Returns a uniformly distributed value in the range [0..n-1]
|
||||
// REQUIRES: n > 0
|
||||
uint64_t Uniform(uint64_t n) {
|
||||
return std::uniform_int_distribution<uint64_t>(0, n - 1)(generator_);
|
||||
}
|
||||
|
||||
// Randomly returns true ~"1/n" of the time, and false otherwise.
|
||||
// REQUIRES: n > 0
|
||||
bool OneIn(uint64_t n) { return Uniform(n) == 0; }
|
||||
|
||||
// Skewed: pick "base" uniformly from range [0,max_log] and then
|
||||
// return "base" random bits. The effect is to pick a number in the
|
||||
// range [0,2^max_log-1] with exponential bias towards smaller numbers.
|
||||
uint64_t Skewed(int max_log) {
|
||||
return Uniform(uint64_t(1) << Uniform(max_log + 1));
|
||||
}
|
||||
};
|
||||
|
||||
struct KeyrangeUnit {
|
||||
int64_t keyrange_start;
|
||||
int64_t keyrange_access;
|
||||
int64_t keyrange_keys;
|
||||
};
|
||||
|
||||
class GenerateTwoTermExpKeys {
|
||||
public:
|
||||
int64_t keyrange_rand_max_;
|
||||
int64_t keyrange_size_;
|
||||
int64_t keyrange_num_;
|
||||
bool initiated_;
|
||||
std::vector<KeyrangeUnit> keyrange_set_;
|
||||
|
||||
GenerateTwoTermExpKeys() {
|
||||
keyrange_rand_max_ = TOTAL_KEYS;
|
||||
initiated_ = false;
|
||||
}
|
||||
|
||||
~GenerateTwoTermExpKeys() {}
|
||||
|
||||
// Initiate the KeyrangeUnit vector and calculate the size of each
|
||||
// KeyrangeUnit.
|
||||
rocksdb::Status InitiateExpDistribution(int64_t total_keys, double prefix_a,
|
||||
double prefix_b, double prefix_c,
|
||||
double prefix_d) {
|
||||
int64_t amplify = 0;
|
||||
int64_t keyrange_start = 0;
|
||||
initiated_ = true;
|
||||
if (KEYRANGE_NUM <= 0) {
|
||||
keyrange_num_ = 1;
|
||||
} else {
|
||||
keyrange_num_ = KEYRANGE_NUM;
|
||||
}
|
||||
keyrange_size_ = total_keys / keyrange_num_;
|
||||
|
||||
// Calculate the key-range shares size based on the input parameters
|
||||
for (int64_t pfx = keyrange_num_; pfx >= 1; pfx--) {
|
||||
// Step 1. Calculate the probability that this key range will be
|
||||
// accessed in a query. It is based on the two-term expoential
|
||||
// distribution
|
||||
double keyrange_p = prefix_a * std::exp(prefix_b * pfx) +
|
||||
prefix_c * std::exp(prefix_d * pfx);
|
||||
if (keyrange_p < std::pow(10.0, -16.0)) {
|
||||
keyrange_p = 0.0;
|
||||
}
|
||||
// Step 2. Calculate the amplify
|
||||
// In order to allocate a query to a key-range based on the random
|
||||
// number generated for this query, we need to extend the probability
|
||||
// of each key range from [0,1] to [0, amplify]. Amplify is calculated
|
||||
// by 1/(smallest key-range probability). In this way, we ensure that
|
||||
// all key-ranges are assigned with an Integer that >=0
|
||||
if (amplify == 0 && keyrange_p > 0) {
|
||||
amplify = static_cast<int64_t>(std::floor(1 / keyrange_p)) + 1;
|
||||
}
|
||||
|
||||
// Step 3. For each key-range, we calculate its position in the
|
||||
// [0, amplify] range, including the start, the size (keyrange_access)
|
||||
KeyrangeUnit p_unit;
|
||||
p_unit.keyrange_start = keyrange_start;
|
||||
if (0.0 >= keyrange_p) {
|
||||
p_unit.keyrange_access = 0;
|
||||
} else {
|
||||
p_unit.keyrange_access =
|
||||
static_cast<int64_t>(std::floor(amplify * keyrange_p));
|
||||
}
|
||||
p_unit.keyrange_keys = keyrange_size_;
|
||||
keyrange_set_.push_back(p_unit);
|
||||
keyrange_start += p_unit.keyrange_access;
|
||||
}
|
||||
keyrange_rand_max_ = keyrange_start;
|
||||
|
||||
// Step 4. Shuffle the key-ranges randomly
|
||||
// Since the access probability is calculated from small to large,
|
||||
// If we do not re-allocate them, hot key-ranges are always at the end
|
||||
// and cold key-ranges are at the begin of the key space. Therefore, the
|
||||
// key-ranges are shuffled and the rand seed is only decide by the
|
||||
// key-range hotness distribution. With the same distribution parameters
|
||||
// the shuffle results are the same.
|
||||
Random64 rand_loca(keyrange_rand_max_);
|
||||
for (int64_t i = 0; i < KEYRANGE_NUM; i++) {
|
||||
int64_t pos = rand_loca.Next() % KEYRANGE_NUM;
|
||||
assert(i >= 0 && i < static_cast<int64_t>(keyrange_set_.size()) &&
|
||||
pos >= 0 && pos < static_cast<int64_t>(keyrange_set_.size()));
|
||||
std::swap(keyrange_set_[i], keyrange_set_[pos]);
|
||||
}
|
||||
|
||||
// Step 5. Recalculate the prefix start postion after shuffling
|
||||
int64_t offset = 0;
|
||||
for (auto& p_unit : keyrange_set_) {
|
||||
p_unit.keyrange_start = offset;
|
||||
offset += p_unit.keyrange_access;
|
||||
}
|
||||
|
||||
return rocksdb::Status::OK();
|
||||
}
|
||||
|
||||
// Generate the Key ID according to the input ini_rand and key distribution
|
||||
int64_t DistGetKeyID(int64_t ini_rand, double key_dist_a,
|
||||
double key_dist_b) {
|
||||
int64_t keyrange_rand = ini_rand % keyrange_rand_max_;
|
||||
|
||||
// Calculate and select one key-range that contains the new key
|
||||
int64_t start = 0, end = static_cast<int64_t>(keyrange_set_.size());
|
||||
while (start + 1 < end) {
|
||||
int64_t mid = start + (end - start) / 2;
|
||||
assert(mid >= 0 && mid < static_cast<int64_t>(keyrange_set_.size()));
|
||||
if (keyrange_rand < keyrange_set_[mid].keyrange_start) {
|
||||
end = mid;
|
||||
} else {
|
||||
start = mid;
|
||||
}
|
||||
}
|
||||
int64_t keyrange_id = start;
|
||||
|
||||
// Select one key in the key-range and compose the keyID
|
||||
int64_t key_offset = 0, key_seed;
|
||||
if (key_dist_a == 0.0 && key_dist_b == 0.0) {
|
||||
key_offset = ini_rand % keyrange_size_;
|
||||
} else {
|
||||
key_seed = static_cast<int64_t>(
|
||||
ceil(std::pow((ini_rand / key_dist_a), (1 / key_dist_b))));
|
||||
Random64 rand_key(key_seed);
|
||||
key_offset = static_cast<int64_t>(rand_key.Next()) % keyrange_size_;
|
||||
}
|
||||
return keyrange_size_ * keyrange_id + key_offset;
|
||||
}
|
||||
};
|
||||
|
||||
// Decide the ratio of different query types
|
||||
// 0 Get, 1 Put, 2 Seek, 3 SeekForPrev, 4 Delete, 5 SingleDelete, 6 merge
|
||||
class QueryDecider {
|
||||
public:
|
||||
std::vector<int> type_;
|
||||
std::vector<double> ratio_;
|
||||
int range_;
|
||||
|
||||
QueryDecider() {}
|
||||
~QueryDecider() {}
|
||||
|
||||
rocksdb::Status Initiate(std::vector<double> ratio_input) {
|
||||
int range_max = 1000;
|
||||
double sum = 0.0;
|
||||
for (auto& ratio : ratio_input) {
|
||||
sum += ratio;
|
||||
}
|
||||
range_ = 0;
|
||||
for (auto& ratio : ratio_input) {
|
||||
range_ += static_cast<int>(ceil(range_max * (ratio / sum)));
|
||||
type_.push_back(range_);
|
||||
ratio_.push_back(ratio / sum);
|
||||
}
|
||||
return rocksdb::Status::OK();
|
||||
}
|
||||
|
||||
int GetType(int64_t rand_num) {
|
||||
if (rand_num < 0) {
|
||||
rand_num = rand_num * (-1);
|
||||
}
|
||||
assert(range_ != 0);
|
||||
int pos = static_cast<int>(rand_num % range_);
|
||||
for (int i = 0; i < static_cast<int>(type_.size()); i++) {
|
||||
if (pos < type_[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class BaseDistribution {
|
||||
public:
|
||||
BaseDistribution(unsigned int _min, unsigned int _max)
|
||||
: min_value_size_(_min), max_value_size_(_max) {}
|
||||
virtual ~BaseDistribution() {}
|
||||
|
||||
unsigned int Generate() {
|
||||
auto val = Get();
|
||||
if (NeedTruncate()) {
|
||||
val = std::max(min_value_size_, val);
|
||||
val = std::min(max_value_size_, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
private:
|
||||
virtual unsigned int Get() = 0;
|
||||
virtual bool NeedTruncate() {
|
||||
return true;
|
||||
}
|
||||
unsigned int min_value_size_;
|
||||
unsigned int max_value_size_;
|
||||
};
|
||||
|
||||
|
||||
class FixedDistribution : public BaseDistribution
|
||||
{
|
||||
public:
|
||||
FixedDistribution(unsigned int size) :
|
||||
BaseDistribution(size, size),
|
||||
size_(size) {}
|
||||
private:
|
||||
virtual unsigned int Get() override {
|
||||
return size_;
|
||||
}
|
||||
virtual bool NeedTruncate() override {
|
||||
return false;
|
||||
}
|
||||
unsigned int size_;
|
||||
};
|
||||
|
||||
class UniformDistribution
|
||||
: public BaseDistribution,
|
||||
public std::uniform_int_distribution<unsigned int> {
|
||||
public:
|
||||
UniformDistribution(unsigned int _min, unsigned int _max)
|
||||
: BaseDistribution(_min, _max),
|
||||
std::uniform_int_distribution<unsigned int>(_min, _max),
|
||||
gen_(rd_()) {}
|
||||
|
||||
private:
|
||||
virtual unsigned int Get() override {
|
||||
return (*this)(gen_);
|
||||
}
|
||||
virtual bool NeedTruncate() override {
|
||||
return false;
|
||||
}
|
||||
std::random_device rd_;
|
||||
std::mt19937 gen_;
|
||||
};
|
||||
|
||||
class NormalDistribution
|
||||
: public BaseDistribution, public std::normal_distribution<double> {
|
||||
public:
|
||||
NormalDistribution(unsigned int _min, unsigned int _max)
|
||||
: BaseDistribution(_min, _max),
|
||||
// 99.7% values within the range [min, max].
|
||||
std::normal_distribution<double>(
|
||||
(double)(_min + _max) / 2.0 /*mean*/,
|
||||
(double)(_max - _min) / 6.0 /*stddev*/),
|
||||
gen_(rd_()) {}
|
||||
|
||||
private:
|
||||
virtual unsigned int Get() override {
|
||||
return static_cast<unsigned int>((*this)(gen_));
|
||||
}
|
||||
std::random_device rd_;
|
||||
std::mt19937 gen_;
|
||||
};
|
||||
|
||||
|
||||
class Random {
|
||||
private:
|
||||
enum : uint32_t {
|
||||
M = 2147483647L // 2^31-1
|
||||
};
|
||||
enum : uint64_t {
|
||||
A = 16807 // bits 14, 8, 7, 5, 2, 1, 0
|
||||
};
|
||||
|
||||
uint32_t seed_;
|
||||
|
||||
static uint32_t GoodSeed(uint32_t s) { return (s & M) != 0 ? (s & M) : 1; }
|
||||
|
||||
public:
|
||||
// This is the largest value that can be returned from Next()
|
||||
enum : uint32_t { kMaxNext = M };
|
||||
|
||||
explicit Random(uint32_t s) : seed_(GoodSeed(s)) {}
|
||||
|
||||
void Reset(uint32_t s) { seed_ = GoodSeed(s); }
|
||||
|
||||
uint32_t Next() {
|
||||
// We are computing
|
||||
// seed_ = (seed_ * A) % M, where M = 2^31-1
|
||||
//
|
||||
// seed_ must not be zero or M, or else all subsequent computed values
|
||||
// will be zero or M respectively. For all other values, seed_ will end
|
||||
// up cycling through every number in [1,M-1]
|
||||
uint64_t product = seed_ * A;
|
||||
|
||||
// Compute (product % M) using the fact that ((x << 31) % M) == x.
|
||||
seed_ = static_cast<uint32_t>((product >> 31) + (product & M));
|
||||
// The first reduction may overflow by 1 bit, so we may need to
|
||||
// repeat. mod == M is not possible; using > allows the faster
|
||||
// sign-bit-based test.
|
||||
if (seed_ > M) {
|
||||
seed_ -= M;
|
||||
}
|
||||
return seed_;
|
||||
}
|
||||
|
||||
// Returns a uniformly distributed value in the range [0..n-1]
|
||||
// REQUIRES: n > 0
|
||||
uint32_t Uniform(int n) { return Next() % n; }
|
||||
|
||||
// Randomly returns true ~"1/n" of the time, and false otherwise.
|
||||
// REQUIRES: n > 0
|
||||
bool OneIn(int n) { return Uniform(n) == 0; }
|
||||
|
||||
// "Optional" one-in-n, where 0 or negative always returns false
|
||||
// (may or may not consume a random value)
|
||||
bool OneInOpt(int n) { return n > 0 && OneIn(n); }
|
||||
|
||||
// Returns random bool that is true for the given percentage of
|
||||
// calls on average. Zero or less is always false and 100 or more
|
||||
// is always true (may or may not consume a random value)
|
||||
bool PercentTrue(int percentage) {
|
||||
return static_cast<int>(Uniform(100)) < percentage;
|
||||
}
|
||||
|
||||
// Skewed: pick "base" uniformly from range [0,max_log] and then
|
||||
// return "base" random bits. The effect is to pick a number in the
|
||||
// range [0,2^max_log-1] with exponential bias towards smaller numbers.
|
||||
uint32_t Skewed(int max_log) {
|
||||
return Uniform(1 << Uniform(max_log + 1));
|
||||
}
|
||||
|
||||
// Returns a Random instance for use by the current thread without
|
||||
// additional locking
|
||||
static Random* GetTLSInstance();
|
||||
};
|
||||
|
||||
static rocksdb::Slice RandomString(Random* rnd, int len, std::string* dst) {
|
||||
dst->resize(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
(*dst)[i] = static_cast<char>(' ' + rnd->Uniform(95)); // ' ' .. '~'
|
||||
}
|
||||
return rocksdb::Slice(*dst);
|
||||
}
|
||||
|
||||
static rocksdb::Slice CompressibleString(Random* rnd, double compressed_fraction,
|
||||
int len, std::string* dst) {
|
||||
int raw = static_cast<int>(len * compressed_fraction);
|
||||
if (raw < 1) raw = 1;
|
||||
std::string raw_data;
|
||||
RandomString(rnd, raw, &raw_data);
|
||||
|
||||
// Duplicate the random data until we have filled "len" bytes
|
||||
dst->clear();
|
||||
while (dst->size() < (unsigned int)len) {
|
||||
dst->append(raw_data);
|
||||
}
|
||||
dst->resize(len);
|
||||
return rocksdb::Slice(*dst);
|
||||
}
|
||||
|
||||
class RandomGenerator {
|
||||
private:
|
||||
std::string data_;
|
||||
unsigned int pos_;
|
||||
std::unique_ptr<BaseDistribution> dist_;
|
||||
|
||||
public:
|
||||
|
||||
RandomGenerator() {
|
||||
auto max_value_size = VALUESIZE_MAX;
|
||||
switch (VALUESIZE_DIST) {
|
||||
case kUniform:
|
||||
dist_.reset(new UniformDistribution(VALUESIZE_MIN,
|
||||
VALUESIZE_MAX));
|
||||
break;
|
||||
case kNormal:
|
||||
dist_.reset(new NormalDistribution(VALUESIZE_MIN,
|
||||
VALUESIZE_MAX));
|
||||
break;
|
||||
case kFixed:
|
||||
default:
|
||||
dist_.reset(new FixedDistribution(FIXED_VALUE_SIZE));
|
||||
max_value_size = FIXED_VALUE_SIZE;
|
||||
}
|
||||
// We use a limited amount of data over and over again and ensure
|
||||
// that it is larger than the compression window (32KB), and also
|
||||
// large enough to serve all typical value sizes we want to write.
|
||||
Random rnd(301);
|
||||
std::string piece;
|
||||
while (data_.size() < (unsigned)std::max(1048576, max_value_size)) {
|
||||
// Add a short fragment that is as compressible as specified
|
||||
// by FLAGS_compression_ratio.
|
||||
CompressibleString(&rnd, 0.5, 100, &piece);
|
||||
data_.append(piece);
|
||||
}
|
||||
pos_ = 0;
|
||||
}
|
||||
|
||||
rocksdb::Slice Generate(unsigned int len) {
|
||||
assert(len <= data_.size());
|
||||
if (pos_ + len > data_.size()) {
|
||||
pos_ = 0;
|
||||
}
|
||||
pos_ += len;
|
||||
return rocksdb::Slice(data_.data() + pos_ - len, len);
|
||||
}
|
||||
|
||||
rocksdb::Slice Generate() {
|
||||
auto len = dist_->Generate();
|
||||
return Generate(len);
|
||||
}
|
||||
};
|
||||
|
||||
// The inverse function of Pareto distribution
|
||||
static int64_t ParetoCdfInversion(double u, double theta, double k, double sigma) {
|
||||
double ret;
|
||||
if (k == 0.0) {
|
||||
ret = theta - sigma * std::log(u);
|
||||
} else {
|
||||
ret = theta + sigma * (std::pow(u, -1 * k) - 1) / k;
|
||||
}
|
||||
return static_cast<int64_t>(ceil(ret));
|
||||
}
|
||||
|
||||
|
||||
static int64_t GetRandomKey(Random64* rand) {
|
||||
uint64_t rand_int = rand->Next();
|
||||
int64_t key_rand = 0;
|
||||
if (READ_RANDOM_EXP_RANGE == 0) {
|
||||
key_rand = rand_int % TOTAL_KEYS;
|
||||
}
|
||||
return key_rand;
|
||||
}
|
||||
|
||||
static rocksdb::Slice AllocateKey(std::unique_ptr<const char[]>* key_guard, int key_size_) {
|
||||
char* data = new char[key_size_];
|
||||
const char* const_data = data;
|
||||
key_guard->reset(const_data);
|
||||
return rocksdb::Slice(key_guard->get(), key_size_);
|
||||
}
|
||||
|
||||
static void GenerateKeyFromInt(uint64_t v, int64_t num_keys, int key_size_, rocksdb::Slice* key) {
|
||||
char* start = const_cast<char*>(key->data());
|
||||
char* pos = start;
|
||||
|
||||
int bytes_to_fill = std::min(key_size_ - static_cast<int>(pos - start), 8);
|
||||
if (IS_LITTLE_ENDIAN) {
|
||||
for (int i = 0; i < bytes_to_fill; ++i) {
|
||||
pos[i] = (v >> ((bytes_to_fill - i - 1) << 3)) & 0xFF;
|
||||
}
|
||||
} else {
|
||||
memcpy(pos, static_cast<void*>(&v), bytes_to_fill);
|
||||
}
|
||||
pos += bytes_to_fill;
|
||||
if (key_size_ > pos - start) {
|
||||
memset(pos, '0', key_size_ - (pos - start));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GenerateTwoTermExpKeys gen_exp;
|
||||
QueryDecider query;
|
||||
Random64 rand;
|
||||
RandomGenerator gen;
|
||||
rocksdb::Slice key;
|
||||
std::unique_ptr<const char[]> key_guard;
|
||||
|
||||
public:
|
||||
rdb_gen(const int conn_id, std::unordered_map<std::string, std::string>* args);
|
||||
rdb_gen() = delete;
|
||||
~rdb_gen();
|
||||
DISABLE_EVIL_CONSTRUCTORS(rdb_gen);
|
||||
int send_req(int fd);
|
||||
int read_resp(int fd);
|
||||
};
|
||||
#endif
|
30
dismember/util.cc
Normal file
30
dismember/util.cc
Normal file
@ -0,0 +1,30 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void sleep_time(double duration) {
|
||||
if (duration > 0) usleep((useconds_t) (duration * 1000000));
|
||||
}
|
||||
|
||||
#define FNV_64_PRIME (0x100000001b3ULL)
|
||||
#define FNV1_64_INIT (0xcbf29ce484222325ULL)
|
||||
uint64_t fnv_64_buf(const void* buf, size_t len) {
|
||||
uint64_t hval = FNV1_64_INIT;
|
||||
|
||||
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
|
||||
unsigned char *be = bp + len; /* beyond end of buffer */
|
||||
|
||||
while (bp < be) {
|
||||
hval ^= (uint64_t)*bp++;
|
||||
hval *= FNV_64_PRIME;
|
||||
}
|
||||
|
||||
return hval;
|
||||
}
|
||||
|
||||
void generate_key(int n, int length, char *buf) {
|
||||
snprintf(buf, length + 1, "%0*d", length, n);
|
||||
}
|
52
dismember/util.h
Normal file
52
dismember/util.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
inline double tv_to_double(struct timeval *tv) {
|
||||
return tv->tv_sec + (double) tv->tv_usec / 1000000;
|
||||
}
|
||||
|
||||
inline void double_to_tv(double val, struct timeval *tv) {
|
||||
long long secs = (long long) val;
|
||||
long long usecs = (long long) ((val - secs) * 1000000);
|
||||
|
||||
tv->tv_sec = secs;
|
||||
tv->tv_usec = usecs;
|
||||
}
|
||||
|
||||
inline double get_time_accurate() {
|
||||
#if USE_CLOCK_GETTIME
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
// clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ts.tv_sec + (double) ts.tv_nsec / 1000000000;
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv_to_double(&tv);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double get_time() {
|
||||
//#if USE_CLOCK_GETTIME
|
||||
// struct timespec ts;
|
||||
// clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
// // clock_gettime(CLOCK_REALTIME, &ts);
|
||||
// return ts.tv_sec + (double) ts.tv_nsec / 1000000000;
|
||||
//#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv_to_double(&tv);
|
||||
//#endif
|
||||
}
|
||||
|
||||
void sleep_time(double duration);
|
||||
|
||||
uint64_t fnv_64_buf(const void* buf, size_t len);
|
||||
inline uint64_t fnv_64(uint64_t in) { return fnv_64_buf(&in, sizeof(in)); }
|
||||
|
||||
void generate_key(int n, int length, char *buf);
|
||||
|
||||
#endif // UTIL_H
|
19
include/const.h
Executable file
19
include/const.h
Executable file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum WORKLOAD_TYPE {
|
||||
ECHO = 0,
|
||||
TOUCH = 1,
|
||||
RDB = 2,
|
||||
HTTP = 3,
|
||||
};
|
||||
|
||||
struct ppd_msg {
|
||||
uint32_t size;
|
||||
char payload[0];
|
||||
};
|
||||
|
||||
static constexpr int PPD_RDB_OP_GET = 0;
|
||||
static constexpr int PPD_RDB_OP_PUT = 1;
|
||||
static constexpr int PPD_RDB_OP_SEEK = 2;
|
128
include/util.h
Normal file
128
include/util.h
Normal file
@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include "const.h"
|
||||
#include <cerrno>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/event.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <thread>
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/_cpuset.h>
|
||||
#include <sys/cpuset.h>
|
||||
|
||||
#define W(fmt, ...) do { \
|
||||
fprintf(stderr, "[WARN] %s: " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define E(fmt, ...) do { \
|
||||
fprintf(stderr, "[ERROR] %s: " fmt, __func__, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
#define V(fmt, ...) do { \
|
||||
if (options.verbose) { \
|
||||
fprintf(stdout, "[INFO] %s: " fmt, __func__, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void
|
||||
cpulist_to_cpuset(char * cpulist, cpuset_t * cpuset)
|
||||
{
|
||||
char * cpu = strtok(cpulist, ",");
|
||||
CPU_ZERO(cpuset);
|
||||
|
||||
while (cpu != nullptr) {
|
||||
CPU_SET(atoi(cpu), cpuset);
|
||||
cpu = strtok(nullptr, ",");
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
readbuf(int fd, void *buf, int len)
|
||||
{
|
||||
int status;
|
||||
|
||||
do {
|
||||
if ((status = recv(fd, buf, len, 0)) < 0) {
|
||||
return -1;
|
||||
} else if (status == 0) { // connection disconnected.
|
||||
return -1;
|
||||
}
|
||||
buf = (char*)buf + status;
|
||||
len -= status;
|
||||
} while (len > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
writebuf(int fd, void * buf, int len)
|
||||
{
|
||||
int status;
|
||||
|
||||
do {
|
||||
if ((status = send(fd, buf, len, 0)) < 0) {
|
||||
return -1;
|
||||
} else if (status == 0) {
|
||||
return -1;
|
||||
}
|
||||
buf = (char*) buf + status;
|
||||
len -= status;
|
||||
} while (len > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
readmsg(int fd, char *buf, int len)
|
||||
{
|
||||
if ((uint)len < sizeof(struct ppd_msg)) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
int status = readbuf(fd, buf, sizeof(struct ppd_msg));
|
||||
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (((struct ppd_msg *)buf)->size + sizeof(ppd_msg) > (uint)len) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
if (((struct ppd_msg *)buf)->size > 0) {
|
||||
status = readbuf(fd, buf + sizeof(ppd_msg), ((struct ppd_msg *)buf)->size);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int
|
||||
writemsg(int fd, char *buf, int len, const char *msg, int mlen)
|
||||
{
|
||||
int real_len = sizeof(struct ppd_msg) + mlen;
|
||||
if (len < real_len) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
struct ppd_msg * m = (struct ppd_msg *)buf;
|
||||
memmove(m->payload, msg, mlen);
|
||||
|
||||
m->size = mlen;
|
||||
|
||||
return writebuf(fd, buf, real_len);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
get_time_us()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
// clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
#define UNUSED(x) (x)
|
116
include/util_ff.h
Normal file
116
include/util_ff.h
Normal file
@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include "const.h"
|
||||
#include <cerrno>
|
||||
#include <stdint.h>
|
||||
#include <thread>
|
||||
#include <stdio.h>
|
||||
#include <ff_api.h>
|
||||
|
||||
#define W(fmt, ...) do { \
|
||||
fprintf(stderr, "[WARN] " fmt, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define E(fmt, ...) do { \
|
||||
fprintf(stderr, "[ERROR] " fmt, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
#define V(fmt, ...) do { \
|
||||
if (options.verbose) { \
|
||||
fprintf(stdout, "[INFO] " fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline int
|
||||
readbuf(int fd, void *buf, int len)
|
||||
{
|
||||
int status;
|
||||
|
||||
do {
|
||||
if ((status = ff_recv(fd, buf, len, 0)) < 0) {
|
||||
return -1;
|
||||
} else if (status == 0) { // connection disconnected.
|
||||
return -1;
|
||||
}
|
||||
buf = (char*)buf + status;
|
||||
len -= status;
|
||||
} while (len > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
writebuf(int fd, void * buf, int len)
|
||||
{
|
||||
int status;
|
||||
|
||||
do {
|
||||
if ((status = ff_send(fd, buf, len, 0)) < 0) {
|
||||
return -1;
|
||||
} else if (status == 0) {
|
||||
return -1;
|
||||
}
|
||||
buf = (char*) buf + status;
|
||||
len -= status;
|
||||
} while (len > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
readmsg(int fd, char *buf, int len)
|
||||
{
|
||||
if ((uint)len < sizeof(struct ppd_msg)) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
int status = readbuf(fd, buf, sizeof(struct ppd_msg));
|
||||
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (((struct ppd_msg *)buf)->size + sizeof(ppd_msg) > (uint)len) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
if (((struct ppd_msg *)buf)->size > 0) {
|
||||
status = readbuf(fd, buf + sizeof(ppd_msg), ((struct ppd_msg *)buf)->size);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int
|
||||
writemsg(int fd, char *buf, int len, const char *msg, int mlen)
|
||||
{
|
||||
int real_len = sizeof(struct ppd_msg) + mlen;
|
||||
if (len < real_len) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
struct ppd_msg * m = (struct ppd_msg *)buf;
|
||||
memmove(m->payload, msg, mlen);
|
||||
|
||||
m->size = mlen;
|
||||
|
||||
return writebuf(fd, buf, real_len);
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_numcpus()
|
||||
{
|
||||
return std::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
get_time_us()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
// clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
#define UNUSED(x) (x)
|
1695
msg/msg.pb.h
Normal file
1695
msg/msg.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
43
msg/msg.proto
Normal file
43
msg/msg.proto
Normal file
@ -0,0 +1,43 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message ppd_slave_resp {
|
||||
int32 qps = 1;
|
||||
int32 send_sz = 2;
|
||||
int32 resp_sz = 3;
|
||||
}
|
||||
|
||||
message ppd_master_start {
|
||||
int32 dummy = 0;
|
||||
}
|
||||
|
||||
message ppd_rdb_req {
|
||||
int32 op = 1;
|
||||
int32 optarg = 2;
|
||||
bytes key = 3;
|
||||
bytes val = 4;
|
||||
}
|
||||
|
||||
message ppd_rdb_resp {
|
||||
int32 status = 1;
|
||||
bytes result = 2;
|
||||
}
|
||||
|
||||
message ppd_echo_req {
|
||||
int32 enable_delay = 1;
|
||||
int32 data_size = 2;
|
||||
}
|
||||
|
||||
message ppd_echo_resp {
|
||||
int32 status = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message ppd_touch_req {
|
||||
int32 touch_cnt = 1;
|
||||
int32 inc = 2;
|
||||
}
|
||||
|
||||
message ppd_touch_resp {
|
||||
int32 status = 1;
|
||||
}
|
||||
|
0
ppd/conection.cc
Normal file
0
ppd/conection.cc
Normal file
39
ppd/options.h
Normal file
39
ppd/options.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "openssl/types.h"
|
||||
#include <vector>
|
||||
#include <util.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
|
||||
static constexpr int MAX_MODE_PARAMS = 8;
|
||||
static constexpr int MAX_MODE_PARAMS_LEN = 64;
|
||||
|
||||
struct ppd_options {
|
||||
int port;
|
||||
int shared_kq;
|
||||
cpuset_t cpuset;
|
||||
int verbose;
|
||||
int enable_tls;
|
||||
int enable_ktls;
|
||||
char secret_key_fn[128];
|
||||
char cert_fn[128];
|
||||
|
||||
std::vector<char*> hpip;
|
||||
|
||||
int skq_dump;
|
||||
int skq_flag;
|
||||
int skq_rtshare;
|
||||
int skq_rtfreq;
|
||||
|
||||
/* the mode this server runs in */
|
||||
int mode;
|
||||
|
||||
char mode_params[MAX_MODE_PARAMS][MAX_MODE_PARAMS_LEN + 1];
|
||||
int num_mode_params;
|
||||
|
||||
/* runtime states */
|
||||
SSL_CTX * ssl_ctx; // static SSL_CTX
|
||||
};
|
||||
|
||||
extern ppd_options options;
|
679
ppd/ppd.cc
Executable file
679
ppd/ppd.cc
Executable file
@ -0,0 +1,679 @@
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <unordered_map>
|
||||
#include <cstring>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdalign.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/cpuset.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/event.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread_np.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <netdb.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "reqproc.h"
|
||||
#include <const.h>
|
||||
#include <util.h>
|
||||
|
||||
#include <msg.pb.h>
|
||||
|
||||
static constexpr int NEVENT = 2048;
|
||||
static constexpr int SOCK_BACKLOG = 10000;
|
||||
static constexpr int SINGLE_LEGACY = -1;
|
||||
static constexpr int DEFAULT_PORT = 9898;
|
||||
|
||||
static std::unordered_map<std::string, std::string> mode_params;
|
||||
|
||||
struct alignas(CACHE_LINE_SIZE) ppd_cache_item {
|
||||
int val;
|
||||
};
|
||||
static_assert(sizeof(struct ppd_cache_item) == CACHE_LINE_SIZE, "cache_item not cache line sized");
|
||||
|
||||
struct ppd_conn {
|
||||
int conn_fd;
|
||||
req_proc * proc;
|
||||
SSL * ssl;
|
||||
};
|
||||
|
||||
struct ppd_thread_ctx {
|
||||
int tid;
|
||||
pthread_t thrd;
|
||||
int kqfd;
|
||||
long evcnt;
|
||||
};
|
||||
|
||||
ppd_options options = {.port = DEFAULT_PORT,
|
||||
.shared_kq = 0,
|
||||
.cpuset = CPUSET_T_INITIALIZER(1), // first core
|
||||
.verbose = 0,
|
||||
.enable_tls = 0,
|
||||
.enable_ktls = 0,
|
||||
.secret_key_fn = {0},
|
||||
.cert_fn = {0},
|
||||
.skq_dump = 0,
|
||||
.skq_flag = 0,
|
||||
.skq_rtshare = 100,
|
||||
.skq_rtfreq = 0,
|
||||
.mode = WORKLOAD_TYPE::ECHO,
|
||||
.num_mode_params = 0 };
|
||||
|
||||
static int
|
||||
main_kqueue_create(std::vector<int> *server_socks)
|
||||
{
|
||||
int status;
|
||||
struct kevent kev;
|
||||
|
||||
int mkqfd = kqueue();
|
||||
if (mkqfd == -1) {
|
||||
E("Failed to create kqueue: %d.\n", errno);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < server_socks->size(); i++) {
|
||||
EV_SET(&kev, server_socks->at(i), EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
|
||||
status = kevent(mkqfd, &kev, 1, NULL, 0, NULL);
|
||||
if (status == -1) {
|
||||
E("kevent() failed: %d.\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
#define TIMER_FD (-1234)
|
||||
EV_SET(&kev, TIMER_FD, EVFILT_TIMER, EV_ADD, NOTE_MSECONDS, 1, NULL);
|
||||
status = kevent(mkqfd, &kev, 1, NULL, 0, NULL);
|
||||
if (status == -1) {
|
||||
E("kevent() timer failed: %d.\n", errno);
|
||||
}
|
||||
|
||||
return mkqfd;
|
||||
}
|
||||
|
||||
static void
|
||||
create_tls_context()
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
|
||||
ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (!ctx) {
|
||||
E("SSL_CTX_new() failed: %ld\n", ERR_get_error());
|
||||
}
|
||||
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
|
||||
|
||||
if (options.enable_ktls) {
|
||||
if ((SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS) & SSL_OP_ENABLE_KTLS) != SSL_OP_ENABLE_KTLS) {
|
||||
E("SSL_CTX_set_options() SSL_OP_ENABLE_KTLS failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the key and cert */
|
||||
if (SSL_CTX_use_certificate_file(ctx, options.cert_fn, SSL_FILETYPE_PEM) <= 0) {
|
||||
E("SSL_CTX_use_certificate_file() %s failed: %ld\n", options.cert_fn, ERR_get_error());
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, options.secret_key_fn, SSL_FILETYPE_PEM) <= 0 ) {
|
||||
E("SSL_CTX_use_PrivateKey_file() %s failed: %ld\n", options.secret_key_fn, ERR_get_error());
|
||||
}
|
||||
|
||||
options.ssl_ctx = ctx;
|
||||
}
|
||||
|
||||
static void
|
||||
listen_socket_create(std::vector<int> *socks)
|
||||
{
|
||||
struct sockaddr_in server_addr;
|
||||
int status;
|
||||
const int enable = 1;
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(options.port);
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
for (int i = 0; i < 1; i++) {
|
||||
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (fd < 0) {
|
||||
E("socket() returned %d", errno);
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
|
||||
E("setsockopt() reuseaddr %d", errno);
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) < 0) {
|
||||
E("setsockopt() reuseport", errno);
|
||||
}
|
||||
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
|
||||
E("setsockopt() NODELAY %d", errno);
|
||||
}
|
||||
|
||||
status = bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
|
||||
if (status < 0) {
|
||||
E("bind() returned %d", errno);
|
||||
}
|
||||
|
||||
status = listen(fd, SOCK_BACKLOG);
|
||||
if (status < 0) {
|
||||
E("listen() returned %d", errno);
|
||||
}
|
||||
|
||||
socks->push_back(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ppd_conn_free(struct ppd_conn *conn)
|
||||
{
|
||||
SSL_shutdown(conn->ssl);
|
||||
SSL_free(conn->ssl);
|
||||
close(conn->conn_fd);
|
||||
delete conn->proc;
|
||||
delete conn;
|
||||
}
|
||||
|
||||
static void
|
||||
drop_conn(struct ppd_thread_ctx *tinfo, struct kevent *kev)
|
||||
{
|
||||
int status;
|
||||
int conn_fd = kev->ident;
|
||||
struct kevent ev;
|
||||
EV_SET(&ev, conn_fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
|
||||
status = kevent(tinfo->kqfd, &ev, 1, 0, 0, NULL);
|
||||
|
||||
if (status < 0) {
|
||||
E("Failed to drop connection %d from kqueue: %d\n", conn_fd, errno);
|
||||
}
|
||||
|
||||
ppd_conn_free((struct ppd_conn *)kev->udata);
|
||||
}
|
||||
|
||||
static int
|
||||
handle_event(struct ppd_thread_ctx *tinfo, struct kevent* kev)
|
||||
{
|
||||
int conn_fd;
|
||||
int status;
|
||||
struct ppd_conn *hint;
|
||||
|
||||
if (kev->filter != EVFILT_READ) {
|
||||
E("Unknown event filter %d\n", kev->filter);
|
||||
}
|
||||
|
||||
conn_fd = kev->ident;
|
||||
hint = (struct ppd_conn*)kev->udata;
|
||||
|
||||
if (kev->flags & EV_EOF) {
|
||||
V("Thread %d dropped connection %d due to EOF.\n", tinfo->tid, conn_fd);
|
||||
drop_conn(tinfo, kev);
|
||||
return ECONNRESET;
|
||||
}
|
||||
|
||||
status = hint->proc->proc_req(conn_fd);
|
||||
|
||||
if (status < 0) {
|
||||
W("Thread %d dropped connection %d due to req_proc error %d\n", tinfo->tid, conn_fd, status);
|
||||
drop_conn(tinfo, kev);
|
||||
return status;
|
||||
}
|
||||
|
||||
tinfo->evcnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void*
|
||||
worker_main(void *info)
|
||||
{
|
||||
struct ppd_thread_ctx *tinfo = (struct ppd_thread_ctx *)info;
|
||||
struct kevent kev[NEVENT];
|
||||
struct kevent skev[NEVENT];
|
||||
int skev_sz;
|
||||
int status;
|
||||
|
||||
V("Thread %d started.\n", tinfo->tid);
|
||||
|
||||
skev_sz = 0;
|
||||
while (1) {
|
||||
status = kevent(tinfo->kqfd, skev, skev_sz, kev, NEVENT, NULL);
|
||||
|
||||
if (status <= 0) {
|
||||
E("kevent() failed: %d\n", errno);
|
||||
}
|
||||
|
||||
skev_sz = 0;
|
||||
for (int i = 0; i < status; i++) {
|
||||
if (handle_event(tinfo, &kev[i]) == 0) {
|
||||
if (options.shared_kq && options.skq_flag == SINGLE_LEGACY) {
|
||||
EV_SET(&skev[skev_sz], (int)kev[i].ident, EVFILT_READ, EV_ENABLE, 0, 0, kev[i].udata);
|
||||
skev_sz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dump_options()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Configuration:\n"
|
||||
<< " port: " << options.port << std::endl
|
||||
<< " mode: " << options.mode << std::endl
|
||||
<< " enable TLS: " << options.enable_tls << std::endl
|
||||
<< " enable kTLS: " << options.enable_ktls << std::endl
|
||||
<< " TLS secret key: " << options.secret_key_fn << std::endl
|
||||
<< " TLS certificate: " << options.cert_fn << std::endl
|
||||
<< " shared Kqueue: " << options.shared_kq << std::endl
|
||||
<< " number of threads: " << CPU_COUNT(&options.cpuset) << std::endl
|
||||
<< " verbose: " << options.verbose << std::endl
|
||||
<< " SKQ flags: " << options.skq_flag << std::endl
|
||||
<< " SKQ dump:" << options.skq_dump << std::endl
|
||||
<< " SKQ rtshare: " << options.skq_rtshare << std::endl
|
||||
<< " SKQ rtfreq: " << options.skq_rtfreq << std::endl
|
||||
<< " SKQ high priority clients (" << options.hpip.size() << "): " << std::endl;
|
||||
|
||||
for(uint i = 0; i < options.hpip.size(); i++) {
|
||||
ss << " " << options.hpip.at(i) << std::endl;
|
||||
}
|
||||
|
||||
V("%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stdout, "Usage:\n"
|
||||
" p: server port\n"
|
||||
" v: verbose mode\n"
|
||||
" m: shared Kqueue across workers\n"
|
||||
" s: enable TLS\n"
|
||||
" k: enable kTLS\n"
|
||||
" S: TLS private key file\n"
|
||||
" C: TLS certificate file\n"
|
||||
" c: server thread cpu list\n"
|
||||
" h: show help\n"
|
||||
" d: SKQ dump interval (s)\n"
|
||||
" r: SKQ realtime client hostname(s)\n"
|
||||
" R: SKQ rt_share\n"
|
||||
" F: SKQ rt_freq\n"
|
||||
" M: server mode: 0 - ECHO, 1 - TOUCH, 2 - HTTP, 3 - RDB\n"
|
||||
" O: mode specific parameters in the format \"key=value\"\n"
|
||||
" Workload specific parameters:\n"
|
||||
" TOUCH:\n"
|
||||
" ENTRIES - Number of cache-aligned entries per connection.\n\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
create_workers(std::vector<struct ppd_thread_ctx*> *workers)
|
||||
{
|
||||
int kq = -1;
|
||||
|
||||
if (options.shared_kq) {
|
||||
kq = kqueue();
|
||||
|
||||
if (kq <= 0) {
|
||||
E("Failed to create kqueue. ERR %d\n", errno);
|
||||
}
|
||||
|
||||
#ifdef FKQMULTI
|
||||
if (options.skq_flag != SINGLE_LEGACY) {
|
||||
status = ioctl(kq, FKQMULTI, &options.skq_flag);
|
||||
if (status == -1) {
|
||||
E("ioctl failed. ERR %d\n", errno);
|
||||
}
|
||||
V("SKQ enabled. SFLAG %d\n", options.skq_flag);
|
||||
}
|
||||
|
||||
int para = KQTUNE_MAKE(KQTUNE_RTSHARE, options.kq_rtshare);
|
||||
status = ioctl(kq, FKQTUNE, ¶);
|
||||
if (status == -1) {
|
||||
E("rtshare ioctl failed. ERR %d\n", errno);
|
||||
}
|
||||
|
||||
para = KQTUNE_MAKE(KQTUNE_FREQ, options.kq_tfreq);
|
||||
status = ioctl(kq, FKQTUNE, ¶);
|
||||
if (status == -1) {
|
||||
E("freq ioctl failed. ERR %d\n", errno);
|
||||
}
|
||||
V("KQ IOCTL: RTSHARE %d TFREQ %d\n", options.kq_rtshare, options.kq_tfreq);
|
||||
#else
|
||||
/* legacy single KQ only supports flag -1 */
|
||||
options.skq_flag = SINGLE_LEGACY;
|
||||
#endif
|
||||
}
|
||||
|
||||
int tid = 0;
|
||||
int core;
|
||||
CPU_FOREACH_ISSET(core, &options.cpuset) {
|
||||
V("Creating worker thread on core %d...\n", core);
|
||||
|
||||
struct ppd_thread_ctx *thrd = (struct ppd_thread_ctx *)aligned_alloc(CACHE_LINE_SIZE, sizeof(struct ppd_thread_ctx));
|
||||
thrd->evcnt = 0;
|
||||
thrd->tid = tid;
|
||||
|
||||
if (!options.shared_kq) {
|
||||
kq = kqueue();
|
||||
if (kq <= 0) {
|
||||
E("kqueue() failed: %d\n", errno);
|
||||
}
|
||||
|
||||
#ifdef FKQMULTI
|
||||
int para = KQTUNE_MAKE(KQTUNE_RTSHARE, options.kq_rtshare);
|
||||
status = ioctl(kq, FKQTUNE, ¶);
|
||||
if (status == -1) {
|
||||
E("rtshare ioctl failed. ERR %d\n", errno);
|
||||
}
|
||||
|
||||
para = KQTUNE_MAKE(KQTUNE_FREQ, options.kq_tfreq);
|
||||
status = ioctl(kq, FKQTUNE, ¶);
|
||||
if (status == -1) {
|
||||
E("freq ioctl failed. ERR %d\n", errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
thrd->kqfd = kq;
|
||||
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
cpuset_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(core, &cpuset);
|
||||
|
||||
int status = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
|
||||
if (status != 0) {
|
||||
E("pthread_attr_setaffinity_np() failed : %d\n", status);
|
||||
}
|
||||
|
||||
status = pthread_create(&thrd->thrd, &attr, worker_main, thrd);
|
||||
workers->push_back(thrd);
|
||||
|
||||
tid++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_ip_from_hostname(const char* hostname, char* buf, int len)
|
||||
{
|
||||
struct in_addr **addr;
|
||||
struct hostent *he;
|
||||
|
||||
if ((he = gethostbyname(hostname)) == NULL) {
|
||||
E("Hostname %s cannot be resolved.\n", hostname);
|
||||
}
|
||||
addr = (struct in_addr**)he->h_addr_list;
|
||||
for (int i=0;addr[i]!=NULL;i++) {
|
||||
strncpy(buf, inet_ntoa(*addr[i]), len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_mode_params()
|
||||
{
|
||||
char * saveptr;
|
||||
|
||||
for (int i = 0; i < options.num_mode_params; i++) {
|
||||
saveptr = NULL;
|
||||
char *key = strtok_r(options.mode_params[i], "=", &saveptr);
|
||||
char *val = strtok_r(NULL, "=", &saveptr);
|
||||
|
||||
mode_params.insert({key, val});
|
||||
|
||||
V("Parsed workload specific parameter: %s = %s\n", key, val);
|
||||
}
|
||||
}
|
||||
|
||||
static SSL *
|
||||
tls_handshake_server(int conn_fd)
|
||||
{
|
||||
SSL * ssl;
|
||||
int r;
|
||||
|
||||
ssl = SSL_new(options.ssl_ctx);
|
||||
if (ssl == NULL) {
|
||||
E("SSL_new() failed: %ld\n", ERR_get_error());
|
||||
}
|
||||
|
||||
if (SSL_set_fd(ssl, conn_fd) == 0) {
|
||||
E("SSL_set_fd() failed: %ld\n", ERR_get_error());
|
||||
}
|
||||
|
||||
if ((r = SSL_accept(ssl)) <= 0) {
|
||||
E("SSL_accept() failed: %ld\n", ERR_get_error());
|
||||
}
|
||||
|
||||
if (options.enable_ktls) {
|
||||
// make sure ktls is enabled
|
||||
if (!(BIO_get_ktls_send(SSL_get_wbio(ssl)) && BIO_get_ktls_recv(SSL_get_rbio(ssl)))) {
|
||||
E("kTLS not enabled on %d: %ld\n", conn_fd, ERR_get_error());
|
||||
}
|
||||
}
|
||||
|
||||
return ssl;
|
||||
}
|
||||
|
||||
static void
|
||||
loop_main(int m_kq, std::vector<struct ppd_thread_ctx*> *workers)
|
||||
{
|
||||
struct kevent kev;
|
||||
int cur_conn = 0;
|
||||
long cur_ts = 0;
|
||||
while (1) {
|
||||
if (kevent(m_kq, NULL, 0, &kev, 1, NULL) != 1) {
|
||||
E("kevent() failed: %d\n", errno);
|
||||
}
|
||||
|
||||
if (kev.ident == (uintptr_t)TIMER_FD) {
|
||||
#ifdef FKQMULTI
|
||||
/* timer event */
|
||||
if (options.skq_dump > 0 && cur_ts % (options.skq_dump * 1000) == 0) {
|
||||
uintptr_t args = (uintptr_t)dbuf;
|
||||
memset(dbuf, 0, 1024 * 1024 + 1);
|
||||
status = ioctl(kq, FKQMPRNT, &args);
|
||||
if (status == -1) {
|
||||
E("SKQ dump failed %d\n", errno);
|
||||
} else {
|
||||
fprintf(stdout, "====== KQ DUMP ======\n%s\n", dbuf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
cur_ts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// new connection
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_addr_size = sizeof(client_addr);
|
||||
|
||||
int conn_fd = accept(kev.ident, (struct sockaddr*)&client_addr, &client_addr_size);
|
||||
|
||||
if (conn_fd < 0) {
|
||||
W("accept() failed: %d\n", errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
const int enable = 1;
|
||||
if (setsockopt(conn_fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
|
||||
W("setsockopt() nodelay failed on conn %d: err %d\n", conn_fd, errno);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
char ipaddr[INET_ADDRSTRLEN + 1];
|
||||
strncpy(ipaddr, inet_ntoa(client_addr.sin_addr), INET_ADDRSTRLEN);
|
||||
ipaddr[INET_ADDRSTRLEN] = 0;
|
||||
|
||||
V("Accepted new connection %d from %s\n", conn_fd, ipaddr);
|
||||
|
||||
struct ppd_conn *conn = new struct ppd_conn;
|
||||
|
||||
conn->conn_fd = conn_fd;
|
||||
switch (options.mode) {
|
||||
case WORKLOAD_TYPE::ECHO:
|
||||
conn->proc = new echo_proc(&mode_params);
|
||||
break;
|
||||
case WORKLOAD_TYPE::TOUCH:
|
||||
conn->proc = new touch_proc(&mode_params);
|
||||
break;
|
||||
#ifdef WITH_ROCKSDB
|
||||
case WORKLOAD_TYPE::RDB:
|
||||
hint->proc = new rdb_proc(&mode_params);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
E("Unknown server mode %d", options.mode);
|
||||
}
|
||||
|
||||
if (options.enable_tls) {
|
||||
conn->ssl = tls_handshake_server(conn_fd);
|
||||
V("Established TLS on connection %d...\n", conn_fd);
|
||||
}
|
||||
|
||||
int target_kq = workers->at(cur_conn % workers->size())->kqfd;
|
||||
int ev_flags = EV_ADD;
|
||||
|
||||
#ifdef FKQMULTI
|
||||
for (uint32_t i = 0; i < options.hpip.size(); i++) {
|
||||
if (strcmp(ipaddr, options.hpip.at(i)) == 0) {
|
||||
V("Connection %d marked as realtime.\n", conn_fd);
|
||||
ev_flags |= EV_REALTIME;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options.shared_kq && options.skq_flag == SINGLE_LEGACY) {
|
||||
ev_flags |= EV_DISPATCH;
|
||||
}
|
||||
|
||||
EV_SET(&kev, conn_fd, EVFILT_READ, ev_flags, 0, 0, conn);
|
||||
int status = kevent(target_kq, &kev, 1, NULL, 0, NULL);
|
||||
if (status == -1) {
|
||||
E("kevent() failed: %d\n", errno);
|
||||
}
|
||||
|
||||
V("Connection %d assigned to thread %d\n", conn_fd, workers->at(cur_conn % workers->size())->tid);
|
||||
cur_conn++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
SSL_library_init();
|
||||
srand(time(NULL));
|
||||
// don't raise SIGPIPE when sending into broken TCP connections
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
#ifdef FKQMULTI
|
||||
char dbuf[1024 * 1024 + 1];
|
||||
#endif
|
||||
char ch;
|
||||
while ((ch = getopt(argc, argv, "d:p:c:m:vhr:R:F:M:O:skS:C:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
options.skq_dump = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
options.port = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
cpulist_to_cpuset(optarg, &options.cpuset);
|
||||
break;
|
||||
case 'm':
|
||||
options.shared_kq = 1;
|
||||
options.skq_flag = atoi(optarg);
|
||||
break;
|
||||
case 'O': {
|
||||
if (options.num_mode_params >= MAX_MODE_PARAMS) {
|
||||
E("Too many workload parameters.\n");
|
||||
}
|
||||
snprintf(options.mode_params[options.num_mode_params], MAX_MODE_PARAMS_LEN, "%s", optarg);
|
||||
options.num_mode_params++;
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
options.mode = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
options.verbose = 1;
|
||||
W("Verbose mode can cause SUBSTANTIAL latency fluctuations in some terminals!\n");
|
||||
break;
|
||||
case 'r': {
|
||||
char* eip = new char[INET_ADDRSTRLEN + 1];
|
||||
get_ip_from_hostname(optarg, eip, INET_ADDRSTRLEN);
|
||||
options.hpip.push_back(eip);
|
||||
break;
|
||||
}
|
||||
case 'R':
|
||||
options.skq_rtshare = atoi(optarg);
|
||||
break;
|
||||
case 'F':
|
||||
options.skq_rtfreq = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
options.enable_tls = 1;
|
||||
break;
|
||||
case 'k':
|
||||
options.enable_ktls = 1;
|
||||
break;
|
||||
case 'S':
|
||||
snprintf(options.secret_key_fn, sizeof(options.secret_key_fn), "%s", optarg);
|
||||
break;
|
||||
case 'C':
|
||||
snprintf(options.cert_fn, sizeof(options.cert_fn), "%s", optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
default:
|
||||
E("Unrecognized option -%c. See -h.\n\n", ch);
|
||||
}
|
||||
}
|
||||
dump_options();
|
||||
parse_mode_params();
|
||||
|
||||
std::vector<struct ppd_thread_ctx *> wrk_thrds;
|
||||
std::vector<int> server_socks;
|
||||
|
||||
V("Setting up server sockets...\n");
|
||||
listen_socket_create(&server_socks);
|
||||
|
||||
if (options.enable_tls) {
|
||||
V("Setting up TLS context...\n");
|
||||
create_tls_context();
|
||||
}
|
||||
|
||||
V("Setting up main thread kqueue...\n");
|
||||
int m_kq = main_kqueue_create(&server_socks);
|
||||
|
||||
V("Creating worker threads...\n");
|
||||
create_workers(&wrk_thrds);
|
||||
|
||||
V("Entering main event loop...\n");
|
||||
loop_main(m_kq, &wrk_thrds);
|
||||
|
||||
E("Critical bug - main loop exited\n");
|
||||
return 0;
|
||||
}
|
324
ppd/reqproc.cc
Normal file
324
ppd/reqproc.cc
Normal file
@ -0,0 +1,324 @@
|
||||
#include "reqproc.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <random>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/param.h>
|
||||
#include <chrono>
|
||||
#include <unistd.h>
|
||||
#include "options.h"
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
#include <rocksdb/cache.h>
|
||||
#include <rocksdb/db.h>
|
||||
#include <rocksdb/env.h>
|
||||
#include <rocksdb/filter_policy.h>
|
||||
#include <rocksdb/memtablerep.h>
|
||||
#include <rocksdb/options.h>
|
||||
#include <rocksdb/perf_context.h>
|
||||
#include <rocksdb/persistent_cache.h>
|
||||
#include <rocksdb/rate_limiter.h>
|
||||
#include <rocksdb/slice.h>
|
||||
#include <rocksdb/slice_transform.h>
|
||||
#include <rocksdb/stats_history.h>
|
||||
#include <rocksdb/utilities/object_registry.h>
|
||||
#include <rocksdb/utilities/optimistic_transaction_db.h>
|
||||
#include <rocksdb/utilities/options_util.h>
|
||||
#include <rocksdb/utilities/sim_cache.h>
|
||||
#include <rocksdb/utilities/transaction.h>
|
||||
#include <rocksdb/utilities/transaction_db.h>
|
||||
#include <rocksdb/write_batch.h>
|
||||
#include <rocksdb/table.h>
|
||||
#endif
|
||||
|
||||
#include <msg.pb.h>
|
||||
#include <util.h>
|
||||
#include <const.h>
|
||||
|
||||
////////////////
|
||||
// TOUCH Generator
|
||||
////////////////
|
||||
touch_proc::touch_proc(std::unordered_map<std::string, std::string>* args) : req_proc()
|
||||
{
|
||||
if (args->find(PARAM_TBSZ) != args->end()) {
|
||||
this->buffer_sz = atoi(args->at(PARAM_TBSZ).c_str());
|
||||
} else {
|
||||
this->buffer_sz = PARAM_TBSZ_DEFAULT;
|
||||
}
|
||||
|
||||
V("Allocating %d items x %d CASZ for connnection\n", this->buffer_sz, CACHE_LINE_SIZE);
|
||||
this->buffer = (struct ppd_touch_cache_item *)aligned_alloc(CACHE_LINE_SIZE, this->buffer_sz * sizeof(struct ppd_touch_cache_item));
|
||||
|
||||
this->rgen = new std::default_random_engine();
|
||||
this->rdist = new std::uniform_int_distribution<int>(0, INT32_MAX);
|
||||
}
|
||||
|
||||
touch_proc::~touch_proc()
|
||||
{
|
||||
delete this->buffer;
|
||||
delete this->rgen;
|
||||
delete this->rdist;
|
||||
}
|
||||
|
||||
int touch_proc::proc_req(int fd)
|
||||
{
|
||||
ppd_touch_req req;
|
||||
ppd_touch_resp resp;
|
||||
struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
|
||||
int ret = readmsg(fd, this->read_buf, this->MAX_READ_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
W("Readmsg failed with %d for connection %d\n", ret, fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
W("ParseFromArray failed for connection %d\n", fd);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
V("Conn %d touching %d items...\n", fd, req.touch_cnt());
|
||||
|
||||
int sum = 0;
|
||||
int rand = (*this->rdist)(*this->rgen);
|
||||
for(int64_t i = rand; i < rand + req.touch_cnt(); i++) {
|
||||
if (req.inc() > 0) {
|
||||
this->buffer[i % this->buffer_sz].val += 1;
|
||||
} else {
|
||||
/* only read */
|
||||
sum += this->buffer[i % this->buffer_sz].val;
|
||||
}
|
||||
}
|
||||
sum = sum + 1;
|
||||
|
||||
resp.set_status(0);
|
||||
if (!resp.SerializeToArray(this->read_buf, MAX_READ_BUF_SIZE)) {
|
||||
W("Couldn't searialize to array connection %d\n", fd);
|
||||
}
|
||||
|
||||
ret = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
if (ret < 0) {
|
||||
W("Writemsg failed with %d for connection %d\n", ret, fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// ECHO Generator
|
||||
////////////////
|
||||
|
||||
echo_proc::echo_proc(std::unordered_map<std::string, std::string>*) : req_proc()
|
||||
{
|
||||
this->buf = new char[BUFFER_SZ];
|
||||
srand(time(NULL));
|
||||
for(size_t i = 0; i < BUFFER_SZ; i++) {
|
||||
char ch = (char)rand();
|
||||
this->buf[i] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
echo_proc::~echo_proc()
|
||||
{
|
||||
delete this->buf;
|
||||
}
|
||||
|
||||
int echo_proc::proc_req(int fd)
|
||||
{
|
||||
uint64_t ms1, ms2;
|
||||
struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
int ret = readmsg(fd, this->read_buf, MAX_READ_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
W("Readbuf failed with %d for connection %d\n", ret, fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ms1 = get_time_us();
|
||||
if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
W("ParseFromArray failed for connection %d\n", fd);
|
||||
return EINVAL;
|
||||
}
|
||||
int data_sz = req.data_size();
|
||||
V("Connection %d delay %d us %d data size \n", fd, req.enable_delay(), data_sz);
|
||||
|
||||
if (req.enable_delay() > 0) {
|
||||
uint64_t server_delay = req.enable_delay();
|
||||
uint64_t now = get_time_us();
|
||||
while (get_time_us() - now <= server_delay) {};
|
||||
}
|
||||
|
||||
if (data_sz > (int)BUFFER_SZ) {
|
||||
data_sz = BUFFER_SZ;
|
||||
}
|
||||
if (data_sz <= 0) {
|
||||
data_sz = 1;
|
||||
}
|
||||
resp.set_data(this->buf, data_sz);
|
||||
resp.set_status(0);
|
||||
if (!resp.SerializeToArray(this->read_buf, MAX_READ_BUF_SIZE)) {
|
||||
W("Couldn't searialize to array connection %d\n", fd);
|
||||
}
|
||||
ms2 = get_time_us();
|
||||
|
||||
V("Serialization: TIME: %ld\n", ms2 - ms1);
|
||||
|
||||
ret = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
if (ret < 0) {
|
||||
W("Writemsg failed with %d for connection %d\n", ret, fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
////////////////
|
||||
// rdb Generator
|
||||
////////////////
|
||||
|
||||
rocksdb::DB * rdb_proc::db = nullptr;
|
||||
std::atomic<int> rdb_proc::db_init {0};
|
||||
|
||||
rdb_proc::rdb_proc(std::unordered_map<std::string, std::string>* args) : req_proc()
|
||||
{
|
||||
const char * db_path;
|
||||
int desired = 0;
|
||||
int target = 1;
|
||||
if (std::atomic_compare_exchange_strong(&rdb_proc::db_init, &desired, target)) {
|
||||
if (args->find(PARAM_PATH) != args->end()) {
|
||||
db_path = args->at(PARAM_PATH).c_str();
|
||||
} else {
|
||||
E("Must specify -OPATH for rocksdb.\n");
|
||||
}
|
||||
|
||||
V("Initializing rocksdb, path: %s.\n", db_path);
|
||||
|
||||
rocksdb::Options opt;
|
||||
std::shared_ptr<rocksdb::Cache> cache = rocksdb::NewLRUCache(CACHE_SIZE, 6, false, 0.0);
|
||||
opt.use_direct_io_for_flush_and_compaction = USE_DIRECT_IO_FOR_FLUSH_AND_COMPACTION;
|
||||
opt.use_direct_reads = USE_DIRECT_READS;
|
||||
|
||||
rocksdb::BlockBasedTableOptions block_based_options;
|
||||
block_based_options.index_type = rocksdb::BlockBasedTableOptions::kBinarySearch;
|
||||
block_based_options.block_cache = cache;
|
||||
opt.table_factory.reset(rocksdb::NewBlockBasedTableFactory(block_based_options));
|
||||
|
||||
opt.IncreaseParallelism(12);
|
||||
opt.OptimizeLevelStyleCompaction(1024 * 1024 * 1024);
|
||||
opt.OptimizeUniversalStyleCompaction(1024 * 1024 * 1024);
|
||||
opt.write_buffer_size = 1024 * 1024 * 1024;
|
||||
opt.create_if_missing = false;
|
||||
opt.compression = rocksdb::kNoCompression;
|
||||
|
||||
|
||||
rocksdb::Status s = rocksdb::DB::Open(opt, std::string(db_path), &this->db);
|
||||
if (!s.ok()) {
|
||||
E("Could not open rocksdb! Err %s\n", s.ToString().c_str());
|
||||
}
|
||||
|
||||
rdb_proc::db_init.store(2);
|
||||
V("Finished initializing rocksdb.\n");
|
||||
} else {
|
||||
V("Checking for rocksdb initialization...\n");
|
||||
while(rdb_proc::db_init.load() != 2) {};
|
||||
V("Detected initialized rocksdb.\n");
|
||||
}
|
||||
}
|
||||
|
||||
rdb_proc::~rdb_proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int rdb_proc::proc_req(int fd)
|
||||
{
|
||||
ppd_rdb_resp resp;
|
||||
ppd_rdb_req req;
|
||||
rocksdb::Status s;
|
||||
struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
|
||||
int i = 0;
|
||||
int status = readmsg(fd, this->read_buf, MAX_READ_BUF_SIZE);
|
||||
|
||||
if (status != 0) {
|
||||
W("Readmsg failed with %d for connection %d\n", status, fd);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
W("ParseFromArray failed for connection %d\n", fd);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
V("Connection %d op: %d, key: %s. val: %s. optarg: %d.\n", fd, req.op(), req.key().c_str(), req.val().c_str(), req.optarg());
|
||||
|
||||
switch (req.op()) {
|
||||
case PPD_RDB_OP_PUT:{
|
||||
s = this->db->Put(rocksdb::WriteOptions(), req.key(), req.val());
|
||||
resp.set_status(s.code());
|
||||
break;
|
||||
}
|
||||
case PPD_RDB_OP_GET: {
|
||||
std::string val;
|
||||
s = this->db->Get(rocksdb::ReadOptions(), req.key(), &val);
|
||||
if (s.ok()) {
|
||||
resp.set_result(val);
|
||||
}
|
||||
resp.set_status(s.code());
|
||||
break;
|
||||
}
|
||||
case PPD_RDB_OP_SEEK: {
|
||||
rocksdb::Slice val;
|
||||
rocksdb::Iterator *it = this->db->NewIterator(rocksdb::ReadOptions(false, true));
|
||||
|
||||
it->Seek(req.key());
|
||||
resp.set_status(it->status().code());
|
||||
|
||||
if (it->Valid()) {
|
||||
val = it->value();
|
||||
resp.set_result(val.data(), val.size());
|
||||
}
|
||||
|
||||
for(int64_t j = 0; j < req.optarg() && it->Valid(); j++) {
|
||||
rocksdb::Slice val = it->value();
|
||||
// do something about the key
|
||||
std::memcpy(this->read_buf, val.data(), MIN(val.size(), MAX_READ_BUF_SIZE));
|
||||
it->Next();
|
||||
if (!it->status().ok()) {
|
||||
resp.set_status(it->status().code());
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete it;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
W("Invalid opcode %d for connection %d\n", req.op(), fd);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
resp.set_status(0);
|
||||
|
||||
status = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
if (status < 0) {
|
||||
W("Writemsg failed with %d for connection %d\n", status, fd);
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
85
ppd/reqproc.h
Normal file
85
ppd/reqproc.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#ifdef WITH_ROCKSDB
|
||||
#include <rocksdb/db.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <const.h>
|
||||
#include <atomic>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <msg.pb.h>
|
||||
|
||||
#define DISABLE_EVIL_CONSTRUCTORS(name) \
|
||||
name(const name&) = delete; \
|
||||
void operator=(const name) = delete
|
||||
|
||||
struct alignas(CACHE_LINE_SIZE) ppd_touch_cache_item {
|
||||
int val;
|
||||
};
|
||||
|
||||
class req_proc {
|
||||
protected:
|
||||
constexpr static int MAX_READ_BUF_SIZE = 1024 * 1024;
|
||||
char * read_buf;
|
||||
public:
|
||||
req_proc() {this->read_buf = new char[MAX_READ_BUF_SIZE]; };
|
||||
virtual ~req_proc() {delete[] this->read_buf;};
|
||||
virtual int proc_req(int fd) = 0;
|
||||
};
|
||||
|
||||
class touch_proc : public req_proc
|
||||
{
|
||||
private:
|
||||
int buffer_sz;
|
||||
std::default_random_engine * rgen;
|
||||
std::uniform_int_distribution<int> * rdist;
|
||||
struct ppd_touch_cache_item* buffer;
|
||||
static constexpr const char* PARAM_TBSZ = "ENTRIES";
|
||||
static constexpr const int PARAM_TBSZ_DEFAULT = 64;
|
||||
constexpr static int MAX_SZ = 1024 * 1024;
|
||||
public:
|
||||
touch_proc(std::unordered_map<std::string, std::string>* args);
|
||||
touch_proc() = delete;
|
||||
~touch_proc();
|
||||
DISABLE_EVIL_CONSTRUCTORS(touch_proc);
|
||||
int proc_req(int fd);
|
||||
};
|
||||
|
||||
class echo_proc : public req_proc
|
||||
{
|
||||
private:
|
||||
ppd_echo_req req;
|
||||
ppd_echo_resp resp;
|
||||
char * buf;
|
||||
static constexpr size_t BUFFER_SZ = 1024 * 1024;
|
||||
public:
|
||||
echo_proc(std::unordered_map<std::string, std::string>* args);
|
||||
echo_proc() = delete;
|
||||
~echo_proc();
|
||||
DISABLE_EVIL_CONSTRUCTORS(echo_proc);
|
||||
int proc_req(int fd);
|
||||
};
|
||||
|
||||
#ifdef WITH_ROCKSDB
|
||||
class rdb_proc : public req_proc
|
||||
{
|
||||
private:
|
||||
constexpr static bool USE_DIRECT_IO_FOR_FLUSH_AND_COMPACTION = true;
|
||||
constexpr static bool USE_DIRECT_READS = true;
|
||||
constexpr static int CACHE_SIZE = 268435456;
|
||||
constexpr static int MAX_MSG_SZ = 4096;
|
||||
static constexpr const char* PARAM_PATH = "PATH";
|
||||
static std::atomic<int> db_init;
|
||||
static rocksdb::DB *db;
|
||||
public:
|
||||
rdb_proc(std::unordered_map<std::string, std::string>* args);
|
||||
rdb_proc() = delete;
|
||||
~rdb_proc();
|
||||
DISABLE_EVIL_CONSTRUCTORS(rdb_proc);
|
||||
int proc_req(int fd);
|
||||
};
|
||||
#endif
|
19
ppd_ff/options.h
Normal file
19
ppd_ff/options.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include "const.h"
|
||||
|
||||
static constexpr int MAX_MODE_PARAMS = 8;
|
||||
static constexpr int MAX_MODE_PARAMS_LEN = 64;
|
||||
struct server_options {
|
||||
int port = 9898;
|
||||
WORKLOAD_TYPE mode = WORKLOAD_TYPE::ECHO;
|
||||
int verbose = 0;
|
||||
int num_mode_params;
|
||||
char mode_params[MAX_MODE_PARAMS][MAX_MODE_PARAMS_LEN];
|
||||
std::unordered_map<std::string, std::string> parsed_mode_params;
|
||||
};
|
||||
extern server_options options;
|
315
ppd_ff/ppd.cc
Executable file
315
ppd_ff/ppd.cc
Executable file
@ -0,0 +1,315 @@
|
||||
#include <cstdlib>
|
||||
#include <strings.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <getopt.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ff_api.h>
|
||||
#include <ff_config.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "const.h"
|
||||
#include "util_ff.h"
|
||||
#include "reqproc.h"
|
||||
#include "msg.pb.h"
|
||||
#include "options.h"
|
||||
|
||||
static constexpr int NEVENT = 128;
|
||||
static constexpr int SOCK_BACKLOG = 512;
|
||||
static constexpr int DEFAULT_PORT = 9898;
|
||||
|
||||
struct alignas(CACHE_LINE_SIZE) cache_item {
|
||||
int val;
|
||||
};
|
||||
static_assert(sizeof(struct cache_item) == CACHE_LINE_SIZE, "cache_item not cache line sized");
|
||||
|
||||
struct conn_hint {
|
||||
req_proc * proc;
|
||||
};
|
||||
|
||||
struct alignas(CACHE_LINE_SIZE) loop_info {
|
||||
int kqfd;
|
||||
int srv_sock;
|
||||
};
|
||||
static_assert(sizeof(struct loop_info) == CACHE_LINE_SIZE, "loop_info not cache line sized");
|
||||
|
||||
|
||||
struct server_options options;
|
||||
|
||||
static void
|
||||
conn_hint_destroy(struct conn_hint *hint)
|
||||
{
|
||||
delete hint->proc;
|
||||
delete hint;
|
||||
}
|
||||
|
||||
static int
|
||||
server_socket_create(int port)
|
||||
{
|
||||
struct sockaddr_in server_addr;
|
||||
int status;
|
||||
const int enable = 1;
|
||||
bzero(&server_addr, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(port);
|
||||
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
int fd = ff_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (fd < 0) {
|
||||
E("server listen socket");
|
||||
}
|
||||
|
||||
// if (ff_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
|
||||
// E("server listen setsockopt reuseaddr");
|
||||
// }
|
||||
|
||||
// if (ff_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) < 0) {
|
||||
// E("server listen setsockopt reuseport");
|
||||
// }
|
||||
|
||||
if (ff_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
|
||||
E("server listen setsockopt NODELAY");
|
||||
}
|
||||
|
||||
status = ff_bind(fd, (struct linux_sockaddr*)&server_addr, sizeof(server_addr));
|
||||
if (status < 0) {
|
||||
E("server listen bind");
|
||||
}
|
||||
|
||||
status = ff_listen(fd, SOCK_BACKLOG);
|
||||
if (status < 0) {
|
||||
E("listen");
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
drop_conn(int kqfd, int connfd, struct conn_hint * hint)
|
||||
{
|
||||
int status;
|
||||
struct kevent ev;
|
||||
EV_SET(&ev, connfd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
|
||||
status = ff_kevent(kqfd, &ev, 1, 0, 0, NULL);
|
||||
|
||||
if (status < 0) {
|
||||
E("Failed to delete connection %d from kqueue\n", connfd);
|
||||
}
|
||||
|
||||
ff_close(connfd);
|
||||
|
||||
conn_hint_destroy(hint);
|
||||
}
|
||||
|
||||
static int
|
||||
loopm(void *info)
|
||||
{
|
||||
struct loop_info *linfo = (struct loop_info *)info;
|
||||
struct kevent kev[NEVENT];
|
||||
struct kevent skev[NEVENT + 1];
|
||||
std::vector<int> socks;
|
||||
int skev_sz = 0;
|
||||
int status;
|
||||
|
||||
status = ff_kevent(linfo->kqfd, NULL, 0, kev, NEVENT, NULL);
|
||||
|
||||
if (status < 0) {
|
||||
E("main_loop: kevent() failed with %d\n", errno);
|
||||
}
|
||||
|
||||
skev_sz = 0;
|
||||
for (int i = 0; i < status; i++) {
|
||||
int fd = (int)kev[i].ident;
|
||||
struct conn_hint * conn_hint = (struct conn_hint *)kev[i].udata;
|
||||
|
||||
V("Event fired on fd %d\n", fd);
|
||||
|
||||
if (kev->flags & EV_EOF) {
|
||||
if (fd != linfo->srv_sock) {
|
||||
V("main_loop: connection %d dropped due to EOF. ERR: %d\n", fd, kev->fflags);
|
||||
drop_conn(linfo->kqfd, fd, conn_hint);
|
||||
continue;
|
||||
} else {
|
||||
E("main_loop: unexpected EOF received on server socket.\n");
|
||||
}
|
||||
} else if (kev->flags & EV_ERROR) {
|
||||
W("main_loop: kevent() fd %d EV_ERROR set : 0x%lx. Dropping...\n", fd, kev[i].data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd == linfo->srv_sock) {
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_addr_size = sizeof(client_addr);
|
||||
|
||||
int client_fd = ff_accept(fd, (struct linux_sockaddr*)&client_addr, &client_addr_size);
|
||||
|
||||
if (client_fd < 0) {
|
||||
W("main_loop: ff_accept() failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// const int enable = 1;
|
||||
// if (setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
|
||||
// W("setsockopt() failed on socket %d\n", client_fd);
|
||||
// ff_close(client_fd);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
char ipaddr[INET_ADDRSTRLEN + 1];
|
||||
strncpy(ipaddr, inet_ntoa(client_addr.sin_addr), INET_ADDRSTRLEN);
|
||||
ipaddr[INET_ADDRSTRLEN] = 0;
|
||||
|
||||
V("main_loop: accepted new connection %d from %s\n", client_fd, ipaddr);
|
||||
|
||||
struct conn_hint *hint = new struct conn_hint;
|
||||
|
||||
switch (options.mode) {
|
||||
case WORKLOAD_TYPE::ECHO:
|
||||
hint->proc = new echo_proc(&options.parsed_mode_params);
|
||||
break;
|
||||
case WORKLOAD_TYPE::TOUCH:
|
||||
hint->proc = new touch_proc(&options.parsed_mode_params);
|
||||
break;
|
||||
// case WORKLOAD_TYPE::RDB:
|
||||
// hint->proc = new rdb_proc(&options.parsed_mode_params);
|
||||
// break;
|
||||
default:
|
||||
E("Unknown server mode %d", options.mode);
|
||||
}
|
||||
|
||||
EV_SET(&skev[skev_sz], client_fd, EVFILT_READ, EV_ADD, 0, 0, hint);
|
||||
skev_sz++;
|
||||
} else {
|
||||
status = conn_hint->proc->proc_req(fd);
|
||||
|
||||
if (status < 0) {
|
||||
W("Connection %d proc_req returned error %d\n", fd, status);
|
||||
drop_conn(linfo->kqfd, fd, conn_hint);
|
||||
} else {
|
||||
// EV_SET(&skev[skev_sz], fd, EVFILT_READ, EV_ENABLE, 0, 0, conn_hint);
|
||||
// skev_sz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(skev_sz <= NEVENT + 1);
|
||||
status = ff_kevent(linfo->kqfd, skev, skev_sz, NULL, 0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_options()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Configuration:" << std::endl
|
||||
<< " port: " << options.port << std::endl
|
||||
<< " mode: " << options.mode << std::endl
|
||||
<< " verbose: " << options.verbose << std::endl
|
||||
<< " mode parameters: " << std::endl;
|
||||
|
||||
for(int i = 0; i < options.num_mode_params; i++) {
|
||||
ss << " " << options.mode_params[i] << std::endl;
|
||||
}
|
||||
|
||||
V("%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stdout, "Usage:\n"
|
||||
" p: listen port\n"
|
||||
" v: verbose mode\n"
|
||||
" h: show help\n"
|
||||
" M: server mode: 0 - ECHO, 1 - TOUCH, 2 - HTTP, 3 - RDB\n"
|
||||
" O: mode specific parameters in the format \"key=value\"\n"
|
||||
" Workload specific parameters:\n"
|
||||
" TOUCH:\n"
|
||||
" ENTRIES - Number of cache-aligned entries per connection.\n\n");
|
||||
}
|
||||
|
||||
void parse_mode_params()
|
||||
{
|
||||
char * saveptr;
|
||||
|
||||
for (int i = 0; i < options.num_mode_params; i++) {
|
||||
saveptr = NULL;
|
||||
char *key = strtok_r(options.mode_params[i], "=", &saveptr);
|
||||
char *val = strtok_r(NULL, "=", &saveptr);
|
||||
|
||||
options.parsed_mode_params.insert({key, val});
|
||||
|
||||
V("Parsed workload parameter: %s = %s\n", key, val);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
// don't raise SIGPIPE when sending into broken TCP connections
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
srand(time(NULL));
|
||||
|
||||
ff_init(argc, argv);
|
||||
|
||||
char ch;
|
||||
while ((ch = getopt(argc, argv, "M:h:O:vp:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'O': {
|
||||
strncpy(options.mode_params[options.num_mode_params], optarg, MAX_MODE_PARAMS_LEN);
|
||||
options.num_mode_params++;
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
options.mode = (WORKLOAD_TYPE)strtod(optarg, nullptr);
|
||||
break;
|
||||
case 'v':
|
||||
options.verbose = 1;
|
||||
W("Verbose mode can cause SUBSTANTIAL latency fluctuations in some terminals!\n");
|
||||
break;
|
||||
case 'p':
|
||||
options.port = strtod(optarg, nullptr);
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
default:
|
||||
E("Unrecognized option -%c. See -h.\n\n", ch);
|
||||
}
|
||||
}
|
||||
|
||||
parse_mode_params();
|
||||
|
||||
dump_options();
|
||||
|
||||
V("Setting up listen sockets...\n");
|
||||
|
||||
int srv_sock = server_socket_create(options.port);
|
||||
|
||||
int kq = ff_kqueue();
|
||||
if (kq <= 0) {
|
||||
E("Cannot create kqueue\n");
|
||||
}
|
||||
struct kevent kev;
|
||||
EV_SET(&kev, srv_sock, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
|
||||
int status = ff_kevent(kq, &kev, 1, NULL, 0, NULL);
|
||||
if (status == -1) {
|
||||
E("kevent() add srv_sock failed %d\n", errno);
|
||||
}
|
||||
|
||||
V("Entering main event loop...\n");
|
||||
struct loop_info info;
|
||||
info.kqfd = kq;
|
||||
info.srv_sock = srv_sock;
|
||||
ff_run(loopm, &info);
|
||||
|
||||
// shouldn't get here
|
||||
assert(false);
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
return 0;
|
||||
}
|
305
ppd_ff/reqproc.cc
Normal file
305
ppd_ff/reqproc.cc
Normal file
@ -0,0 +1,305 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <random>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/param.h>
|
||||
#include <chrono>
|
||||
#include <unistd.h>
|
||||
|
||||
// #include <rocksdb/cache.h>
|
||||
// #include <rocksdb/status.h>
|
||||
// #include <rocksdb/cache.h>
|
||||
// #include <rocksdb/db.h>
|
||||
// #include <rocksdb/env.h>
|
||||
// #include <rocksdb/filter_policy.h>
|
||||
// #include <rocksdb/memtablerep.h>
|
||||
// #include <rocksdb/options.h>
|
||||
// #include <rocksdb/perf_context.h>
|
||||
// #include <rocksdb/persistent_cache.h>
|
||||
// #include <rocksdb/rate_limiter.h>
|
||||
// #include <rocksdb/slice.h>
|
||||
// #include <rocksdb/slice_transform.h>
|
||||
// #include <rocksdb/stats_history.h>
|
||||
// #include <rocksdb/utilities/object_registry.h>
|
||||
// #include <rocksdb/utilities/optimistic_transaction_db.h>
|
||||
// #include <rocksdb/utilities/options_util.h>
|
||||
// #include <rocksdb/utilities/sim_cache.h>
|
||||
// #include <rocksdb/utilities/transaction.h>
|
||||
// #include <rocksdb/utilities/transaction_db.h>
|
||||
// #include <rocksdb/write_batch.h>
|
||||
// #include <rocksdb/table.h>
|
||||
|
||||
#include "msg.pb.h"
|
||||
#include "util_ff.h"
|
||||
#include "const.h"
|
||||
#include "reqproc.h"
|
||||
#include "options.h"
|
||||
|
||||
////////////////
|
||||
// TOUCH Generator
|
||||
////////////////
|
||||
touch_proc::touch_proc(std::unordered_map<std::string, std::string>* args) : req_proc()
|
||||
{
|
||||
if (args->find(PARAM_TBSZ) != args->end()) {
|
||||
this->buffer_sz = atoi(args->at(PARAM_TBSZ).c_str());
|
||||
} else {
|
||||
this->buffer_sz = PARAM_TBSZ_DEFAULT;
|
||||
}
|
||||
|
||||
V("Allocating %d items x %d CASZ for connnection\n", this->buffer_sz, CACHE_LINE_SIZE);
|
||||
this->buffer = (struct ppd_touch_cache_item *)aligned_alloc(CACHE_LINE_SIZE, this->buffer_sz * sizeof(struct ppd_touch_cache_item));
|
||||
|
||||
this->rgen = new std::default_random_engine();
|
||||
this->rdist = new std::uniform_int_distribution<int>(0, INT32_MAX);
|
||||
}
|
||||
|
||||
touch_proc::~touch_proc()
|
||||
{
|
||||
delete this->buffer;
|
||||
delete this->rgen;
|
||||
delete this->rdist;
|
||||
}
|
||||
|
||||
int touch_proc::proc_req(int fd)
|
||||
{
|
||||
ppd_touch_req req;
|
||||
ppd_touch_resp resp;
|
||||
struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
|
||||
int ret = readmsg(fd, this->read_buf, this->MAX_READ_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
W("Readmsg failed with %d for connection %d\n", ret, fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
W("ParseFromArray failed for connection %d\n", fd);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
V("Conn %d touching %d items...\n", fd, req.touch_cnt());
|
||||
|
||||
int sum = 0;
|
||||
int rand = (*this->rdist)(*this->rgen);
|
||||
for(int64_t i = rand; i < rand + req.touch_cnt(); i++) {
|
||||
if (req.inc() > 0) {
|
||||
this->buffer[i % this->buffer_sz].val += 1;
|
||||
} else {
|
||||
/* only read */
|
||||
sum += this->buffer[i % this->buffer_sz].val;
|
||||
}
|
||||
}
|
||||
sum = sum + 1;
|
||||
|
||||
resp.set_status(0);
|
||||
if (!resp.SerializeToArray(this->read_buf, MAX_READ_BUF_SIZE)) {
|
||||
W("Couldn't searialize to array connection %d\n", fd);
|
||||
}
|
||||
|
||||
ret = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
if (ret < 0) {
|
||||
W("Writemsg failed with %d for connection %d\n", ret, fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// ECHO Generator
|
||||
////////////////
|
||||
|
||||
echo_proc::echo_proc(std::unordered_map<std::string, std::string>*) : req_proc()
|
||||
{
|
||||
}
|
||||
|
||||
echo_proc::~echo_proc()
|
||||
{
|
||||
}
|
||||
|
||||
int echo_proc::proc_req(int fd)
|
||||
{
|
||||
uint64_t ms1, ms2;
|
||||
struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
int ret = readmsg(fd, this->read_buf, MAX_READ_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
W("Readbuf failed with %d for connection %d\n", ret, fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ms1 = get_time_us();
|
||||
if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
W("ParseFromArray failed for connection %d\n", fd);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
V("Connection %d delay %d us \n", fd, req.enable_delay());
|
||||
|
||||
if (req.enable_delay() > 0) {
|
||||
uint64_t server_delay = req.enable_delay();
|
||||
uint64_t now = get_time_us();
|
||||
while (get_time_us() - now <= server_delay) {};
|
||||
}
|
||||
|
||||
resp.set_status(0);
|
||||
if (!resp.SerializeToArray(this->read_buf, MAX_READ_BUF_SIZE)) {
|
||||
W("Couldn't searialize to array connection %d\n", fd);
|
||||
}
|
||||
ms2 = get_time_us();
|
||||
|
||||
V("Serialization: TIME: %ld\n", ms2 - ms1);
|
||||
|
||||
ret = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
if (ret < 0) {
|
||||
W("Writemsg failed with %d for connection %d\n", ret, fd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// ////////////////
|
||||
// // rdb Generator
|
||||
// ////////////////
|
||||
|
||||
// rocksdb::DB * rdb_proc::db = nullptr;
|
||||
// std::atomic<int> rdb_proc::db_init {0};
|
||||
|
||||
// rdb_proc::rdb_proc(std::unordered_map<std::string, std::string>* args) : req_proc()
|
||||
// {
|
||||
// const char * db_path;
|
||||
// int desired = 0;
|
||||
// int target = 1;
|
||||
// if (std::atomic_compare_exchange_strong(&rdb_proc::db_init, &desired, target)) {
|
||||
// if (args->find(PARAM_PATH) != args->end()) {
|
||||
// db_path = args->at(PARAM_PATH).c_str();
|
||||
// } else {
|
||||
// E("Must specify -OPATH for rocksdb.\n");
|
||||
// }
|
||||
|
||||
// V("Initializing rocksdb, path: %s.\n", db_path);
|
||||
|
||||
// rocksdb::Options opt;
|
||||
// std::shared_ptr<rocksdb::Cache> cache = rocksdb::NewLRUCache(CACHE_SIZE, 6, false, 0.0);
|
||||
// opt.use_direct_io_for_flush_and_compaction = USE_DIRECT_IO_FOR_FLUSH_AND_COMPACTION;
|
||||
// opt.use_direct_reads = USE_DIRECT_READS;
|
||||
|
||||
// rocksdb::BlockBasedTableOptions block_based_options;
|
||||
// block_based_options.index_type = rocksdb::BlockBasedTableOptions::kBinarySearch;
|
||||
// block_based_options.block_cache = cache;
|
||||
// opt.table_factory.reset(rocksdb::NewBlockBasedTableFactory(block_based_options));
|
||||
|
||||
// opt.IncreaseParallelism(12);
|
||||
// opt.OptimizeLevelStyleCompaction(1024 * 1024 * 1024);
|
||||
// opt.OptimizeUniversalStyleCompaction(1024 * 1024 * 1024);
|
||||
// opt.write_buffer_size = 1024 * 1024 * 1024;
|
||||
// opt.create_if_missing = false;
|
||||
// opt.compression = rocksdb::kNoCompression;
|
||||
|
||||
|
||||
// rocksdb::Status s = rocksdb::DB::Open(opt, std::string(db_path), &this->db);
|
||||
// if (!s.ok()) {
|
||||
// E("Could not open rocksdb! Err %s\n", s.ToString().c_str());
|
||||
// }
|
||||
|
||||
// rdb_proc::db_init.store(2);
|
||||
// V("Finished initializing rocksdb.\n");
|
||||
// } else {
|
||||
// V("Checking for rocksdb initialization...\n");
|
||||
// while(rdb_proc::db_init.load() != 2) {};
|
||||
// V("Detected initialized rocksdb.\n");
|
||||
// }
|
||||
// }
|
||||
|
||||
// rdb_proc::~rdb_proc()
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// int rdb_proc::proc_req(int fd)
|
||||
// {
|
||||
// ppd_rdb_resp resp;
|
||||
// ppd_rdb_req req;
|
||||
// rocksdb::Status s;
|
||||
// struct ppd_msg *msg = (struct ppd_msg *)this->read_buf;
|
||||
|
||||
// int i = 0;
|
||||
// int status = readmsg(fd, this->read_buf, MAX_READ_BUF_SIZE);
|
||||
|
||||
// if (status != 0) {
|
||||
// W("Readmsg failed with %d for connection %d\n", status, fd);
|
||||
// return status;
|
||||
// }
|
||||
|
||||
// if (!req.ParseFromArray(msg->payload, msg->size)) {
|
||||
// W("ParseFromArray failed for connection %d\n", fd);
|
||||
// return EINVAL;
|
||||
// }
|
||||
|
||||
// V("Connection %d op: %d, key: %s. val: %s. optarg: %d.\n", fd, req.op(), req.key().c_str(), req.val().c_str(), req.optarg());
|
||||
|
||||
// switch (req.op()) {
|
||||
// case PPD_RDB_OP_PUT:{
|
||||
// s = this->db->Put(rocksdb::WriteOptions(), req.key(), req.val());
|
||||
// resp.set_status(s.code());
|
||||
// break;
|
||||
// }
|
||||
// case PPD_RDB_OP_GET: {
|
||||
// std::string val;
|
||||
// s = this->db->Get(rocksdb::ReadOptions(), req.key(), &val);
|
||||
// if (s.ok()) {
|
||||
// resp.set_result(val);
|
||||
// }
|
||||
// resp.set_status(s.code());
|
||||
// break;
|
||||
// }
|
||||
// case PPD_RDB_OP_SEEK: {
|
||||
// rocksdb::Slice val;
|
||||
// rocksdb::Iterator *it = this->db->NewIterator(rocksdb::ReadOptions(false, true));
|
||||
|
||||
// it->Seek(req.key());
|
||||
// resp.set_status(it->status().code());
|
||||
|
||||
// if (it->Valid()) {
|
||||
// val = it->value();
|
||||
// resp.set_result(val.data(), val.size());
|
||||
// }
|
||||
|
||||
// for(int64_t j = 0; j < req.optarg() && it->Valid(); j++) {
|
||||
// rocksdb::Slice val = it->value();
|
||||
// // do something about the key
|
||||
// std::memcpy(this->read_buf, val.data(), MIN(val.size(), MAX_READ_BUF_SIZE));
|
||||
// it->Next();
|
||||
// if (!it->status().ok()) {
|
||||
// resp.set_status(it->status().code());
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// delete it;
|
||||
// break;
|
||||
// }
|
||||
// default: {
|
||||
// W("Invalid opcode %d for connection %d\n", req.op(), fd);
|
||||
// return EINVAL;
|
||||
// }
|
||||
// }
|
||||
|
||||
// resp.set_status(0);
|
||||
|
||||
// status = writemsg(fd, this->read_buf, MAX_READ_BUF_SIZE, this->read_buf, resp.ByteSizeLong());
|
||||
// if (status < 0) {
|
||||
// W("Writemsg failed with %d for connection %d\n", status, fd);
|
||||
// }
|
||||
|
||||
|
||||
// return status;
|
||||
// }
|
78
ppd_ff/reqproc.h
Normal file
78
ppd_ff/reqproc.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <const.h>
|
||||
#include <atomic>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <msg.pb.h>
|
||||
|
||||
#define DISABLE_EVIL_CONSTRUCTORS(name) \
|
||||
name(const name&) = delete; \
|
||||
void operator=(const name) = delete
|
||||
|
||||
struct alignas(CACHE_LINE_SIZE) ppd_touch_cache_item {
|
||||
int val;
|
||||
};
|
||||
|
||||
class req_proc {
|
||||
protected:
|
||||
constexpr static int MAX_READ_BUF_SIZE = 1024 * 1024;
|
||||
char * read_buf;
|
||||
public:
|
||||
req_proc() {this->read_buf = new char[MAX_READ_BUF_SIZE]; };
|
||||
virtual ~req_proc() {delete[] this->read_buf;};
|
||||
virtual int proc_req(int fd) = 0;
|
||||
};
|
||||
|
||||
class touch_proc : public req_proc
|
||||
{
|
||||
private:
|
||||
int buffer_sz;
|
||||
std::default_random_engine * rgen;
|
||||
std::uniform_int_distribution<int> * rdist;
|
||||
struct ppd_touch_cache_item* buffer;
|
||||
static constexpr const char* PARAM_TBSZ = "ENTRIES";
|
||||
static constexpr const int PARAM_TBSZ_DEFAULT = 64;
|
||||
constexpr static int MAX_SZ = 1024 * 1024;
|
||||
public:
|
||||
touch_proc(std::unordered_map<std::string, std::string>* args);
|
||||
touch_proc() = delete;
|
||||
~touch_proc();
|
||||
DISABLE_EVIL_CONSTRUCTORS(touch_proc);
|
||||
int proc_req(int fd);
|
||||
};
|
||||
|
||||
class echo_proc : public req_proc
|
||||
{
|
||||
private:
|
||||
ppd_echo_req req;
|
||||
ppd_echo_resp resp;
|
||||
public:
|
||||
echo_proc(std::unordered_map<std::string, std::string>* args);
|
||||
echo_proc() = delete;
|
||||
~echo_proc();
|
||||
DISABLE_EVIL_CONSTRUCTORS(echo_proc);
|
||||
int proc_req(int fd);
|
||||
};
|
||||
|
||||
// class rdb_proc : public req_proc
|
||||
// {
|
||||
// private:
|
||||
// constexpr static bool USE_DIRECT_IO_FOR_FLUSH_AND_COMPACTION = true;
|
||||
// constexpr static bool USE_DIRECT_READS = true;
|
||||
// constexpr static int CACHE_SIZE = 268435456;
|
||||
// constexpr static int MAX_MSG_SZ = 4096;
|
||||
// static constexpr const char* PARAM_PATH = "PATH";
|
||||
// static std::atomic<int> db_init;
|
||||
// static rocksdb::DB *db;
|
||||
// public:
|
||||
// rdb_proc(std::unordered_map<std::string, std::string>* args);
|
||||
// rdb_proc() = delete;
|
||||
// ~rdb_proc();
|
||||
// DISABLE_EVIL_CONSTRUCTORS(rdb_proc);
|
||||
// int proc_req(int fd);
|
||||
// };
|
Loading…
Reference in New Issue
Block a user