TT#3670 Proof of concept and WIP for HS client

Change-Id: I419deec561ff4c107cedaf0c7ff1e86bad04ce0a
changes/84/14384/1
Andreas Granig 9 years ago
parent c3a6a631ef
commit 3ac0a074ab

@ -0,0 +1,37 @@
export LIBNAME=libhsclient-c-wrapper
export VERSION=0
export LIBSO=$(LIBNAME).so
export LIBSOVER=$(LIBNAME).so.$(VERSION)
LIBDIR=lib
HDR=include/hsclient-c-wrapper
DESTDIR?=/usr/local
all:
$(MAKE) -C src
$(MAKE) -C tests
clean:
$(MAKE) -C src clean
$(MAKE) -C tests clean
rm -rf project.tgz cov-int
install: all
mkdir -p $(DESTDIR)/$(HDR)
cp include/*.h $(DESTDIR)/$(HDR)
mkdir -p $(DESTDIR)/$(LIBDIR)
cp src/$(LIBSOVER) $(DESTDIR)/$(LIBDIR)/$(LIBSOVER)
ln -sf $(LIBSOVER) $(DESTDIR)/$(LIBDIR)/$(LIBSO)
coverity:
cov-build --dir cov-int $(MAKE)
tar -czf project.tgz cov-int
curl --form token=$(COVERITY_TOKEN) \
--form email=$(DEBEMAIL) \
--form file=@project.tgz \
--form version="$(COVERITY_VERSION)" \
--form description="automatic upload" \
https://scan.coverity.com/builds?project=$(COVERITY_PROJECT)
.PHONY: all clean install coverity

5
debian/changelog vendored

@ -0,0 +1,5 @@
libhsclient-c-wrapper (5.3.0.0+0~mr5.3.0.0) unstable; urgency=medium
* Initial revision
-- Andreas Granig <agranig@sipwise.com> Wed, 12 Apr 2017 21:09:23 +0200

1
debian/compat vendored

@ -0,0 +1 @@
9

26
debian/control vendored

@ -0,0 +1,26 @@
Source: libhsclient-c-wrapper
Section: admin
Priority: extra
Maintainer: Sipwise Development Team <support@sipwise.com>
Build-Depends: debhelper (>= 9)
Standards-Version: 3.9.7
Homepage: http://sipwise.com/
Package: libhsclient-c-wrapper0
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, libhsclient-dev
Description: library for handlersocket handling in C programs
This library provides a high-level interface for C programs
to interact with the HandlerSocket interface in MySQL flavor
databases. It uses the C++ libhsclient library under the hood.
Package: libhsclient-c-wrapper-dev
Architecture: any
Section: libdevel
Depends: libhsclient-c-wrapper0 (= ${binary:Version}), ${misc:Depends}
Description: Headers for libhsclient-c-wrapper0
This library provides a high-level interface for C programs
to interact with the HandlerSocket interface in MySQL flavor
databases. It uses the C++ libhsclient library under the hood.
.
This package provides the header files.

23
debian/copyright vendored

@ -0,0 +1,23 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https://www.sipwise.com/
Upstream-Contact: Sipwise Development Team <support@sipwise.com>
Files: *
Copyright:
Copyright © 2013-2016 Sipwise GmbH, Austria
License: GPL-3+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Comment:
On Debian systems, the full text of the GNU General Public License
version 3 can be found in the file '/usr/share/common-licenses/GPL-3'.

@ -0,0 +1,2 @@
debian/tmp/include/* /usr/include/
debian/tmp/lib/*.so /usr/lib/

@ -0,0 +1 @@
debian/tmp/lib/*.so.* /usr/lib/

@ -0,0 +1 @@
activate-noawait ldconfig

6
debian/rules vendored

@ -0,0 +1,6 @@
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
# export DH_VERBOSE=1
%:
dh $@

@ -0,0 +1 @@
3.0 (native)

@ -0,0 +1,88 @@
#ifndef _HSCLIENT_C_WRAPPER_H
#define _HSCLIENT_C_WRAPPER_H
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef char* hs_element;
typedef hs_element* hs_row;
typedef hs_row* hs_table;
typedef struct hs_result_s {
hs_table table;
size_t cols, rows;
size_t row_iter;
char error[1024];
int success;
} hs_result;
typedef hs_result* hs_res;
typedef struct hs_filterkey_s {
char *name;
char *op;
char *val;
} hs_filterkey;
typedef hs_filterkey hs_key;
typedef enum hs_value_type_e {
HS_NULL,
HS_INT,
HS_DOUBLE,
HS_STRING,
} hs_valtype;
typedef struct hs_value_s {
char *name;
union {
int64_t val_int;
char *val_string;
double val_double;
};
hs_valtype type;
} hs_value;
typedef hs_value hs_val;
typedef struct hs_connection_s {
uint32_t index;
} hs_connection;
typedef hs_connection* hs_con;
size_t hs_num_rows(hs_res res);
size_t hs_num_cols(hs_res res);
hs_row hs_fetch_row(hs_res res);
void hs_free_result(hs_res res);
int hs_success(hs_res res);
char* hs_get_error(hs_res res);
size_t hs_max();
hs_res hs_init_connection(hs_con *con,
const char *host, uint16_t port,
const char *dbname, const char *table,
const char *index, const char *indexcols,
const char *secret);
hs_res hs_prepare_query(hs_con con, hs_key** keys);
hs_res hs_connect(hs_con *con, char **cfg, size_t num_cfg);
void hs_close(hs_con con);
hs_res hs_select(hs_con con, char **keys, size_t num_keys, size_t limit, size_t offset);
hs_res hs_insert(hs_con con, char **keys, size_t num_keys);
hs_res hs_filtered_update(hs_con con, char **keys, size_t num_keys, char **filters, size_t num_filters, char **vals, size_t num_vals);
hs_res hs_update(hs_con con, char **keys, size_t num_keys, char **vals, size_t num_vals);
hs_res hs_filtered_delete(hs_con con, char **keys, size_t num_keys, char **filters, size_t num_filters);
hs_res hs_delete(hs_con con, char **keys, size_t num_keys);
#ifdef __cplusplus
}
#endif
#endif // _HSCLIENT_C_WRAPPER_H

@ -0,0 +1,26 @@
CC=g++
CXXFLAGS+=-I../include -g -Wall -O3 -fPIC -std=c++11
CXXFLAGS+=-D_GLIBCXX_USE_CXX11_ABI=0
LDFLAGS=-lhsclient -O3 -shared -fPIC -Wl,-soname,$(LIBNAME).so.$(VERSION)
CPPFILES=$(wildcard *.cpp)
OFILES=$(CPPFILES:.cpp=.o)
.PHONY: all clean
all: $(LIBSO)
$(LIBSO): $(LIBSOVER)
ln -fs $(LIBSOVER) $(LIBSO)
$(LIBSOVER): $(OFILES)
$(CC) -o $@ $(OFILES) $(LDFLAGS)
%.o: %.cpp
$(CC) $(CXXFLAGS) -c -o $@ $<
clean:
rm -f $(OFILES)
rm -f $(LIBSO)
rm -f $(LIBSOVER)

@ -0,0 +1,235 @@
#include <stdexcept>
#include <memory>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <handlersocket/hstcpcli.hpp>
#include <handlersocket/string_util.hpp>
#include "hsclient-c-wrapper.h"
typedef std::shared_ptr<dena::hstcpcli_i> hstcpcli_sptr;
typedef std::vector<std::string> hs_indexlist;
std::vector<hs_indexlist> g_indexlist;
struct hs_handle_s {
dena::config config;
hs_indexlist indexcols;
hstcpcli_sptr cli;
};
typedef struct hs_handle_s hs_handle;
typedef std::shared_ptr<hs_handle> hs_handle_sptr;
std::vector<hs_handle_sptr> g_handles;
extern hs_result *hs_create_result();
extern void hs_set_error(hs_result *res, const char* format, ...);
hstcpcli_sptr hs_get_cli(hs_connection *con) {
hs_handle_sptr handle;
try {
handle = g_handles.at(con->index);
} catch(const std::out_of_range &e) {
//hs_set_error(r, "invalid connection index %lu", con->index);
//return r;
return NULL;
}
if(!handle) {
//hs_set_error(r, "invalid connection index %lu", con->index);
//return r;
return NULL;
}
return handle->cli;
}
static hs_connection *hs_create_connection() {
hs_connection *con = (hs_connection*)malloc(sizeof(hs_connection));
if(!con) {
return NULL;
}
con->index= 0;
return con;
}
std::vector<std::string> hs_splitstring(const char *str, char c = ',')
{
std::vector<std::string> res;
do
{
const char *begin = str;
while(*str != c && *str) {
str++;
}
res.push_back(std::string(begin, str));
} while (0 != *str++);
return res;
}
hs_result* hs_init_connection(hs_connection **con,
const char *host, uint16_t port,
const char *dbname, const char* table,
const char *index, const char* indexcols,
const char *secret) {
dena::config conf;
dena::socket_args sockargs;
hs_result *r = hs_create_result();
if(!r) {
return NULL;
}
*con = hs_create_connection();
if(!*con) {
hs_set_error(r, "failed to create connection: %s", strerror(errno));
return r;
}
conf["host"] = std::string(host);
conf["port"] = std::to_string(port);
conf["dbname"] = std::string(dbname);
conf["table"] = std::string(table);
conf["index"] = std::string(index);
conf["indexcols"] = std::string(indexcols);
sockargs.set(conf);
hs_indexlist index_list = hs_splitstring(indexcols, ',');
hstcpcli_sptr cli = dena::hstcpcli_i::create(sockargs);
hs_handle_sptr handle = hs_handle_sptr(new hs_handle);
handle->cli = cli;
handle->indexcols = index_list;
handle->config = conf;
g_handles.push_back(handle);
(*con)->index = g_handles.size()-1;
if(secret != NULL) {
int code = 0;
size_t numflds = 0;
cli->request_buf_auth(secret, "1");
do {
if(cli->request_send() != 0) {
hs_set_error(r, "failed to send auth request: %s", cli->get_error().c_str());
hs_close(*con);
break;
}
if((code = cli->response_recv(numflds)) != 0) {
hs_set_error(r, "failed to receive auth response: %s", cli->get_error().c_str());
hs_close(*con);
}
cli->response_buf_remove();
} while(false);
}
return r;
}
hs_result* hs_prepare_query(hs_connection *con, hs_filterkey** keys) {
printf("preparing result\n");
hs_result *r = hs_create_result();
if(!r) {
return NULL;
}
/*
1. if all keys->name and keys->op are found in sets, use con as is
2. else find keys->name in index and prepare index list,
prepare remaining names as filter list,
open index, store lists in sets
*/
hs_handle_sptr handle;
hs_indexlist index_list;
hstcpcli_sptr cli;
try {
handle = g_handles.at(con->index);
} catch(const std::out_of_range &e) {
hs_set_error(r, "invalid connection index %lu", con->index);
return r;
}
if(!handle) {
hs_set_error(r, "invalid connection index %lu", con->index);
return r;
}
index_list = handle->indexcols;
cli = handle->cli;
std::vector<std::string> filter_list;
for(hs_filterkey **k = keys; *k != NULL; ++k) {
size_t pos;
char *name = (*k)->name;
printf("checking: %s %s %s\n", name, (*k)->op, (*k)->val);
auto it = std::find(index_list.begin(), index_list.end(), std::string(name));
if(it != index_list.end()) {
pos = std::distance(index_list.begin(), it);
printf("found key %s at position %lu in index list\n", name, pos);
// TODO: add val in right index slot
} else {
printf("key %s not in index list, search filter list\n", name);
auto it = std::find(filter_list.begin(), filter_list.end(), std::string(name));
if(it != filter_list.end()) {
pos = std::distance(filter_list.begin(), it);
printf("found key %s at position %lu in filter list\n", name, pos);
} else {
filter_list.push_back(std::string(name));
pos = filter_list.size()-1;
printf("added key %s at position %lu in filter list\n", name, pos);
}
// TODO: add val in right filter slot
}
}
std::string index;
for(auto it = index_list.begin(); it != index_list.end(); it++) {
if(it != index_list.begin()) {
index.append(",");
}
index.append(*it);
}
std::string filter;
for(auto it = filter_list.begin(); it != filter_list.end(); it++) {
if(it != filter_list.begin()) {
filter.append(",");
}
filter.append(*it);
}
printf("index=%s\n", index.c_str()); // that's not actually needed, just make sure we have all the vals
printf("filter=%s\n", filter.c_str());
// TODO: add field list
std::string fields("strvalue,intvalue");
do {
int code;
size_t numflds;
cli->request_buf_open_index(con->index, handle->config["dbname"].c_str(), handle->config["table"].c_str(),
handle->config["index"].c_str(), fields.c_str(), filter.c_str());
if(cli->request_send() != 0) {
hs_set_error(r, "failed to send opening request: %s", cli->get_error().c_str());
hs_close(con);
break;
}
if((code = cli->response_recv(numflds)) != 0) {
hs_set_error(r, "failed to receive opening response: %s", cli->get_error().c_str());
cli->response_buf_remove();
hs_close(con);
break;
}
cli->response_buf_remove();
} while(false);
return r;
}
void hs_close(hs_connection *con) {
if(!con) {
return;
}
g_handles.at(con->index) = NULL;
// TODO: also clear g_indexlist.at(con->index) ?
free(con);
}

@ -0,0 +1,134 @@
#include <stdexcept>
#include <memory>
#include <unordered_map>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <handlersocket/hstcpcli.hpp>
#include <handlersocket/string_util.hpp>
#include "hsclient-c-wrapper.h"
typedef struct dena::hstcpcli_filter hs_filter;
typedef std::shared_ptr<dena::hstcpcli_i> hstcpcli_sptr;
extern hstcpcli_sptr hs_get_cli(hs_connection *con);
extern hs_result *hs_create_result();
extern void hs_set_error(hs_result *res, const char* format, ...);
extern int hs_set_element(hs_result *res, size_t row, size_t col, const char *val);
extern int hs_add_row(hs_result *res);
static hs_result* hs_query_full(hs_connection *con, char **keys, size_t num_keys, size_t limit, size_t offset, const dena::string_ref op_ref, char **vals, size_t num_vals, const dena::string_ref mod_ref, char **filters, size_t num_filters) {
std::vector<dena::string_ref> keyrefs;
std::vector<dena::string_ref> valrefs;
std::vector<hs_filter> filterrefs;
int code = 0;
size_t numflds = 0;
hs_result *r = hs_create_result();
if(!r) {
return NULL;
}
hstcpcli_sptr cli = hs_get_cli(con);
if(!cli) {
hs_set_error(r, "invalid connection index %lu", con->index);
return r;
}
for(size_t i = 0; i < num_keys; ++i) {
const dena::string_ref ref(keys[i], strlen(keys[i]));
keyrefs.push_back(ref);
}
for(size_t i = 0; i < num_vals; ++i) {
if(vals[i] == NULL) {
valrefs.push_back(dena::string_ref(0, 1));
} else {
valrefs.push_back(dena::string_ref(vals[i], strlen(vals[i])));
}
}
for(size_t i = 0; i < num_filters; ++i) {
hs_filter ref;
ref.filter_type = dena::string_ref("F", 1); // F for filter, W for while (stop on first mismatch)
ref.op = dena::string_ref("=", 1);
ref.val = dena::string_ref(filters[i], strlen(filters[i]));
filterrefs.push_back(ref);
}
cli->request_buf_exec_generic(con->index,
op_ref, num_keys == 0 ? 0 : &keyrefs[0], num_keys,
limit, offset,
mod_ref, num_vals == 0 ? 0 : &valrefs[0], num_vals,
num_filters == 0 ? 0 : &filterrefs[0], num_filters);
if(cli->request_send() != 0) {
hs_set_error(r, "failed to send hs request: %s", cli->get_error().c_str());
return r;
}
do {
if((code = cli->response_recv(numflds)) != 0) {
hs_set_error(r, "failed to receive hs response: %s", cli->get_error().c_str());
break;
}
r->cols = numflds;
int rowcnt = 0;
while(true) {
const dena::string_ref *const row = cli->get_next_row();
if(row == 0) {
break;
}
if(hs_add_row(r) < 0) {
break;
}
for(size_t i = 0; i < numflds; ++i) {
const std::string val(row[i].begin(), row[i].size());
if(hs_set_element(r, rowcnt, i, val.c_str()) < 0) {
break;
}
}
rowcnt++;
}
} while(false);
cli->response_buf_remove();
return r;
}
static hs_result* hs_query(hs_connection *con, char **keys, size_t num_keys, size_t limit, size_t offset, const dena::string_ref op_ref) {
return hs_query_full(con, keys, num_keys, limit, offset, op_ref, NULL, 0, dena::string_ref(), NULL, 0);
}
hs_result* hs_select(hs_connection *con, char **keys, size_t num_keys, size_t limit, size_t offset) {
const dena::string_ref op_ref("=", 1);
return hs_query(con, keys, num_keys, limit, offset, op_ref);
}
hs_result* hs_insert(hs_connection *con, char **keys, size_t num_keys) {
const dena::string_ref op_ref("+", 1);
return hs_query(con, keys, num_keys, 0, 0, op_ref);
}
hs_result* hs_filtered_update(hs_connection *con, char **keys, size_t num_keys, char **filters, size_t num_filters, char **vals, size_t num_vals) {
const dena::string_ref op_ref("=", 1);
const dena::string_ref mod_ref("U", 1);
return hs_query_full(con, keys, num_keys, hs_max(), 0, op_ref, vals, num_vals, mod_ref, filters, num_filters);
}
hs_result* hs_delete(hs_connection *con, char **keys, size_t num_keys) {
return hs_filtered_delete(con, keys, num_keys, NULL, 0);
}
hs_result* hs_filtered_delete(hs_connection *con, char **keys, size_t num_keys, char **filters, size_t num_filters) {
const dena::string_ref op_ref("=", 1);
const dena::string_ref mod_ref("D", 1);
return hs_query_full(con, keys, num_keys, hs_max(), 0, op_ref, NULL, 0, mod_ref, filters, num_filters);
}
hs_result* hs_update(hs_connection *con, char **keys, size_t num_keys, char **vals, size_t num_vals) {
return hs_filtered_update(con, keys, num_keys, NULL, 0, vals, num_vals);
}

@ -0,0 +1,128 @@
#include <stdexcept>
#include <memory>
#include <unordered_map>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <handlersocket/hstcpcli.hpp>
#include <handlersocket/string_util.hpp>
#include "hsclient-c-wrapper.h"
size_t hs_max() {
return UINT_MAX;
}
hs_result *hs_create_result() {
hs_result *res = (hs_result*)malloc(sizeof(hs_result));
if(!res) {
fprintf(stderr, "failed to allocate result: %s\n", strerror(errno));
return NULL;
}
snprintf(res->error, sizeof(res->error), "success");
res->success = 1;
res->rows = 0;
res->cols = 0;
res->row_iter = 0;
res->table = NULL;
return res;
}
void hs_set_error(hs_result *res, const char* format, ...) {
va_list args;
va_start(args, format);
snprintf(res->error, sizeof(res->error), format, args);
va_end(args);
res->success = 0;
}
int hs_add_row(hs_result *res) {
hs_row row = (hs_row)malloc(res->cols * sizeof(hs_element));
if(!row) {
hs_set_error(res, "failed to allocate row memory: %s", strerror(errno));
return -1;
}
memset(row, 0, res->cols * sizeof(hs_element));
if(!res->rows) {
res->table = (hs_table)malloc(sizeof(hs_row));
if(!res->table) {
hs_set_error(res, "failed to allocate table memory: %s", strerror(errno));
free(row);
return -1;
}
} else {
res->table = (hs_table)realloc(res->table, (res->rows+1) * sizeof(hs_row));
if(!res->table) {
hs_set_error(res, "failed to increase table memory: %s", strerror(errno));
free(row);
return -1;
}
}
res->table[res->rows++] = row;
return 0;
}
int hs_set_element(hs_result *res, size_t row, size_t col, const char *val) {
if(row > res->rows) {
hs_set_error(res, "failed row boundary check while setting element");
return -1;
}
if(col > res->cols) {
hs_set_error(res, "failed col boundary check while setting element");
return -1;
}
res->table[row][col] = strdup(val);
return 0;
}
size_t hs_num_rows(hs_result *res) {
return res->rows;
}
size_t hs_num_cols(hs_result *res) {
return res->cols;
}
hs_row hs_fetch_row(hs_result *res) {
if(res->row_iter == res->rows) {
return NULL;
}
return res->table[res->row_iter++];
}
void hs_free_result(hs_result *res) {
if(!res || !res->table) {
return;
}
for(size_t row = 0; row < res->rows; ++row) {
if(!res->table[row]) {
continue;
}
for(size_t col = 0; col < res->cols; ++col) {
if(res->table[row][col]) {
free(res->table[row][col]);
}
}
free(res->table[row]);
}
free(res->table);
free(res);
}
int hs_success(hs_result *res) {
if(res) {
return res->success;
} else {
return 0;
}
}
char* hs_get_error(hs_result *res) {
if(!res) {
return (char*) "invalid result handle";
} else {
return res->error;
}
}

@ -0,0 +1,10 @@
CFILES=$(wildcard *.c)
BINFILES=$(CFILES:.c=.bin)
all: $(BINFILES)
%.bin: %.c
gcc -o $@ $< -Wall -I../include -L../src -lhsclient-c-wrapper
clean:
rm -f *.bin

@ -0,0 +1,78 @@
#!/usr/bin/perl -w
use strict;
# apt-get install libnet-handlersocket-perl
use Net::HandlerSocket;
use DBI;
use Time::HiRes qw/gettimeofday tv_interval/;
use Data::Dumper;
my $runs = 10000;
my $acc_cols = "method,from_tag,to_tag,callid,sip_code,sip_reason,time,time_hires,src_leg,dst_leg,dst_user,dst_ouser,dst_domain,src_user,src_domain";
my @acc = (
'INVITE',
'test_from_tag',
'test_to_tag',
'test_call_id',
'200',
'OK',
'2017-04-12 12:00:00',
Time::HiRes::time,
'test_src_leg',
'test_dst_leg',
'test_dst_user',
'test_dst_ouser',
'test_dst_domain',
'test_src_user',
'test_src_domain',
);
my %prefs;
my $start;
my $duration;
###### Handlersocket version goes here: ######################
my $idx = 1;
my $hs = Net::HandlerSocket->new({
host => '127.0.0.1',
port => 9997,
}) or die "Failed to connect to HS port\n";
if($hs->open_index($idx, 'kamailio', 'acc', 'PRIMARY', $acc_cols)) {
die "Failed to open index on kamailio.acc: " . $hs->get_error() . "\n";
}
$start = [gettimeofday];
for(my $i = 0; $i < $runs; ++$i) {
my $res = $hs->execute_single($idx, '+', \@acc, -1, 0);
my $status = shift @{ $res };
if($status != 0) {
die "Failed to execute query: " . $hs->get_error() . "\n";
}
}
$duration = tv_interval($start, [gettimeofday]);
print "--------- handlersocket results --------------\n";
print "Duration: $duration sec for $runs interations\n";
print "Req/sec: " . ($runs/$duration) . "\n";
print "Dur/req: " . ($duration/$runs) . " sec\n";
###### SQL version goes here: ######################
my $dsn = "DBI:mysql:database=kamailio;host=localhost;port=3306";
my $dbh = DBI->connect($dsn, "kamailio", "XHNLjd3L4fWtdwE3ta7L");
my $valtmpl = join ",", (("?") x @acc);
my $q = "insert into acc($acc_cols) values($valtmpl)";
my $sth = $dbh->prepare($q);
for(my $i = 0; $i < $runs; ++$i) {
%prefs = ();
$sth->execute(@acc);
}
$duration = tv_interval($start, [gettimeofday]);
print "--------- sql results --------------\n";
print "Duration: $duration sec for $runs interations\n";
print "Req/sec: " . ($runs/$duration) . "\n";
print "Dur/req: " . ($duration/$runs) . " sec\n";

@ -0,0 +1,111 @@
#!/usr/bin/perl -w
use strict;
# apt-get install libnet-handlersocket-perl
use Net::HandlerSocket;
use DBI;
use Time::HiRes qw/gettimeofday tv_interval/;
use Data::Dumper;
my $uuid = 'fcd7f63c-a99e-4f13-befe-df0a9d1941f8';
my %prefs;
my $start;
my $duration;
#my $runs = 100000;
my $runs = 1;
###### Handlersocket version goes here: ######################
my $kam_usr_pref = 1;
my $hs = Net::HandlerSocket->new({
host => '127.0.0.1',
port => 9996,
}) or die "Failed to connect to HS port\n";
if($hs->auth("readsecret")) {
die "Failed to auth\n";
}
if($hs->open_index($kam_usr_pref, 'kamailio', 'usr_preferences', 'ua_idx', 'attribute,value')) {
die "Failed to open index on kamailio.usr_preferences: " . $hs->get_error() . "\n";
}
$start = [gettimeofday];
for(my $i = 0; $i < $runs; ++$i) {
%prefs = ();
my $res = $hs->execute_single($kam_usr_pref, '=', [$uuid], -1, 0);
my $status = shift @{ $res };
if($status != 0) {
die "Failed to execute query: " . $hs->get_error() . "\n";
}
while(@{ $res }) {
my $k = shift @{ $res };
my $v = shift @{ $res };
# we already had this key, so it's an attribute
# with at least 2 values
if(exists $prefs{$k}) {
# if we already have >1 values, it's
# converted to an array ref, so just
# push our value at the end
if(ref $prefs{$k} eq "ARRAY") {
push @{ $prefs{$k} }, $v;
# otherwise we have to convert the existing
# value to an array ref and attach our new
# value at the end
} else {
$prefs{$k} = [ $prefs{$k}, $v ];
}
} else {
$prefs{$k} = $v;
}
}
}
$duration = tv_interval($start, [gettimeofday]);
print "--------- handlersocket results --------------\n";
print "Duration: $duration sec for $runs interations\n";
print "Req/sec: " . ($runs/$duration) . "\n";
print "Dur/req: " . ($duration/$runs) . " sec\n";
print "Result:\n";
print Dumper \%prefs;
exit;
###### SQL version goes here: ######################
my $dsn = "DBI:mysql:database=kamailio;host=localhost;port=3306";
my $dbh = DBI->connect($dsn, "kamailioro", "VvKthesty9cVziaVCidM");
my $sth = $dbh->prepare("select attribute,value from usr_preferences where uuid=?") or die "Failed to prepare select statement\n";
for(my $i = 0; $i < $runs; ++$i) {
%prefs = ();
$sth->execute($uuid);
while(my $ref = $sth->fetchrow_hashref()) {
my $k = $ref->{'attribute'};
my $v = $ref->{'value'};
# we already had this key, so it's an attribute
# with at least 2 values
if(exists $prefs{$k}) {
# if we already have >1 values, it's
# converted to an array ref, so just
# push our value at the end
if(ref $prefs{$k} eq "ARRAY") {
push @{ $prefs{$k} }, $v;
# otherwise we have to convert the existing
# value to an array ref and attach our new
# value at the end
} else {
$prefs{$k} = [ $prefs{$k}, $v ];
}
} else {
$prefs{$k} = $v;
}
}
}
$duration = tv_interval($start, [gettimeofday]);
print "--------- sql results --------------\n";
print "Duration: $duration sec for $runs interations\n";
print "Req/sec: " . ($runs/$duration) . "\n";
print "Dur/req: " . ($duration/$runs) . " sec\n";
print "Result:\n";
print Dumper \%prefs;

@ -0,0 +1,81 @@
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include "hsclient-c-wrapper.h"
/*
create table test (
id int(11) not null auto_increment primary key,
uuid varchar(255) not null default '',
attribute varchar(255) not null default '',
somekey varchar(255) not null default '',
strvalue varchar(255) default null,
intvalue int(11) not null default 0,
key ua_idx(uuid,attribute)
);
insert into test values(NULL, 'update-key', 'testattr', 'someval', 'testval', 42);
*/
static int update_prefs() {
char *host = "127.0.0.1";
uint16_t port = 9997;
char *dbname = "kamailio";
char *table = "test";
char *index = "ua_idx";
char *indexcols = "uuid,attribute";
char *secret = "writesecret";
hs_key *keys[] = {
&((hs_key){ .name = "uuid", .op = "=", .val = "update-key" }),
&((hs_key){ .name = "attribute", .op = "=", .val = "testattr" }),
&((hs_key){ .name = "somekey", .op = "=", .val = "someval" }),
NULL
};
hs_val *vals[] = {
&((hs_val){ .name = "strval", .type = HS_NULL }),
&((hs_val){ .name = "intval", .val_int = 99, .type = HS_INT }),
NULL
};
hs_res res;
hs_con con;
res = hs_init_connection(&con, host, port, dbname, table, index, indexcols, secret);
if(!hs_success(res)) {
printf("failed to init usr_pref connection: %s\n", hs_get_error(res));
hs_free_result(res);
return -1;
}
hs_free_result(res);
res = hs_prepare_query(con, keys);
if(!hs_success(res)) {
printf("failed to prepare query: %s\n", hs_get_error(res));
hs_free_result(res);
return -1;
}
hs_free_result(res);
#if 0
for(i = 0; i < runs; ++i) {
res = hs_update(con, vals);
if(!hs_success(res)) {
printf("failed to update prefs: %s\n", hs_get_error(res));
hs_free_result(res);
return -1;
}
hs_free_result(res);
}
#endif
hs_close(con);
return 0;
}
int main() {
update_prefs();
return 0;
}
Loading…
Cancel
Save