From a25ec56db70b69459190f4f9cba787ca04ec9e3c Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 6 Mar 2015 20:11:22 +0100 Subject: [PATCH] [3736] Removed dhcp-ubench and folders. --- Makefile.am | 3 +- configure.ac | 2 - tests/Makefile.am | 1 - tests/tools/Makefile.am | 1 - tests/tools/dhcp-ubench/Makefile | 65 - tests/tools/dhcp-ubench/README | 10 - tests/tools/dhcp-ubench/benchmark.cc | 198 --- tests/tools/dhcp-ubench/benchmark.h | 209 --- tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 1452 ----------------- tests/tools/dhcp-ubench/memfile_ubench.cc | 356 ---- tests/tools/dhcp-ubench/memfile_ubench.h | 103 -- tests/tools/dhcp-ubench/mysql.schema | 86 - tests/tools/dhcp-ubench/mysql_ubench.cc | 674 -------- tests/tools/dhcp-ubench/mysql_ubench.h | 104 -- .../performance-results-graph1.png | Bin 38873 -> 0 bytes .../performance-results-graph2.png | Bin 38979 -> 0 bytes .../tools/dhcp-ubench/performance-results.ods | Bin 48268 -> 0 bytes tests/tools/dhcp-ubench/sqlite.schema | 85 - tests/tools/dhcp-ubench/sqlite_ubench.cc | 513 ------ tests/tools/dhcp-ubench/sqlite_ubench.h | 74 - 20 files changed, 1 insertion(+), 3935 deletions(-) delete mode 100644 tests/Makefile.am delete mode 100644 tests/tools/Makefile.am delete mode 100644 tests/tools/dhcp-ubench/Makefile delete mode 100644 tests/tools/dhcp-ubench/README delete mode 100644 tests/tools/dhcp-ubench/benchmark.cc delete mode 100644 tests/tools/dhcp-ubench/benchmark.h delete mode 100644 tests/tools/dhcp-ubench/dhcp-perf-guide.xml delete mode 100644 tests/tools/dhcp-ubench/memfile_ubench.cc delete mode 100644 tests/tools/dhcp-ubench/memfile_ubench.h delete mode 100644 tests/tools/dhcp-ubench/mysql.schema delete mode 100644 tests/tools/dhcp-ubench/mysql_ubench.cc delete mode 100644 tests/tools/dhcp-ubench/mysql_ubench.h delete mode 100644 tests/tools/dhcp-ubench/performance-results-graph1.png delete mode 100644 tests/tools/dhcp-ubench/performance-results-graph2.png delete mode 100644 tests/tools/dhcp-ubench/performance-results.ods delete mode 100644 tests/tools/dhcp-ubench/sqlite.schema delete mode 100644 tests/tools/dhcp-ubench/sqlite_ubench.cc delete mode 100644 tests/tools/dhcp-ubench/sqlite_ubench.h diff --git a/Makefile.am b/Makefile.am index f580336779..56fe5dcd49 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS} # ^^^^^^^^ This has to be the first line and cannot come later in this # Makefile.am due to some bork in some versions of autotools. -SUBDIRS = compatcheck doc . ext src tests m4macros +SUBDIRS = compatcheck doc . ext src m4macros USE_LCOV=@USE_LCOV@ LCOV=@LCOV@ GENHTML=@GENHTML@ @@ -81,7 +81,6 @@ endif if HAVE_OPENSSL openssl/\* \ endif - tests/\* \ unittests/\* \ \*_unittests.cc \ \*_unittest.cc \ diff --git a/configure.ac b/configure.ac index 65810afe67..8fbcad25e3 100644 --- a/configure.ac +++ b/configure.ac @@ -1504,8 +1504,6 @@ AC_CONFIG_FILES([compatcheck/Makefile src/lib/util/threads/tests/Makefile src/lib/util/unittests/Makefile tools/path_replacer.sh - tests/Makefile - tests/tools/Makefile ]) AC_CONFIG_COMMANDS([permissions], [ diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index e214ef2523..0000000000 --- a/tests/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = tools \ No newline at end of file diff --git a/tests/tools/Makefile.am b/tests/tools/Makefile.am deleted file mode 100644 index 1a7eb296dc..0000000000 --- a/tests/tools/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = . diff --git a/tests/tools/dhcp-ubench/Makefile b/tests/tools/dhcp-ubench/Makefile deleted file mode 100644 index 16735e9972..0000000000 --- a/tests/tools/dhcp-ubench/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# Linux switches -CFLAGS= -Ofast -Wall -pedantic -Wextra - -# Mac OS: We don't use pedantic as Mac OS version of MySQL (5.5.24) does use long long (not part of ISO C++) -#CFLAGS=-g -O0 -Wall -Wextra -I/opt/local/include - -# Mac OS does not require -lrt -# Linux requires -lrt -LDFLAGS=-lrt - -MEMFILE_CFLAGS= -MEMFILE_LDFLAGS= - -# It is mysql_config on most Linux systems and mysql_config5 on Mac OS -MYSQL_CONFIG=mysql_config - -MYSQL_CFLAGS=`$(MYSQL_CONFIG) --cflags` -MYSQL_LDFLAGS=`$(MYSQL_CONFIG) --libs` - -SQLITE_CFLAGS=`pkg-config sqlite3 --cflags` -SQLITE_LDFLAGS=`pkg-config sqlite3 --libs` - -all: mysql_ubench sqlite_ubench memfile_ubench - -doc: dhcp-perf-guide.html dhcp-perf-guide.pdf - -mysql_ubench.o: mysql_ubench.cc mysql_ubench.h benchmark.h - $(CXX) $< -c $(CFLAGS) $(MYSQL_CFLAGS) - -benchmark.o: benchmark.cc benchmark.h - $(CXX) $< -c $(CFLAGS) $(MYSQL_CFLAGS) - -mysql_ubench: mysql_ubench.o benchmark.o - $(CXX) $< benchmark.o -o mysql_ubench $(CFLAGS) $(MYSQL_CFLAGS) $(LDFLAGS) $(MYSQL_LDFLAGS) - -sqlite_ubench.o: sqlite_ubench.cc sqlite_ubench.h benchmark.h - $(CXX) $< -c $(CFLAGS) $(SQLLITE_CFLAGS) - -sqlite_ubench: sqlite_ubench.o benchmark.o - $(CXX) $< benchmark.o -o sqlite_ubench $(CFLAGS) $(SQLITE_CFLAGS) $(LDFLAGS) $(SQLITE_LDFLAGS) - -memfile_ubench.o: memfile_ubench.cc memfile_ubench.h benchmark.h - $(CXX) $< -c $(CFLAGS) $(MEMFILE_CFLAGS) - -memfile_ubench: memfile_ubench.o benchmark.o - $(CXX) $< benchmark.o -o memfile_ubench $(LDFLAGS) $(MEMFILE_LDFLAGS) - -clean: - rm -f mysql_ubench sqlite_ubench memfile_ubench *.o - -version.ent: - ln -s ../../../doc/version.ent - -dhcp-perf-guide.html: dhcp-perf-guide.xml version.ent - xsltproc --novalid --xinclude --nonet \ - -o $@ \ - --path ../../../doc \ - --stringparam section.autolabel 1 \ - --stringparam section.label.includes.component.label 1 \ - --stringparam html.stylesheet bind10-guide.css \ - http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \ - dhcp-perf-guide.xml - -dhcp-perf-guide.pdf: dhcp-perf-guide.xml - dblatex -P doc.collab.show=0 -P latex.output.revhistory=0 $< diff --git a/tests/tools/dhcp-ubench/README b/tests/tools/dhcp-ubench/README deleted file mode 100644 index 1bada272e3..0000000000 --- a/tests/tools/dhcp-ubench/README +++ /dev/null @@ -1,10 +0,0 @@ - - This directory contains benchmarks for various planned and considered database - backends for BIND10 DHCP, codename Kea. - - Before using the code, please read DHCP Performance Guide, available in - HTML and PDF formats. - - To compile the code, type: make - - To regenerate documentation, type: make doc diff --git a/tests/tools/dhcp-ubench/benchmark.cc b/tests/tools/dhcp-ubench/benchmark.cc deleted file mode 100644 index c987bf5013..0000000000 --- a/tests/tools/dhcp-ubench/benchmark.cc +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include -#include -#include -#include "benchmark.h" - -// The following headers are for getting precise time (clock_gettime on Linux or that mac os thingie) -#include -#include -#ifdef __MACH__ -#include -#include -#endif - - -using namespace std; - -uBenchmark::uBenchmark(uint32_t iterations, const std::string& dbname, - bool sync /*= false*/, bool verbose /*= true*/, - const std::string& host /* = "" */, - const std::string& user /* = "" */, - const std::string& pass /* = "" */) - :num_(iterations), sync_(sync), verbose_(verbose), - hostname_(host), user_(user), passwd_(pass), dbname_(dbname), - hitratio_(0.9f), compiled_stmt_(true) -{ - /// @todo: make compiled statements a configurable parameter - - /// @todo: convert hitratio_ to user-configurable parameter - - memset(ts_, 0, sizeof(ts_)); -} - -void uBenchmark::usage() { - cout << "This is a benchmark designed to measure expected performance" << endl; - cout << "of several backends. This backend identifies itself as:" << endl; - printInfo(); - - cout << endl << "Possible command-line parameters:" << endl; - cout << " -h - help (you are reading this)" << endl; - cout << " -m hostname - specifies MySQL server to connect (MySQL backend only)" << endl; - cout << " -u username - specifies MySQL user name (MySQL backend only)" << endl; - cout << " -p password - specifies MySQL passwod (MySQL backend only)" << endl; - cout << " -f name - database or filename (MySQL, SQLite and memfile)" << endl; - cout << " -n integer - number of test iterations (MySQL, SQLite and memfile)" << endl; - cout << " -s yes|no - synchronous/asynchronous operation (MySQL, SQLite and memfile)" << endl; - cout << " -v yes|no - verbose mode (MySQL, SQLite and memfile)" << endl; - cout << " -c yes|no - compiled statements (MySQL and SQLite)" << endl; - - exit(EXIT_FAILURE); -} - -void uBenchmark::parseCmdline(int argc, char* const argv[]) { - int ch; - - while ((ch = getopt(argc, argv, "hm:u:p:f:n:s:v:c:")) != -1) { - switch (ch) { - case 'h': - usage(); - case 'm': - hostname_ = string(optarg); - break; - case 'u': - user_ = string(optarg); - break; - case 'p': - passwd_ = string(optarg); - break; - case 'f': - dbname_ = string(optarg); - break; - case 'n': - try { - num_ = boost::lexical_cast(optarg); - } catch (const boost::bad_lexical_cast &) { - cerr << "Failed to parse number of iterations (-n option):" - << optarg << endl; - usage(); - } - break; - case 'c': - compiled_stmt_ = !strcasecmp(optarg, "yes") || !strcmp(optarg, "1"); - break; - case 's': - sync_ = !strcasecmp(optarg, "yes") || !strcmp(optarg, "1"); - break; - case 'v': - verbose_ = !strcasecmp(optarg, "yes") || !strcmp(optarg, "1"); - break; - default: - usage(); - } - } -} - -void uBenchmark::failure(const char* operation) { - cout << "Error during " << operation << endl; - throw string(operation); -} - -void uBenchmark::printClock(const std::string& operation, uint32_t num, - const struct timespec& before, - const struct timespec& after) { - long int tv_sec = after.tv_sec - before.tv_sec; - - long int tv_nsec = after.tv_nsec - before.tv_nsec; - - if (tv_nsec < 0) { - tv_sec--; - tv_nsec += 1000000000; // 10^9 - } - - double oneoper = (tv_nsec/1000 + tv_sec*1000000)/num; - - cout << operation << " repeated " << num << " times took " - << tv_sec << " s, " << tv_nsec/1000 << " us, 1 operation took " - << oneoper << "us (or " << (1000000/oneoper) << " oper/sec)" << endl; - -} - -int uBenchmark::run() { - - cout << "Starting test. Parameters:" << endl - << "Number of iterations : " << num_ << endl - << "Sync/async : " << (sync_ ? "sync" : "async") << endl - << "Verbose : " << (verbose_ ? "verbose" : "quiet") << endl - << "Compiled statements : " << (compiled_stmt_ ? "yes": "no") << endl - << "Database name : " << dbname_ << endl - << "MySQL hostname : " << hostname_ << endl - << "MySQL username : " << user_ << endl - << "MySQL password : " << passwd_ << endl << endl; - - - srandom(time(NULL)); - - try { - connect(); - - ts_[0] = getTime(); - - createLease4Test(); - ts_[1] = getTime(); - - searchLease4Test(); - ts_[2] = getTime(); - - updateLease4Test(); - ts_[3] = getTime(); - - deleteLease4Test(); - ts_[4] = getTime(); - - disconnect(); - - } catch (const std::string& e) { - cout << "Failed: " << e << endl; - return (-1); - } - - printClock("Create leases4", num_, ts_[0], ts_[1]); - printClock("Search leases4", num_, ts_[1], ts_[2]); - printClock("Update leases4", num_, ts_[2], ts_[3]); - printClock("Delete leases4", num_, ts_[3], ts_[4]); - - return (0); -} - -struct timespec uBenchmark::getTime() { - struct timespec ts; - -#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - ts.tv_sec = mts.tv_sec; - ts.tv_nsec = mts.tv_nsec; -#else - clock_gettime(CLOCK_REALTIME, &ts); -#endif - - return ts; -} diff --git a/tests/tools/dhcp-ubench/benchmark.h b/tests/tools/dhcp-ubench/benchmark.h deleted file mode 100644 index fc3360698c..0000000000 --- a/tests/tools/dhcp-ubench/benchmark.h +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include - -#ifndef BENCHMARK_H -#define BENCHMARK_H - -/// @brief Micro-benchmark base class. -/// -/// This class represents an abstract DHCP database backend benchmark. -/// It is not intended to be used directly, but serves as a common -/// denominator for specific backend benchmarks that are derived from -/// it. Currently there are at least 3 benchmarks implemented that -/// take advantage of it: -/// - MySQL (MySQL_uBenchmark) -/// - SQLite (SQLite_uBenchmark) -/// - memfile (memfile_uBenchmark) -class uBenchmark { -public: - - /// @brief the sole constructor, used by all derivated benchmarks - /// - /// @param iterations number of iterations of each step (insert, search, - /// update, delete) - /// @param dbname name of the database (that is backend-specific, either - /// filename or DB name) - /// @param sync sync or async test mode - /// @param verbose should extra logging be enabled? - /// @param host some backends (currently only MySQL) need this (optional) - /// @param username some backends (currently only MySQL) need this (optional) - /// @param pass some backends (currently only MySQL) need this (optional) - uBenchmark(uint32_t iterations, const std::string& dbname, - bool sync, bool verbose, - const std::string& host = "", - const std::string& username = "", - const std::string& pass = ""); - - /// @brief Prints version information about specific backend. - /// - /// The implementation is provided by the DB-specific class. - virtual void printInfo() = 0; - - /// @brief Opens a connection to the database. - /// - /// The implementation is provided by the DB-specific class. - virtual void connect() = 0; - - /// @brief Closes connection to the database. - /// - /// The implementation is provided by the DB-specific class. - virtual void disconnect() = 0; - - /// @brief Benchmarks IPv4 address lease creation. - /// - /// That benchmark method will be called first. - /// It is expected to create specific number of leases, - /// as specified by \ref num_ parameter. Following - /// methods (searchLease4Test(), updateLease4Test(), - /// and deleteLease4Test()) assume that lease creation - /// is successful. The benchmark is expected to create leases - /// starting from BASE_ADDR4 and ending on BASE_ADDR4 + num_. - /// - /// The implementation is provided by the DB-specific class. - virtual void createLease4Test() = 0; - - /// @brief Benchmarks IPv4 address lease search. - /// - /// This is the second benchmark in a series of four. - /// It is called after createLease4Test(), so it expects that the - /// database is populated with at least \ref num_ leases. - /// It repeats search for a lease num_ times. - /// - /// The algorithm randomly picks a lease with \ref hitratio_ (typically 90%) - /// chance of finding a lease. During typical DHCP operation the server - /// sometimes wants to check if specific lease is assigned or not and the - /// lease is sometimes not present (e.g. when randomly trying to pick a new - /// lease for a new client or doing confirm). Although rather unlikely, - /// cases when searching for non-existing leases may be more costly, - /// thus should be modelled. - /// - /// The implementation is provided by the DB-specific class. - virtual void searchLease4Test() = 0; - - /// @brief Benchmarks IPv4 address lease update. - /// - /// This is the third benchmark in a series of four. - /// It is called after createLease4Test(), so it expects that the - /// database is populated with at least \ref num_ leases. - /// - /// In a normal DHCP operation, search and update operations are used - /// together, but for the benchmarking purposes they are executed - /// separately here. Once a lease is found, it is being updated. Typically - /// the update is just changing lease expiration timers, so that is what - /// the test does. It exploits the fact that there are num_ leases - /// in the database, so it picks randomly an address from - /// BASE_ADDR4 ... BASE_ADDR4 + num_ range and has a guarantee for the lease - /// to be present. - /// - /// The implementation is provided by the DB-specific class. - virtual void updateLease4Test() = 0; - - /// @brief Benchmarks IPv4 address lease removal. - /// - /// This is the last benchmark in a series of four. - /// It is called after createLease4Test(), so it expects that the - /// database is populated with at least \ref num_ leases. - /// - /// It is expected to iteratively delete all num_ leases from - /// the database. - /// - /// The implementation is provided by the DB-specific class. - virtual void deleteLease4Test() = 0; - - /// @brief Utility function for reporting errors. - /// - /// Benchmarks should call that function when something goes wrong. - /// details of the problem must be passed as a parameter. As the benchmark - /// is not designed to recover from errors, reporting an error aborts - /// benchmark execution. - /// - /// @param operation description of the operation that caused failure - virtual void failure(const char* operation); - - /// @brief Prints elapsed time of a specific operation - /// - /// This method prints out elapsed time of a specific benchmark, together - /// with additional statistics. - /// - /// @param operation name of the operation (usually create, search, update, delete) - /// @param num number or iterations (used for statistics) - /// @param before timestamp before execution - /// @param after timestamp after execution - void printClock(const std::string& operation, uint32_t num, - const struct timespec& before, - const struct timespec& after); - - /// @brief Main benchmark execution routine - /// - /// This method calls create, search, update and delete benchmarks - /// and measures appropriate timestamps in ts_ table. - /// - /// @return 0 if the run was successful, negative value if detected errors - int run(); - - /// @brief parses command-line parameters - /// - /// This method parses command-line parameters and sets up appropriate - /// values. It is ok to pass argc, argv from main() here. - /// - /// This method may not return if -h (help) was specified or invalid - /// arguments are passed. Appropriate error and help will be displayed - /// and the program will terminate. - /// - /// @param argc number of arguments - /// @param argv array to the arguments - void parseCmdline(int argc, char* const argv[]); - -protected: - /// @brief prints out command-line help (list of parameters + version) - void usage(); - - /// @brief a wrapper around OS-specific method for getting time - struct timespec getTime(); - - /// Number of operations (e.g. insert lease num times) - uint32_t num_; - - /// Synchronous or asynchronous mode? - bool sync_; - - /// Should the test print out extra information? - bool verbose_; - - // DB parameters - std::string hostname_; // used by MySQL only - std::string user_; // used by MySQL only - std::string passwd_; // used by MySQL only - std::string dbname_; // used by MySQL, SQLite and memfile - - /// @brief hit ratio for search test (must be between 0.0 and 1.0) - /// - /// This parameter is used in search benchmark. The formula causes the - /// search to find something a lease in 90% cases of hit ratio is 0.9. - float hitratio_; - - /// benchmarks must generate the leases starting from 1.0.0.0 address - const static uint32_t BASE_ADDR4 = 0x01000000; - - /// five timestamps (1 at the beginning and 4 after each step) - struct timespec ts_[5]; - - /// should compiled statements be used? - bool compiled_stmt_; -}; - -#endif diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml deleted file mode 100644 index 0115cd60cd..0000000000 --- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml +++ /dev/null @@ -1,1452 +0,0 @@ - - - -%version; -]> - - - - - - - - DHCP Performance Guide - - - - 2012 - Internet Systems Consortium, Inc. ("ISC") - - - Tomasz - Mrugalski - - - Marcin - Siodelski - - - BIND 10 is a framework that features Domain Name System - (DNS) and Dynamic Host Configuration Protocol (DHCP) - software with development managed by Internet Systems Consortium (ISC). - This document describes various aspects of DHCP performance, - measurements and tuning. It covers BIND 10 DHCP (codename Kea), - existing ISC DHCP4 software, perfdhcp (a DHCP performance - measurement tool) and other related topics. - - - This is a companion document for BIND 10 version - &__VERSION__;. - - - - - Preface - -
- Acknowledgements - - ISC would like to acknowledge generous support for - BIND 10 development of DHCPv4 and DHCPv6 components provided - by Comcast. - -
- -
- - - Introduction - - This document is in the early stages of development. It is - expected to grow significantly in the near future. It will - cover topics like database backend perfomance measurements, - tools, and the pros an cons of various optimization techniques. - - - - - - ISC DHCP 4.x - - TODO: Write something about ISC DHCP4 here. - - - - - Kea - - - - -
- Backend performance evaluation - - Kea will support several different database backends, using - both popular databases (like MySQL or SQLite) and - custom-developed solutions (such as an in-memory database). - To aid in the choice of backend, the BIND 10 - source code features a set of performance microbenchmarks. - Written in C/C++, these are small tools that simulate expected - DHCP server behaviour and evaluate the performance of - considered databases. As implemented, the benchmarks are not really - simulating DHCP operation, but rather use set of primitives - that can be used by a real server. For this reason, they are called - micro-benchmarks. - - - Although there are many operations and data types that - server could store in a database, the most frequently used data - type is lease information. Although the information held for IPv4 - and IPv6 leases differs slightly, it is expected that the performance - differences will be minimal between IPv4 and IPv6 lease operations. - Therefore each test uses the lease4 table (in which IPv4 leases are stored) - for performance measurements. - - - All benchmarks are implemented as single threaded applications - that take advantage of a single database connection. - - - Those benchmarks are stored in tests/tools/dhcp-ubench directory of the - BIND 10 source tree. This directory contains simplified prototypes for - the various database back-ends that are planned or considered as a - possibly for BIND10 DHCP. These benchmarks are expected to evolve into - useful tools that will allow users to measure performance in their - specific environment. - - - - Currently the following benchmarks are implemented: - - In memory + flat file - SQLite - MySQL - - - - - As the benchmarks require additional (sometimes heavy) dependencies, they - are not built by default. Actually, their build system is completely - separate from that of the rest of BIND 10. It will be eventually merged - with the main BIND 10 build system. - - - - All benchmarks will follow the same pattern: - - Prepare operation (connect to a database, create a file etc.) - Measure timestamp 0 - Commit new lease4 record (repeated N times) - Measure timestamp 1 - Search for random lease4 record (repeated N times) - Measure timestamp 2 - Update existing lease4 record (repeated N times) - Measure timestamp 3 - Delete existing lease4 record (repeated N times) - Measure timestamp 4 - Print out statistics, based on N and measured timestamps. - - - Although this approach does not attempt to simulate actual DHCP server - operation that has mix of all steps, it answers the - questions about basic database strengths and weak points. In particular - it can show what is the impact of specific database optimizations, such as - changing engine, optimizing for writes/reads etc. - - - - The framework attempts to do the same amount of work for every - backend thus allowing fair comparison between them. - -
- -
- MySQL backend - The MySQL backend requires the MySQL client development libraries. It uses - the mysql_config tool (similar to pkg-config) to discover required - compilation and linking options. To install required packages on Ubuntu, - use the following command: - - $ sudo apt-get install mysql-client mysql-server libmysqlclient-dev - - Make sure that MySQL server is running. Make sure that you have your setup - configured so there is a user that is able to modify used database. - - Before running tests, you need to initialize your database. You can - use mysql.schema script for that purpose. - - WARNING: It will drop existing - Kea database. Do not run this on your production server. - - Assuming your - MySQL user is "kea", you can initialize your test database by: - - $ mysql -u kea -p < mysql.schema - - - After the database is initialized, you are ready to run the test: - $ ./mysql_ubench - - or - - $ ./mysql_ubench > results-mysql.txt - - Redirecting output to a file is important, because for each operation - there is a single character printed to show progress. If you have a slow - terminal, this may considerably affect test performance. On the other hand, - printing something after each operation is required as poor database settings - may slow down operations to around 20 per second. (The observant user is expected - to note that the initial dots are printed too slowly and abort the test.) - - Currently all default parameters are hardcoded. Default values can be - overridden using command line switches. Although all benchmarks take - the same list of parameters, some of them are specific to a given backend. - To get a list of supported parameters, run the benchmark with the "-h" option: - - $ ./mysql_ubench -h - - - Synchronous operation requires database backend to - physically store changes to disk before proceeding. This - property ensures that no data is lost in case of the server - failure. Unfortunately, it slows operation - considerably. Asynchronous mode allows database to write data at - a later time (usually controlled by the database engine on OS - disk buffering mechanism). - -
- MySQL tweaks - - To modify the default mysql_ubench parameters, command line - switches can be used. The currently supported switches are - (default values specified in brackets): - - -f name - name of the database ("kea") - -m hostname - name of the database host ("localhost") - -u user - MySQL username ("root") - -p password - MySQL password ("secret") - -n num - number of iterations (100) - -s yes|no - should the operations be performed in a synchronous (yes) - or asynchronous (no) manner (yes) - -v yes|no - verbose mode. Should the test print out progress? (yes) - -c yes|no - precompiled statements. Should the SQL statements be precompiled? (yes) - - - - - One parameter that has huge impact on performance is the choice of backend engine. - You can get a list of engines of your MySQL implementation by using - - > show engines; - - in your mysql client. Two notable engines are MyISAM and InnoDB. mysql_ubench uses - use MyISAM for synchronous mode and InnoDB for asynchronous. Please use - '-s yes|no' to choose whether you want synchronous or asynchronous operations. - - Another parameter that affects performance are precompiled statements. - In a basic approach, the actual SQL query is passed as a text string that is - then parsed by the database engine. Alternative is a so called precompiled - statement. In this approach the SQL query is compiled an specific values are being - bound to it. In the next iteration the query remains the same, only bound values - are changing (e.g. searching for a different address). Usage of basic or precompiled - statements is controlled with '-c no|yes'. -
-
- - -
- SQLite-ubench - The SQLite backend requires both the sqlite3 development and run-time packages. Their - names may vary from system to system, but on Ubuntu 12.04 they are called - sqlite3 libsqlite3-dev. To install them, use the following command: - - > sudo apt-get install sqlite3 libsqlite3-dev - - Before running the test the database has to be created. Use the following command for that: - > cat sqlite.schema | sqlite3 sqlite.db - - A new database called sqlite.db will be created. That is the default name used - by sqlite_ubench test. If you prefer other name, make sure you update - sqlite_ubench.cc accordingly. - - Once the database is created, you can run tests: - > ./sqlite_ubench - or - > ./sqlite_ubench > results-sqlite.txt - - -
- SQLite tweaks - To modify default sqlite_ubench parameters, command line - switches can be used. The currently supported switches are - (default values specified in brackets): - - -f filename - name of the database file ("sqlite.db") - -n num - number of iterations (100) - -s yes|no - should the operations be performed in a synchronous (yes) - or asynchronous (no) manner (yes) - -v yes|no - verbose mode. Should the test print out progress? (yes) - -c yes|no - precompiled statements. Should the SQL statements be precompiled? (yes) - - - - SQLite can run in asynchronous or synchronous mode. This - mode can be controlled by using "synchronous" parameter. It is set - using the SQLite command: - - PRAGMA synchronous = ON|OFF - - Another tweakable feature is journal mode. It can be - turned to several modes of operation. Its value can be - modified in SQLite_uBenchmark::connect(). See - http://www.sqlite.org/pragma.html#pragma_journal_mode for - a detailed explanation. - - sqlite_bench supports precompiled statements. Please use - '-c no|yes' to define which should be used: basic SQL query (no) or - precompiled statement (yes). -
-
- -
- memfile-ubench The memfile backend is a - custom backend that somewhat mimics operation of ISC DHCP4. It - implements in-memory storage using standard C++ and boost - mechanisms (std::map and boost::shared_ptr<>). All - database changes are also written to a lease file, which is - strictly write-only. This approach takes advantage of the fact - that file append operation is faster than modifications introduced - in the middle of the file (as it often requires moving all data - after modified point, effectively requiring writing large parts of - the whole file, not just changed fragment). - - There are no preparatory steps required for memfile benchmark. - The only requirement is the ability to create and write specified lease - file (dhcpd.leases in the current directory). The tests can be run - as follows: - > ./memfile_ubench - or - > ./memfile_ubench > results-memfile.txt - - -
- memfile tweaks - To modify default memfile_ubench parameters, command line - switches can be used. Currently supported switches are - (default values specified in brackets): - - -f filename - name of the database file ("dhcpd.leases") - -n num - number of iterations (100) - -s yes|no - should the operations be performend in a synchronous (yes) - or asynchronous (no) manner (yes) - -v yes|no - verbose mode. Should the test print out progress? (yes) - - - - memfile can run in asynchronous or synchronous mode. This - mode can be controlled by using sync parameter. It uses - fflush() and fsync() in synchronous mode to make sure that - data is not buffered and physically stored on disk. -
-
- -
- Basic performance measurements - This section contains sample results for backend performance measurements, - taken using microbenchmarks. Tests were conducted on reasonably powerful machine: - -CPU: Quad-core Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (8 logical cores) -HDD: 1,5TB Seagate Barracuda ST31500341AS 7200rpm, ext4 partition -OS: Ubuntu 12.04, running kernel 3.2.0-26-generic SMP x86_64 -compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 -MySQL version: 5.5.24 -SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe77b41d959e9df13f8c9b5e - - - The benchmarks were run without using precompiled statements. - The code was compiled with the -O0 flag (no code optimizations). - Each run was executed once. - - Two series of measures were made, synchronous and - asynchronous. As those modes offer radically different - performances, synchronous mode was conducted for one - thousand repetitions and asynchronous mode was conducted for - one hundred thousand repetitions. - - - Synchronous results (basic) - - - - - - - - - - - Backend - Operations - Create [s] - Search [s] - Update [s] - Delete [s] - Average [s] - - - - - MySQL - 1,000 - 31.604 - 0.117 - 27.964 - 27.695 - 21.845 - - - - SQLite - 1,000 - 61.421 - 0.033 - 59.477 - 56.034 - 44.241 - - - - memfile - 1,000 - 38.224 - 0.001 - 38.041 - 38.017 - 28.571 - - - - -
- - The following parameters were measured for asynchronous mode. - MySQL and SQLite were run with one hundred thousand repetitions. - - - Asynchronous results (basic) - - - - - - - - - - - Backend - Operations - Create [s] - Search [s] - Update [s] - Delete [s] - Average [s] - - - - - MySQL - 100,000 - 10.585 - 10.386 - 10.062 - 8.890 - 9.981 - - - - SQLite - 100,000 - 3.710 - 3.159 - 2.865 - 2.439 - 3.044 - - - - memfile - 100,000 - 1.300 - 0.039 - 1.307 - 1.278 - 0.981 - - - - -
- - The presented performance results can be converted into operations per second metrics. - It should be noted that due to large differences between various operations (sometimes - over three orders of magnitude), it is difficult to create a simple, readable chart with - that data. - - Estimated basic performance - - - - - - - - - - Backend - Create [oper/s] - Search [oper/s] - Update [oper/s] - Delete [oper/s] - Average [oper/s] - - - - - MySQL (async) - 9447.47 - 9627.97 - 9938.00 - 11248.34 - 10065.45 - - - - SQLite (async) - 26951.59 - 31654.29 - 34899.70 - 40993.59 - 33624.79 - - - - memfile (async) - 76944.27 - 2542588.35 - 76504.54 - 78269.25 - 693576.60 - - - - - MySQL (sync) - 31.64 - 8575.45 - 35.76 - 36.11 - 2169.74 - - - - SQLite (sync) - 16.28 - 20045.37 - 16.81 - 17.85 - 7524.08 - - - - memfile (sync) - 26.16 - 1223990.21 - 26.29 - 26.30 - 306017.24 - - - - -
- - - - - - - - - - - Graphical representation of the basic performance results - presented in table . - - - -
- -
- Optimized performance measurements - This section contains sample results for backend performance measurements, - taken using microbenchmarks. Tests were conducted on reasonably powerful machine: - -CPU: Quad-core Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz (8 logical cores) -HDD: 1,5TB Seagate Barracuda ST31500341AS 7200rpm, ext4 partition -OS: Ubuntu 12.04, running kernel 3.2.0-26-generic SMP x86_64 -compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 -MySQL version: 5.5.24 -SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe77b41d959e9df13f8c9b5e - - - The benchmarks were run with precompiled statements enabled. - The code was compiled with the -Ofast flag (optimize compilation for speed). - Each run was repeated three times and measured values were averaged. - - Again the benchmarks were run in two series, synchronous and - asynchronous. As those modes offer radically different - performances, synchronous mode was conducted for one - thousand repetitions and asynchronous mode was conducted for - one hundred thousand repetitions. - - - Synchronous results (optimized) - - - - - - - - - - - Backend - Operations - Create [s] - Search [s] - Update [s] - Delete [s] - Average [s] - - - - - MySQL - 1,000 - 27.887 - 0.106 - 28.223 - 27.696 - 20.978 - - - - SQLite - 1,000 - 61.299 - 0.015 - 59.648 - 61.098 - 45.626 - - - - memfile - 1,000 - 39.564 - 0.001 - 39.543 - 39.326 - 29.608 - - - - -
- - The following parameters were measured for asynchronous mode. - MySQL and SQLite were run with one hundred thousand repetitions. - - - Asynchronous results (optimized) - - - - - - - - - - - Backend - Operations - Create [s] - Search [s] - Update [s] - Delete [s] - Average [s] - - - - - MySQL - 100,000 - 8.507 - 9.698 - 7.785 - 8.326 - 8.579 - - - - SQLite - 100,000 - 1.562 - 0.949 - 1.513 - 1.502 - 1.382 - - - - memfile - 100,000 - 1.302 - 0.038 - 1.306 - 1.263 - 0.977 - - - - -
- - The presented performance results can be converted into operations per second metrics. - It should be noted that due to large differences between various operations (sometime - over three orders of magnitude), it is difficult to create a simple, readable chart with - the data. - - Estimated optimized performance - - - - - - - - - - Backend - Create [oper/s] - Search [oper/s] - Update [oper/s] - Delete [oper/s] - Average [oper/s] - - - - - MySQL (async) - 11754.84 - 10311.34 - 12845.35 - 12010.24 - 11730.44 - - - - SQLite (async) - 64005.90 - 105391.29 - 66075.51 - 66566.43 - 75509.78 - - - - memfile (async) - 76832.16 - 2636018.56 - 76542.50 - 79188.81 - 717145.51 - - - - - MySQL (sync) - 35.86 - 9461.10 - 35.43 - 36.11 - 2392.12 - - - - SQLite (sync) - 16.31 - 67036.11 - 16.76 - 16.37 - 16771.39 - - - - memfile (sync) - 25.28 - 3460207.61 - 25.29 - 25.43 - 865070.90 - - - - -
- - - - - - - Optimized performance measurements - - - Graphical representation of the optimized performance - results presented in table . - - - -
- -
- Conclusions - - Improvements gained by introducing support for precompiled - statements in MySQL is somewhat disappointing - between 6 and - 29%. On the other hand, the improvement in SQLite is - surprisingly high - the efficiency is more than doubled. - - - Compiled statements do not have any measureable impact on - synchronous operations. That is as expected, because the major - bottleneck is the disk performance. - - - Compilation flags yield surprisingly high improvements for C++ - STL code. The memfile backend is in some operations is almost - twice as fast. - - - - If synchronous operation is required, the current performance - results are likely to be deemed inadequate. The limiting - factor here is a disk access time. Even migrating to high - performance 15,000 rpm disk is expected to only roughly double - number of leases per second, compared to the current results. - The reason is that to write a file to disk, at least two disk - sector writes - are required: the new content and i-node modification of the - file. The easiest way to boost synchronous performance is to - switch to SSD disks. Memory-backed RAM disks are also a viable - solution. However, care should be taken to properly engineer - backup strategy for such RAM disks. - - - - While the custom made backend (memfile) provides the best - perfomance, it carries over all the limitations existing in - the ISC DHCP4 code: there are no external tools to query or - change database, the maintenance requires deep knowledge etc. - Those flaws are not shared by usage of a proper database - backend, like MySQL and SQLite. They both offer third party - tools for administrative tasks, they are well documented and - maintained. However, SQLite support for concurrent access is - limiting in certain cases. Since all three investigated - backends more than meet expected performance results, it is - recommended to use MySQL as a first concrete database backend. - Should this choice be rejected for any reason, the second - recommended choice is SQLite. - - - - It should be emphasized that obtained measurements indicate - only database performance and they cannot be directly - translated to expected leases per second or queries per second - performance by an actual server. The DHCP server must do much - more than just query the database to properly process client's - message. The provided results should be considered as only rough - estimates. They can also be used for relative comparisons - between backends. - - -
- -
- Possible further optimizations - - For basic measurements the code was compiled with -g -O0 - flags. For optimized measurements the benchmarking code was - compiled with -Ofast (optimize for speed). In both cases, the - same backend (MySQL or SQLite) library was used. It may be - useful to recompile the libraries (or the whole server in case - of MySQL) with -Ofast. - - - There are many MySQL parameters that various sources recommend - to improve performance. They were not investigated further. - - - Currently all operations are conducted on one by one - basis. Each operation is treated as a separate - transaction. Grouping N operations together will potentially - bring almost N fold increase in synchronous operations. Such a - feature is present in ISC DHCP4 and is called cache-threshold. - Extension for this benchmark in this regard should be - considered. That affects only write operations (insert, - update and delete). Read operations (search) are expected to - be barely affected. - - - Multi-threaded or multi-process benchmark may be considered in - the future. It may be somewhat difficult as only some backends - support concurrent access. - -
- -
- - - perfdhcp -
- Purpose - - Evaluation of the performance of a DHCP server requires that it - be tested under varying traffic loads. perfdhcp is a testing - tool with the capability to create traffic loads - and generate statistics from the results. Additional features, - such as the ability to send customised DHCP packets, allow it to - be used in a wide range of functional testing. - -
-
- Key features - - perfdhcp has a number of command line switches to - control DHCP message exchanges. Currently they fall into - the following categories: - - - - Rate control - control how many DHCP exchanges - are initiated within a period of time. The tool can also simulate - best effort conditions by attempting to initiate as many DHCP - packet exchanges as possible within a unit of time. - - - - - Test exit specifiers - control the conditions for test - completion, including the number of initiated exchanges, - the test period orthe maximum number of dropped packets. - - - - - Packet templates - specify files containing packet templates that - are used by perfdhcp to create custom DHCP messages. The tool - allows the specification of a number of values indicating - offsets of values within a packet that are set by the tool. - - - - - Reporting - for each test produce a set of performance data - including the achieved packet exchange rate (server performance). - There are a number of diagnostic selectors available that - enable periodic (intermediate) reporting, printing of packet timestamps, - and the listing of detailed information about internal perfdhcp - states (for debugging). - - - - - Different mode of operations - specify the DHCP protocol used - (v4 or v6), two-way or four-way exchanges, use of the - Rapid Commit option for DHCPv6. - - - - - IP layer options - specify the local/remote address, local interface - and local port to be used for communication with DHCP server. - - - - -
-
- Command line options - - The following "help" output from the tool describes the - command line switches. This summary also lists the tool's - possible exit codes as well as describing the - error counters printed when the test is complete: - $ perfdhcp -h -] [-t] [-R] [-b] - [-n] [-p] [-d] [-D] - [-l] [-P] [-a] - [-L] [-s] [-i] [-B] [-c] [-1] - [-T] [-X] [-O] [-S] [-I] - [-x] [-w] [server] - -The [server] argument is the name/address of the DHCP server to -contact. For DHCPv4 operation, exchanges are initiated by -transmitting a DHCP DISCOVER to this address. - -For DHCPv6 operation, exchanges are initiated by transmitting a DHCP -SOLICIT to this address. In the DHCPv6 case, the special name 'all' -can be used to refer to All_DHCP_Relay_Agents_and_Servers (the -multicast address FF02::1:2), or the special name 'servers' to refer -to All_DHCP_Servers (the multicast address FF05::1:3). The [server] -argument is optional only in the case that -l is used to specify an -interface, in which case [server] defaults to 'all'. - -The default is to perform a single 4-way exchange, effectively pinging -the server. -The -r option is used to set up a performance test, without -it exchanges are initiated as fast as possible. - -Options: --1: Take the server-ID option from the first received message. --4: DHCPv4 operation (default). This is incompatible with the -6 option. --6: DHCPv6 operation. This is incompatible with the -4 option. --a: When the target sending rate is not yet reached, - control how many exchanges are initiated before the next pause. --b: The base mac, duid, IP, etc, used to simulate different - clients. This can be specified multiple times, each instance is - in the = form, for instance: - (and default) mac=00:0c:01:02:03:04. --d: Specify the time after which a request is treated as - having been lost. The value is given in seconds and may contain a - fractional component. The default is 1 second. --E: Offset of the (DHCPv4) secs field / (DHCPv6) - elapsed-time option in the (second/request) template. - The value 0 disables it. --h: Print this help. --i: Do only the initial part of an exchange: DO or SA, depending on - whether -6 is given. --I: Offset of the (DHCPv4) IP address in the requested-IP - option / (DHCPv6) IA_NA option in the (second/request) template. --l: For DHCPv4 operation, specify the local - hostname/address to use when communicating with the server. By - default, the interface address through which traffic would - normally be routed to the server is used. - For DHCPv6 operation, specify the name of the network interface - via which exchanges are initiated. --L: Specify the local port to use - (the value 0 means to use the default). --O: Offset of the last octet to randomize in the template. --P: Initiate first exchanges back to back at startup. --r: Initiate DORA/SARR (or if -i is given, DO/SA) - exchanges per second. A periodic report is generated showing the - number of exchanges which were not completed, as well as the - average response latency. The program continues until - interrupted, at which point a final report is generated. --R: Specify how many different clients are used. With 1 - (the default), all requests seem to come from the same client. --s: Specify the seed for randomization, making it repeatable. --S: Offset of the server-ID option in the - (second/request) template. --T: The name of a file containing the template to use - as a stream of hexadecimal digits. --v: Report the version number of this program. --w: Command to call with start/stop at the beginning/end of - the program. --x: Include extended diagnostics in the output. - is a string of single-keywords specifying - the operations for which verbose output is desired. The selector - keyletters are: - * 'a': print the decoded command line arguments - * 'e': print the exit reason - * 'i': print rate processing details - * 'r': print randomization details - * 's': print first server-id - * 't': when finished, print timers of all successful exchanges - * 'T': when finished, print templates --X: Transaction ID (aka. xid) offset in the template. - -DHCPv4 only options: --B: Force broadcast handling. - -DHCPv6 only options: --c: Add a rapid commit option (exchanges will be SA). - -The remaining options are used only in conjunction with -r: - --D: Abort the test if more than requests have - been dropped. Use -D0 to abort if even a single request has been - dropped. If includes the suffix '%', it specifies a - maximum percentage of requests that may be dropped before abort. - In this case, testing of the threshold begins after 10 requests - have been expected to be received. --n: Initiate transactions. No report is - generated until all transactions have been initiated/waited-for, - after which a report is generated and the program terminates. --p: Send requests for the given test period, which is - specified in the same manner as -d. This can be used as an - alternative to -n, or both options can be given, in which case the - testing is completed when either limit is reached. --t: Delay in seconds between two periodic reports. - -Errors: -- tooshort: received a too short message -- orphans: received a message which doesn't match an exchange - (duplicate, late or not related) -- locallimit: reached to local system limits when sending a message. - -Exit status: -The exit status is: -0 on complete success. -1 for a general error. -2 if an error is found in the command line arguments. -3 if there are no general failures in operation, but one or more - exchanges are not successfully completed. -]]> - - -
-
- Starting perfdhcp - - In order to run a performance test, at least two separate systems - have to be installed: client and server. The first one must have - perfdhcp installed, and the latter must be running the DHCP server - (either v4 or v6). If only single system is available the client - and server can be run on virtual machines (running on the same - physical system) but in this case performance data may be heavily - impacted by the overhead involved in running such the virtual - machines. - - - Currently, perfdhcp is seen from the server perspective as relay agent. - This simplifies its implementation: specifically there is no need to - receive traffic sent to broadcast addresses. However, it does impose - a requirement that the IPv4 - address has to be set manually on the interface that will be used to - communicate with the server. For example, if the DHCPv4 server is listening - on the interface connected to the 172.16.1.0 subnet, the interface on client - machine has to have network address assigned from the same subnet, e.g. - #ifconfig eth3 172.16.1.2. netmask 255.255.255.0 up - - - As DHCP uses low port numbers (67 for DHCPv4 relays and - 547 for DHCPv6), running perfdhcp with non-root privileges will - usually result in the error message similar to this: - $ perfdhcp -4 -l eth3 -r 100 all -Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67 - - The '-L' command line switch allows the use of a custom local port. - However, although the following command line will work: - $ perfdhcp -4 -l eth3 -r 100 -L 10067 all - in the standard configuration no responses will be received - from the DHCP server because the server responds to default relay - port 67. A way to overcome this issue is to run - perfdhcp as root. - - -
-
- perfdhcp command line examples - - In this section, a number of perfdhcp command line examples - are presented as a quick start guide for new users. For the - detailed list of command line options refer to - . - -
- Example: basic usage - - If server is listening on interface with IPv4 address 172.16.1.1, - the simplest perfdhcp command line will look like: - # perfdhcp 172.16.1.1 -***Rate statistics*** -Rate: 206.345 - -***Statistics for: DISCOVER-OFFER*** -sent packets: 21641 -received packets: 350 -drops: 21291 -orphans: 0 - -min delay: 9.022 ms -avg delay: 143.100 ms -max delay: 259.303 ms -std deviation: 56.074 ms -collected packets: 30 - -***Statistics for: REQUEST-ACK*** -sent packets: 350 -received packets: 268 -drops: 82 -orphans: 0 - -min delay: 3.010 ms -avg delay: 152.470 ms -max delay: 258.634 ms -std deviation: 56.936 ms -collected packets: 0 - - Here, perfdhcp uses remote address 172.16.1.1 as a - destination address and will use a suitable local interface for - communication. Since, no rate control parameters have been specified, - it will initiate DHCP exchanges at the maximum possible rate. Due to the server's - performance limitation, it is likely that many of the packets will be dropped. - The performance test will continue running until it is - interrupted by the user (with ^C). - - - The default performance statistics reported by perfdhcp have the - following meaning: - - Rate - number of packet exchanges (packet sent - to the server and matching response received from the server) - completed within a second. - sent packets - total number of DHCP packets of - a specific type sent to the server. - received packets - total number of DHCP packets - of specific type received from the server. - drops - number of dropped packets for the - particular exchange. The number of dropped packets is calculated as - the difference between the number of sent packets and number of - response packets received from the server. In some cases, the - server will have sent a reponse but perfdhcp execution ended before - the reponse arrived. In such case this packet will be counted - as dropped. - orphans - number of packets that have been - received from the server and did not match any packet sent by - perfdhcp. This may occur if received packet has been sent - to some other host or if then exchange timed out and - the sent packet was removed from perfdhcp's list of packets - awaiting a response. - min delay - minimum delay that occured between - sending the packet to the server and receiving a reponse from - it. - avg delay - average delay between sending the - packet of the specific type the server and receiving a response - from it. - max delay - maximum delay that occured between - sending the packet to the server and receiving a response from - it. - std deviation - standard deviation of the delay - between sending the packet of a specific type to the server and - receiving response from it. - collected packets - number of sent packets that - were garbage collected. Packets may get garbage collected when - the waiting time for server a response exceeds value set with the - '-d' (drop time) switch. - ]]>. - - - - Note: should multiple interfaces on the system running perfdhcp be - connected to the same subnet, the interface to be used for the test - can be specified using either the interface name: - # perfdhcp -l eth3 - or a local address assigned to it: - # perfdhcp -l 172.16.1.2 - -
-
- Example: rate control - - In the examples above perfdhcp initiates new exchanges with a best - effort rate. With this setting, many packets are expected to be dropped - by the server due to performance limitations. In many cases though, it is - desired to verify that the server can handle an expected (reasonable) rate - without dropping any packets. The following command is an example of such - a test: it causes perfdhcp to initiate 300 four-way exchanges - per second, and runs the test for 60 seconds: - # perfdhcp -l eth3 -p 60 -r 300 -***Rate statistics*** -Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second - -***Statistics for: DISCOVER-OFFER*** -sent packets: 17783 -received packets: 15401 -drops: 2382 -orphans: 0 - -min delay: 0.109 ms -avg delay: 75.756 ms -max delay: 575.614 ms -std deviation: 60.513 ms -collected packets: 11 - -***Statistics for: REQUEST-ACK*** -sent packets: 15401 -received packets: 15317 -drops: 84 -orphans: 0 - -min delay: 0.536 ms -avg delay: 72.072 ms -max delay: 576.749 ms -std deviation: 58.189 ms -collected packets: 0 - - Note that here, the packet drops for the DISCOVER-OFFER - exchange have been significantly reduced (when compared with the - output from the previous example) thanks to the setting of a - reasonable rate. The non-zero number of packet drops and achieved - rate (256/s) indicate that server's measured performance is lower than 300 leases - per second. A further rate decrease should eliminate most of the packet - drops and bring the achieved rate close to expected rate: - # perfdhcp -l eth3 -p 60 -r 100 -R 30 -***Rate statistics*** -Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second - -***Statistics for: DISCOVER-OFFER*** -sent packets: 5989 -received packets: 5989 -drops: 0 -orphans: 0 - -min delay: 0.023 ms -avg delay: 2.198 ms -max delay: 181.760 ms -std deviation: 9.429 ms -collected packets: 0 - -***Statistics for: REQUEST-ACK*** -sent packets: 5989 -received packets: 5989 -drops: 0 -orphans: 0 - -min delay: 0.473 ms -avg delay: 2.355 ms -max delay: 189.658 ms -std deviation: 5.876 ms -collected packets: 0 - - There are now no packet drops, confirming that the server is able to - handle a load of 100 leases/second. - Note that the last parameter (-R 30) configures perfdhcp to simulate - traffic from 30 distinct clients. - -
-
- Example: templates - - By default the DHCP messages are formed with default options. With - template files, it is possible to define a custom packet format. - - - The template file names are specified with ]]> - command line option. This option can be specified zero, one or two times. - The first occurence of this option refers to DISCOVER or SOLICIT message - and the second occurence refers to REQUEST (DHCPv4 or DHCPv6) message. - If -T option occurs only once the DISCOVER or SOLICIT message will be - created from the template and the REQUEST message will be created - dynamically (without the template). Note that each template file - holds data for exactly one DHCP message type. Templates for multiple - message types must not be combined in the single file. - The content in template files is encoded as series of ASCII hexadecimal - digits (each byte represented by two ASCII chars 00..FF). Data in a - template file is laid in network byte order and it can be used on the - systems with different endianness. - perfdhcp forms the packet by replacing parts of the message buffer read - from the file with variable data such as elapsed time, hardware address, DUID - etc. The offsets where such variable data is placed is specific to the - template file and have to be specified from the command line. Refer to - to find out how to - specify offsets for particular options and fields. With the following - command line the DHCPv6 SOLICIT and REQUEST packets will be formed from - solicit.hex and request6.hex packets: - # perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers -***Rate statistics*** -Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second - -***Statistics for: SOLICIT-ADVERTISE*** -sent packets: 570 -received packets: 569 -drops: 1 -orphans: 0 - -min delay: 0.259 ms -avg delay: 0.912 ms -max delay: 6.979 ms -std deviation: 0.709 ms -collected packets: 0 - -***Statistics for: REQUEST-REPLY*** -sent packets: 569 -received packets: 569 -drops: 0 -orphans: 0 - -min delay: 0.084 ms -avg delay: 0.607 ms -max delay: 6.490 ms -std deviation: 0.518 ms -collected packets: 0 - - where the switches have the following meaning: - - two occurences of -O 21 - DUID's last octet - positions in SOLICIT and REQUEST respectively. - -E 84 - elapsed time option position in the - REQUEST template - -S 22 - server id position in the REQUEST - template - -I 40 - IA_NA option position in the REQUEST - template - - - - The offsets of options indicate where they begin in the packet. - The only exception from this rule is ]]> - option that specifies the end of the DUID (DHCPv6) or MAC address - (DHCPv4). Depending on the number of simulated clients - (see ]]> command line option) perfdhcp - will be randomizing bytes in packet buffer starting from this - position backwards. For the number of simulated clients - 256 only one octet (at random-offset position) - will be ranomized, for the number of clients 65536 - two octets (at random-offset and random-offset-1) - will be randmized etc. - -
-
-
-
diff --git a/tests/tools/dhcp-ubench/memfile_ubench.cc b/tests/tools/dhcp-ubench/memfile_ubench.cc deleted file mode 100644 index aa3377fbd1..0000000000 --- a/tests/tools/dhcp-ubench/memfile_ubench.cc +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright (C) 2012, 2015 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include -#include -#include "memfile_ubench.h" - -using namespace std; - - -/// @brief In-memory + lease file database implementation -/// -/// This is a simplified in-memory database that mimics ISC DHCP4 implementation. -/// It uses STL and boost: std::map for storage, boost::shared ptr for memory -/// management. It does use C file operations (fopen, fwrite, etc.), because -/// C++ streams does not offer any easy way to flush their contents, like -/// fflush() and fsync() does. -/// -/// IPv4 address is used as a key in the hash. -class memfile_LeaseMgr { -public: - - /// A hash table for Lease4 leases. - typedef std::map IPv4Hash; - - /// An iterator for Lease4 hash table. - typedef std::map::iterator leaseIt; - - /// @brief The sole memfile lease manager constructor - /// - /// @param filename name of the lease file (will be overwritten) - /// @param sync should operations be - memfile_LeaseMgr(const std::string& filename, bool sync); - - /// @brief Destructor (closes file) - ~memfile_LeaseMgr(); - - /// @brief adds a lease to the hash - /// - /// @param lease lease to be added - bool addLease(Lease4Ptr lease); - - /// @brief returns existing lease - /// - /// @param addr address of the searched lease - /// - /// @return smart pointer to the lease (or NULL if lease is not found) - Lease4Ptr getLease(uint32_t addr); - - /// @brief Simplified lease update. - /// - /// Searches for a lease and then updates its client last transmission - /// time. Writes new lease content to lease file (and calls fflush()/fsync(), - /// if synchronous operation is enabled). - /// - /// @param addr IPv4 address - /// @param new_cltt New client last transmission time - /// - /// @return pointer to the updated lease (or NULL) - Lease4Ptr updateLease(uint32_t addr, uint32_t new_cltt); - - /// @brief Deletes a lease. - /// - /// @param addr IPv4 address of the lease to be deleted. - /// - /// @return true if deletion was successful, false if no such lease exists - bool deleteLease(uint32_t addr); - -protected: - - /// @brief Writes updated lease to a file. - /// - /// @param lease lease to be written - void writeLease(Lease4Ptr lease); - - /// Name of the lease file. - std::string filename_; - - /// should we do flush after each operation? - bool sync_; - - /// File handle to the open lease file. - FILE * file_; - - /// Hash table for IPv4 leases - IPv4Hash ip4Hash_; -}; - -memfile_LeaseMgr::memfile_LeaseMgr(const std::string& filename, bool sync) - : filename_(filename), sync_(sync) { - file_ = fopen(filename.c_str(), "w"); - if (!file_) { - throw "Failed to create file " + filename; - } - -} - -memfile_LeaseMgr::~memfile_LeaseMgr() { - fclose(file_); -} - -void memfile_LeaseMgr::writeLease(Lease4Ptr lease) { - fprintf(file_, "lease %d {\n hw-addr ", lease->addr); - for (std::vector::const_iterator it = lease->hwaddr.begin(); - it != lease->hwaddr.end(); ++it) { - fprintf(file_, "%02x:", *it); - } - fprintf(file_, ";\n client-id "); - for (std::vector::const_iterator it = lease->client_id.begin(); - it != lease->client_id.end(); ++it) { - fprintf(file_, "%02x:", *it); - } - fprintf(file_, ";\n valid-lifetime %d;\n recycle-time %d;\n" - " cltt %d;\n pool-id %d;\n fixed %s; hostname %s;\n" - " fqdn_fwd %s;\n fqdn_rev %s;\n};\n", - lease->valid_lft, lease->recycle_time, (int)lease->cltt, - lease->pool_id, lease->fixed?"true":"false", - lease->hostname.c_str(), lease->fqdn_fwd?"true":"false", - lease->fqdn_rev?"true":"false"); - - if (sync_) { - fflush(file_); - fsync(fileno(file_)); - } -} - -bool memfile_LeaseMgr::addLease(Lease4Ptr lease) { - if (ip4Hash_.find(lease->addr) != ip4Hash_.end()) { - // there is such an address already in the hash - return false; - } - ip4Hash_.insert(pair(lease->addr, lease)); - lease->hostname = "add"; - writeLease(lease); - return (true); -} - -Lease4Ptr memfile_LeaseMgr::getLease(uint32_t addr) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - return x->second; // found - } - - // not found - return Lease4Ptr(); -} - -Lease4Ptr memfile_LeaseMgr::updateLease(uint32_t addr, uint32_t new_cltt) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - x->second->cltt = new_cltt; - x->second->hostname = "update"; - writeLease(x->second); - return x->second; - } - return Lease4Ptr(); -} - -bool memfile_LeaseMgr::deleteLease(uint32_t addr) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - x->second->hostname = "delete"; - writeLease(x->second); - ip4Hash_.erase(x); - return true; - } - return false; -} - -memfile_uBenchmark::memfile_uBenchmark(const string& filename, - uint32_t num_iterations, - bool sync, - bool verbose) - :uBenchmark(num_iterations, filename, sync, verbose) { -} - -void memfile_uBenchmark::connect() { - try { - leaseMgr_ = new memfile_LeaseMgr(dbname_, sync_); - } catch (const std::string& e) { - failure(e.c_str()); - } -} - -void memfile_uBenchmark::disconnect() { - delete leaseMgr_; - leaseMgr_ = NULL; -} - -void memfile_uBenchmark::createLease4Test() { - if (!leaseMgr_) { - throw "No LeaseMgr instantiated."; - } - - uint32_t addr = BASE_ADDR4; // Let's start with 1.0.0.0 address - const uint8_t hwaddr_len = 20; // Not a real field - char hwaddr_tmp[hwaddr_len]; - const uint8_t client_id_len = 128; - char client_id_tmp[client_id_len]; - uint32_t valid_lft = 1000; // We can use the same value for all leases - uint32_t recycle_time = 0; // Not supported in any foreseeable future, - // so keep this as 0 - time_t cltt = time(NULL); // Timestamp - uint32_t pool_id = 0; // Let's use pools 0-99 - bool fixed = false; - string hostname("foo"); // Will generate it dynamically - bool fqdn_fwd = true; // Let's pretend to do AAAA update - bool fqdn_rev = true; // Let's pretend to do PTR update - - cout << "CREATE: "; - - // While we could put the data directly into vector, I would like to - // keep the code as similar to other benchmarks as possible - for (uint8_t i = 0; i < hwaddr_len; ++i) { - hwaddr_tmp[i] = 'A' + i; // let's make hwaddr consisting of letter - } - vector hwaddr(hwaddr_tmp, hwaddr_tmp + hwaddr_len - 1); - - for (uint8_t i = 0; i < client_id_len; i++) { - client_id_tmp[i] = 33 + i; // 33 is being the first, non whitespace - // printable ASCII character - } - vector client_id(client_id_tmp, client_id_tmp + client_id_len - 1); - - for (uint32_t i = 0; i < num_; ++i) { - - cltt++; - - Lease4Ptr lease = boost::shared_ptr(new Lease4()); - lease->addr = addr; - lease->hwaddr = hwaddr; - lease->client_id = client_id; - lease->valid_lft = valid_lft; - lease->recycle_time = recycle_time; - lease->cltt = cltt; - lease->pool_id = pool_id; - lease->fixed = fixed; - lease->hostname = hostname; - lease->fqdn_fwd = fqdn_fwd; - lease->fqdn_rev = fqdn_rev; - - if (!leaseMgr_->addLease(lease)) { - failure("addLease() failed"); - } else { - if (verbose_) { - cout << "."; - } - }; - - addr++; - } - cout << endl; -} - -void memfile_uBenchmark::searchLease4Test() { - if (!leaseMgr_) { - throw "No LeaseMgr instantiated."; - } - - cout << "RETRIEVE: "; - - for (uint32_t i = 0; i < num_; i++) { - uint32_t x = BASE_ADDR4 + random() % int(num_ / hitratio_); - - Lease4Ptr lease = leaseMgr_->getLease(x); - if (verbose_) { - cout << (lease?".":"X"); - } - } - - cout << endl; -} - -void memfile_uBenchmark::updateLease4Test() { - if (!leaseMgr_) { - throw "No LeaseMgr instantiated."; - } - - cout << "UPDATE: "; - - time_t cltt = time(NULL); - - for (uint32_t i = 0; i < num_; i++) { - - uint32_t x = BASE_ADDR4 + random() % num_; - - Lease4Ptr lease = leaseMgr_->updateLease(x, cltt); - if (!lease) { - stringstream tmp; - tmp << "UPDATE failed for lease " << hex << x << dec; - failure(tmp.str().c_str()); - } - if (verbose_) { - cout << "."; - } - } - - cout << endl; -} - -void memfile_uBenchmark::deleteLease4Test() { - if (!leaseMgr_) { - throw "No LeaseMgr instantiated."; - } - - cout << "DELETE: "; - - for (uint32_t i = 0; i < num_; i++) { - - uint32_t x = BASE_ADDR4 + i; - - if (!leaseMgr_->deleteLease(x)) { - stringstream tmp; - tmp << "UPDATE failed for lease " << hex << x << dec; - failure(tmp.str().c_str()); - } - if (verbose_) { - cout << "."; - } - } - - cout << endl; -} - -void memfile_uBenchmark::printInfo() { - cout << "Memory db (using std::map) + write-only file." << endl; -} - - -int main(int argc, char * const argv[]) { - - const char * filename = "dhcpd.leases"; - uint32_t num = 100; - bool sync = true; - bool verbose = false; - - memfile_uBenchmark bench(filename, num, sync, verbose); - - bench.parseCmdline(argc, argv); - - int result = bench.run(); - - return (result); -} diff --git a/tests/tools/dhcp-ubench/memfile_ubench.h b/tests/tools/dhcp-ubench/memfile_ubench.h deleted file mode 100644 index 51b73134fc..0000000000 --- a/tests/tools/dhcp-ubench/memfile_ubench.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include -#include -#include -#include "benchmark.h" - -/// @brief Structure of the Lease4 that is kept in memory -struct Lease4 { - uint32_t addr; /// IPv4 address - std::vector hwaddr; /// hardware address - std::vector client_id; /// client-identifier - uint32_t valid_lft; /// valid lifetime timestamp - uint32_t recycle_time; /// timer for keeping lease after expiration/release - /// (currently not used) - time_t cltt; /// client last transmission time - uint32_t pool_id; /// ID of the pool the lease belongs to - bool fixed; /// is this lease fixed? - std::string hostname; /// client hostname (may be empty) - bool fqdn_fwd; /// did we do AAAA update for this lease? - bool fqdn_rev; /// did we do PTR update for this lease? - std::string options; /// additional options stored with this lease - /// (currently not used) - std::string comments; /// comments on that lease - /// (currently not used) -}; - -/// Pointer to a Lease4 structure -typedef boost::shared_ptr Lease4Ptr; - -/// an implementation of in-memory+file database -/// The actual implementation is in memfile_ubench.cc -class memfile_LeaseMgr; - -/// @brief In-memory + file micro-benchmark. -/// -/// That is a specific backend implementation. See \ref uBenchmark class for -/// detailed explanation of its operations. This class uses custom in-memory -/// pseudo-database and external write-only lease file. That approach simulates -/// modernized model of ISC DHCP4. It uses standard STL maps together with -/// shared_ptr from boost library. The "database" is implemented in the Lease -/// Manager (see \ref LeaseMgr in memfile_ubench.cc). All lease changes are -/// appended to the end of the file, speeding up the process. -class memfile_uBenchmark: public uBenchmark { -public: - - /// @brief The sole memfile benchmark constructor. - /// - /// @param filename name of the write-only lease file - /// @param num_iterations number of iterations - /// @param sync should fsync() be called after every file write? - /// @param verbose would you like extra logging? - memfile_uBenchmark(const std::string& filename, - uint32_t num_iterations, bool sync, bool verbose); - - /// @brief Prints backend info. - virtual void printInfo(); - - /// @brief Spawns lease manager that create empty lease file, initializes - /// empty STL maps. - virtual void connect(); - - /// @brief Delete lease manager that closes lease file. - virtual void disconnect(); - - /// @brief Creates new leases. - /// - /// See uBenchmark::createLease4Test() for detailed explanation. - virtual void createLease4Test(); - - /// @brief Searches for existing leases. - /// - /// See uBenchmark::searchLease4Test() for detailed explanation. - virtual void searchLease4Test(); - - /// @brief Updates existing leases. - /// - /// See uBenchmark::updateLease4Test() for detailed explanation. - virtual void updateLease4Test(); - - /// @brief Deletes existing leases. - /// - /// See uBenchmark::deleteLease4Test() for detailed explanation. - virtual void deleteLease4Test(); - -protected: - - /// Lease Manager (concrete backend implementation, based on STL maps) - memfile_LeaseMgr * leaseMgr_; -}; diff --git a/tests/tools/dhcp-ubench/mysql.schema b/tests/tools/dhcp-ubench/mysql.schema deleted file mode 100644 index ed01e46cf1..0000000000 --- a/tests/tools/dhcp-ubench/mysql.schema +++ /dev/null @@ -1,86 +0,0 @@ -DROP DATABASE kea; -CREATE DATABASE kea; - -CONNECT kea; - -CREATE TABLE lease4 ( - - # Primary key (serial = BININT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE) - lease_id SERIAL, - addr INT UNSIGNED UNIQUE, - - # The largest hardware address is for Infiniband (20 bytes) - hwaddr VARCHAR(20), - - # The largest client-id is DUID in DHCPv6 - up to 128 bytes - client_id VARCHAR(128), - - # Expressed in seconds - valid_lft INT, - - # Expressed in seconds, - recycle_time INT DEFAULT 0, - - cltt TIMESTAMP, - - pool_id int, - - fixed BOOL, - - # DDNS stuff - hostname VARCHAR(255), - fqdn_fwd BOOL DEFAULT false, - fqdn_rev BOOL DEFAULT false, - - options TEXT, - comments TEXT -); - -CREATE TABLE lease6 ( - - # Primary key (serial = BININT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE) - lease_id SERIAL, - addr CHAR(16) BYTE UNIQUE, - - # The largest hardware address is for Infiniband (20 bytes) - hwaddr VARCHAR(20), - - # The largest client-id is DUID in DHCPv6 - up to 128 bytes - client_id VARCHAR(128), - - iaid int unsigned, - - # Used for IA_PD only (tinyint = 0..255) - prefix_len TINYINT unsigned, - - # Expressed in seconds - preferred_lft INT, - - # Expressed in seconds - valid_lft INT, - - # Expressed in seconds, - recycle_time INT DEFAULT 0, - - cltt TIMESTAMP, - - pool_id int, - - fixed BOOL DEFAULT false, - - hostname VARCHAR(255), - fqdn_fwd BOOL DEFAULT false, - fqdn_rev BOOL DEFAULT false, - - options TEXT, - comments TEXT -); - -CREATE TABLE host ( - address BIGINT NULL, - address6 BIGINT NULL, - prefix6 BIGINT NULL, - hostname VARCHAR(255), - options TEXT, - comments TEXT -); \ No newline at end of file diff --git a/tests/tools/dhcp-ubench/mysql_ubench.cc b/tests/tools/dhcp-ubench/mysql_ubench.cc deleted file mode 100644 index b2f58c2e72..0000000000 --- a/tests/tools/dhcp-ubench/mysql_ubench.cc +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include -#include -#include -#include -#include -#include - -#include "benchmark.h" -#include "mysql_ubench.h" - -using namespace std; - -MySQL_uBenchmark::MySQL_uBenchmark(const string& hostname, const string& user, - const string& pass, const string& db, - uint32_t num_iterations, bool sync, - bool verbose) - :uBenchmark(num_iterations, db, sync, verbose, hostname, user, pass), - conn_(NULL) { - -} - -void MySQL_uBenchmark::stmt_failure(MYSQL_STMT * stmt, const char* operation) { - stringstream tmp; - tmp << "Error " << mysql_stmt_errno(stmt) << " during " << operation - << ": " << mysql_stmt_error(stmt); - throw tmp.str(); -} - - - -void MySQL_uBenchmark::failure(const char* operation) { - stringstream tmp; - tmp << "Error " << mysql_errno(conn_) << " during " << operation - << ": " << mysql_error(conn_); - throw tmp.str(); -} - -void MySQL_uBenchmark::connect() { - - conn_ = mysql_init(NULL); - if (!conn_) { - failure("initializing MySQL library"); - } else { - cout << "MySQL library init successful." << endl; - } - - if (!mysql_real_connect(conn_, hostname_.c_str(), user_.c_str(), - passwd_.c_str(), dbname_.c_str(), 0, NULL, 0)) { - failure("connecting to MySQL server"); - } else { - cout << "MySQL connection established." << endl; - } - - string q = "delete from lease4;"; - if (mysql_real_query(conn_, q.c_str(), strlen(q.c_str()))) { - failure("dropping old lease4 entries."); - } - - q = "ALTER TABLE lease4 engine="; - if (sync_) { - q += "InnoDB"; - } else { - q += "MyISAM"; - } - if (mysql_query(conn_, q.c_str())) { - q = "Failed to run query:" + q; - failure(q.c_str()); - } -} - -void MySQL_uBenchmark::disconnect() { - if (!conn_) { - throw "NULL MySQL connection pointer."; - } - mysql_close(conn_); - conn_ = NULL; -} - -void MySQL_uBenchmark::createLease4Test() { - if (!conn_) { - throw "Not connected to MySQL server."; - } - - uint32_t addr = BASE_ADDR4; // Let's start with 1.0.0.0 address - char hwaddr[20]; - size_t hwaddr_len = 20; // Not a real field - char client_id[128]; - size_t client_id_len = 128; - uint32_t valid_lft = 1000; // We can use the same value for all leases - uint32_t recycle_time = 7; // not supported in any foresable future, - - char cltt[48]; // timestamp (specified as text) - size_t cltt_len; - - sprintf(cltt, "2012-07-11 15:43:00"); - cltt_len = strlen(cltt); - - uint32_t pool_id = 1000; // Let's use pool-ids greater than zero - bool fixed = false; - - char hostname[] = "foo"; // Will generate it dynamically - size_t hostname_len; - hostname_len = strlen(hostname); - - bool fqdn_fwd = true; // Let's pretend to do AAAA update - bool fqdn_rev = true; // Let's pretend to do PTR update - - cout << "CREATE: "; - - for (uint8_t i = 0; i < hwaddr_len; i++) { - hwaddr[i] = 'A' + i; // let's make hwaddr consisting of letters - } - hwaddr[19] = 0; // make it is null-terminated - - for (uint8_t i = 0; i < client_id_len; i++) { - client_id[i] = 33 + i; // 33 is being the first, non whitespace - // printable ASCII character - } - client_id[127] = 0; // make it is null-terminated - - MYSQL_STMT * stmt = NULL; - MYSQL_BIND bind[11]; // 11 parameters in the insert statement - - if (compiled_stmt_) { - // create a statement once - stmt = mysql_stmt_init(conn_); - if (!stmt) { - failure("Unable to create compiled statement, mysql_stmt_init() failed"); - } - - const char * statement = "INSERT INTO lease4(addr,hwaddr,client_id," - "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," - "fqdn_fwd,fqdn_rev) VALUES(?,?,?,?,?,?,?,?,?,?,?)"; - - if (mysql_stmt_prepare(stmt, statement, strlen(statement) )) { - failure("Failed to prepare statement, mysql_stmt_prepare() returned non-zero"); - } - int param_cnt = mysql_stmt_param_count(stmt); - if (param_cnt != 11) { - failure("Parameter count sanity check failed."); - } - - memset(bind, 0, sizeof(bind)); - - // 1st parameter: IPv4 address - bind[0].buffer_type = MYSQL_TYPE_LONG; - bind[0].buffer = (&addr); - bind[0].is_null = 0; - bind[0].length = 0; - - // 2nd parameter: Hardware address - bind[1].buffer_type = MYSQL_TYPE_VARCHAR; - bind[1].buffer = hwaddr; - bind[1].buffer_length = hwaddr_len; - bind[1].is_null = 0; - bind[1].length = &hwaddr_len; - - // 3rd parameter: Client-id - bind[2].buffer_type = MYSQL_TYPE_VARCHAR; - bind[2].buffer = client_id; - bind[2].buffer_length = client_id_len; - bind[2].is_null = 0; - bind[2].length = &client_id_len; - - // 4th parameter: valid-lifetime - bind[3].buffer_type = MYSQL_TYPE_LONG; - bind[3].buffer = (&valid_lft); - bind[3].is_null = 0; - bind[3].length = 0; - - // 5th parameter: recycle-time - bind[4].buffer_type = MYSQL_TYPE_LONG; - bind[4].buffer = (&recycle_time); - bind[4].is_null = 0; - bind[4].length = 0; - - // 6th parameter: cltt - bind[5].buffer_type = MYSQL_TYPE_TIMESTAMP; - bind[5].buffer = cltt; - bind[2].buffer_length = cltt_len; - bind[5].is_null = 0; - bind[5].length = &cltt_len; - - // 7th parameter: pool-id - bind[6].buffer_type = MYSQL_TYPE_LONG; - bind[6].buffer = &pool_id; - bind[6].is_null = 0; - bind[6].length = 0; - - // 8th parameter: fixed - bind[7].buffer_type = MYSQL_TYPE_TINY; - bind[7].buffer = &fixed; - bind[7].is_null = 0; - bind[7].length = 0; - - // 9th parameter: hostname - bind[8].buffer_type = MYSQL_TYPE_VARCHAR; - bind[8].buffer = hostname; - bind[8].buffer_length = strlen(hostname); - bind[8].is_null = 0; - bind[8].length = &hostname_len; - - // 10th parameter: fqdn_fwd - bind[9].buffer_type = MYSQL_TYPE_TINY; - bind[9].buffer = &fqdn_fwd; - bind[9].is_null = 0; - bind[9].length = 0; - - // 11th parameter: fqdn_rev - bind[10].buffer_type = MYSQL_TYPE_TINY; - bind[10].buffer = &fqdn_rev; - bind[10].is_null = 0; - bind[10].length = 0; - } - - for (uint32_t i = 0; i < num_; i++) { - - sprintf(cltt, "2012-07-11 15:43:%02d", i % 60); - - - addr++; - - if (!compiled_stmt_) { - // the first address is 1.0.0.0. - char query[2000], * end; - strcpy(query, "INSERT INTO lease4(addr,hwaddr,client_id," - "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," - "fqdn_fwd,fqdn_rev) VALUES("); - end = query + strlen(query); - end += sprintf(end, "%u,\'", addr); - end += mysql_real_escape_string(conn_, end, hwaddr, hwaddr_len); - end += sprintf(end,"\',\'"); - end += mysql_real_escape_string(conn_, end, client_id, client_id_len); - end += sprintf(end, "\',%d,%d,'%s',%d,%s,\'%s\',%s,%s);", - valid_lft, recycle_time, cltt, - pool_id, (fixed?"true":"false"), hostname, - (fqdn_fwd?"true":"false"), (fqdn_rev?"true":"false")); - // lease_id field is set automatically - // options and comments fields are not set - - unsigned int len = end - query; - if (mysql_real_query(conn_, query, len)) { - // something failed. - failure("INSERT query"); - } - } else { - // compiled statement - - if (mysql_stmt_bind_param(stmt, bind)) { - failure("Failed to bind parameters: mysql_stmt_bind_param() returned non-zero"); - } - - if (mysql_stmt_execute(stmt)) { - failure("Failed to execute statement: mysql_stmt_execute() returned non-zero"); - } - - } - - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - if (mysql_stmt_close(stmt)) { - failure("Failed to close compiled statement, mysql_stmt_close returned non-zero"); - } - } - - cout << endl; -} - -void MySQL_uBenchmark::searchLease4Test() { - if (!conn_) { - throw "Not connected to MySQL server."; - } - - cout << "RETRIEVE: "; - - uint32_t addr = 0; - - MYSQL_STMT * stmt = NULL; - MYSQL_BIND bind[1]; // just a single element - if (compiled_stmt_) { - stmt = mysql_stmt_init(conn_); - if (!stmt) { - failure("Unable to create compiled statement"); - } - const char * statement = "SELECT lease_id,addr,hwaddr,client_id," - "valid_lft, cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " - "FROM lease4 where addr=?"; - if (mysql_stmt_prepare(stmt, statement, strlen(statement))) { - failure("Failed to prepare statement, mysql_stmt_prepare() returned non-zero"); - } - int param_cnt = mysql_stmt_param_count(stmt); - if (param_cnt != 1) { - failure("Parameter count sanity check failed."); - } - - memset(bind, 0, sizeof(bind)); - - // 1st parameter: IPv4 address - bind[0].buffer_type = MYSQL_TYPE_LONG; - bind[0].buffer = (&addr); - bind[0].is_null = 0; - bind[0].length = 0; - } - - for (uint32_t i = 0; i < num_; i++) { - - addr = BASE_ADDR4 + random() % int(num_ / hitratio_); - - if (!compiled_stmt_) { - char query[512]; - sprintf(query, "SELECT lease_id,addr,hwaddr,client_id,valid_lft," - "cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " - "FROM lease4 where addr=%d", addr); - mysql_real_query(conn_, query, strlen(query)); - - MYSQL_RES * result = mysql_store_result(conn_); - - int num_rows = mysql_num_rows(result); - int num_fields = mysql_num_fields(result); - - if ( (num_rows > 1) ) { - stringstream tmp; - tmp << "Search: DB returned " << num_rows << " leases for address " - << hex << addr << dec; - failure(tmp.str().c_str()); - } - - if (num_rows) { - if (num_fields == 0) { - failure("Query returned empty set"); - } - - MYSQL_ROW row = mysql_fetch_row(result); - - // pretend to do something with it - if (row[0] == NULL) { - failure("SELECT returned NULL data."); - } - mysql_free_result(result); - - if (verbose_) { - cout << "."; // hit - } - - } else { - if (verbose_) { - cout << "x"; // miss - } - } - } else { - // compiled statement - - if (mysql_stmt_bind_param(stmt, bind)) { - failure("Failed to bind parameters: mysql_stmt_bind_param() returned non-zero"); - } - - if (mysql_stmt_execute(stmt)) { - failure("Failed to execute statement: mysql_stmt_execute() returned non-zero"); - } - - MYSQL_BIND response[11]; - - size_t length[11]; - my_bool is_null[11]; - my_bool error[11]; - - uint32_t lease_id; - uint32_t lease_addr; - char hwaddr[20]; - char client_id[128]; - uint32_t valid_lft; // We can use the same value for all leases - MYSQL_TIME cltt; - uint32_t pool_id; - my_bool fixed; - char hostname[255]; - my_bool fqdn_fwd; - my_bool fqdn_rev; - - for (int j = 0; j < 11; j++) { - response[j].is_null = &is_null[j]; - response[j].length = &length[j]; - response[j].error = &error[j]; - } - - // 1th parameter: lease_id - response[0].buffer_type = MYSQL_TYPE_LONG; - response[0].buffer = (&lease_id); - - // 2nd parameter: IPv4 address - response[1].buffer_type = MYSQL_TYPE_LONG; - response[1].buffer = (&lease_addr); - - // 3rd parameter: Hardware address - response[2].buffer_type = MYSQL_TYPE_STRING; - response[2].buffer = hwaddr; - response[2].buffer_length = sizeof(hwaddr); - - // 4th parameter: Client-id - response[3].buffer_type = MYSQL_TYPE_STRING; - response[3].buffer = &client_id; - response[3].buffer_length = sizeof(client_id); - - // 5th parameter: valid-lifetime - response[4].buffer_type = MYSQL_TYPE_LONG; - response[4].buffer = &valid_lft; - - // 6th parameter: cltt - response[5].buffer_type = MYSQL_TYPE_TIMESTAMP; - response[5].buffer = &cltt; - - // 7th parameter: pool-id - response[6].buffer_type = MYSQL_TYPE_LONG; - response[6].buffer = &pool_id; - - // 8th parameter: fixed - response[7].buffer_type = MYSQL_TYPE_TINY; - response[7].buffer = &fixed; - - // 9th parameter: hostname - response[8].buffer_type = MYSQL_TYPE_STRING; - response[8].buffer = &hostname; - - // 10th parameter: fqdn_fwd - response[9].buffer_type = MYSQL_TYPE_TINY; - response[9].buffer = &fqdn_fwd; - - // 11th parameter: fqdn_rev - response[10].buffer_type = MYSQL_TYPE_TINY; - response[10].buffer = &fqdn_rev; - - if (mysql_stmt_bind_result(stmt, response)) - { - cout << "Error:" << mysql_stmt_error(stmt) << endl; - failure("mysql_stmt_bind_result() failed"); - } - int num_rows = 0; - - int result = mysql_stmt_fetch(stmt); - switch (result) { - case 0: { - if (lease_addr != addr) { - failure("Returned data is bogus!"); - } - num_rows++; - break; - } - case MYSQL_NO_DATA: - { - // that's ok. We randomized non-existing address - break; - - } - default: { - stmt_failure(stmt, "RETRIEVE (mysql_stmt_fetch())"); - } - } - - // we could call mysql_stmt_fetch again to check that there are no - // other data for us. But there should be exactly one row of data - // with specified address. - - if (num_rows) { - if (verbose_) { - cout << "."; // hit - } - } else { - if (verbose_) { - cout << "X"; // miss - } - } - - } - } - - if (compiled_stmt_) { - if (mysql_stmt_close(stmt)) { - failure("Failed to close compiled statement, mysql_stmt_close returned non-zero"); - } - } - - cout << endl; -} - -void MySQL_uBenchmark::updateLease4Test() { - if (!conn_) { - throw "Not connected to MySQL server."; - } - - cout << "UPDATE: "; - - uint32_t valid_lft = 1002; // just some dummy value - char cltt[] = "now()"; - size_t cltt_len = strlen(cltt); - uint32_t addr = 0; - - MYSQL_STMT * stmt = NULL; - MYSQL_BIND bind[3]; - if (compiled_stmt_) { - stmt = mysql_stmt_init(conn_); - if (!stmt) { - failure("Unable to create compiled statement"); - } - const char * statement = "UPDATE lease4 SET valid_lft=?, cltt=? WHERE addr=?"; - if (mysql_stmt_prepare(stmt, statement, strlen(statement))) { - failure("Failed to prepare statement, mysql_stmt_prepare() returned non-zero"); - } - int param_cnt = mysql_stmt_param_count(stmt); - if (param_cnt != 3) { - failure("Parameter count sanity check failed."); - } - - memset(bind, 0, sizeof(bind)); - - // 1st parameter: valid lifetime - bind[0].buffer_type = MYSQL_TYPE_LONG; - bind[0].buffer = &valid_lft; - - // 2nd parameter: cltt - bind[1].buffer_type = MYSQL_TYPE_STRING; - bind[1].buffer = &cltt; - bind[1].buffer_length = cltt_len; - - bind[2].buffer_type = MYSQL_TYPE_LONG; - bind[2].buffer = &addr; - } - - - for (uint32_t i = 0; i < num_; i++) { - - addr = BASE_ADDR4 + random() % num_; - - if (!compiled_stmt_) { - char query[128]; - sprintf(query, "UPDATE lease4 SET valid_lft=1002, cltt=now() WHERE addr=%d", addr); - mysql_real_query(conn_, query, strlen(query)); - - } else { - // compiled statement - if (mysql_stmt_bind_param(stmt, bind)) { - failure("Failed to bind parameters: mysql_stmt_bind_param() returned non-zero"); - } - - if (mysql_stmt_execute(stmt)) { - failure("Failed to execute statement: mysql_stmt_execute() returned non-zero"); - } - } - - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - if (mysql_stmt_close(stmt)) { - failure("Failed to close compiled statement, mysql_stmt_close returned non-zero"); - } - } - - cout << endl; -} - -void MySQL_uBenchmark::deleteLease4Test() { - if (!conn_) { - throw "Not connected to MySQL server."; - } - - cout << "DELETE: "; - - uint32_t addr = 0; - - MYSQL_STMT * stmt = NULL; - MYSQL_BIND bind[1]; // just a single element - if (compiled_stmt_) { - - stmt = mysql_stmt_init(conn_); - if (!stmt) { - failure("Unable to create compiled statement, mysql_stmt_init() failed"); - } - - const char * statement = "DELETE FROM lease4 WHERE addr=?"; - - if (mysql_stmt_prepare(stmt, statement, strlen(statement) )) { - failure("Failed to prepare statement, mysql_stmt_prepare() returned non-zero"); - } - int param_cnt = mysql_stmt_param_count(stmt); - if (param_cnt != 1) { - failure("Parameter count sanity check failed."); - } - - memset(bind, 0, sizeof(bind)); - - // 1st parameter: IPv4 address - bind[0].buffer_type = MYSQL_TYPE_LONG; - bind[0].buffer = (&addr); - bind[0].is_null = 0; - bind[0].length = 0; - } - - - for (uint32_t i = 0; i < num_; i++) { - - addr = BASE_ADDR4 + i; - - if (!compiled_stmt_) { - char query[128]; - sprintf(query, "DELETE FROM lease4 WHERE addr=%d", addr); - mysql_real_query(conn_, query, strlen(query)); - } else { - // compiled statement - if (mysql_stmt_bind_param(stmt, bind)) { - failure("Failed to bind parameters: mysql_stmt_bind_param() returned non-zero"); - } - - if (mysql_stmt_execute(stmt)) { - failure("Failed to execute statement: mysql_stmt_execute() returned non-zero"); - } - } - - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - if (mysql_stmt_close(stmt)) { - failure("Failed to close compiled statement, mysql_stmt_close returned non-zero"); - } - } - - cout << endl; -} - -void MySQL_uBenchmark::printInfo() { - cout << "MySQL client version is " << mysql_get_client_info() << endl; -} - - -int main(int argc, char * const argv[]) { - - const char* hostname ="localhost"; // -m (MySQL server) - const char* user = "root"; // -u - const char* passwd = "secret"; // -p - const char* dbname = "kea"; // -f - uint32_t num = 100; // -n - bool sync = true; // -s - bool verbose = true; // -v - - MySQL_uBenchmark bench(hostname, user, passwd, dbname, num, sync, verbose); - - bench.parseCmdline(argc, argv); - - int result = bench.run(); - - return (result); -} diff --git a/tests/tools/dhcp-ubench/mysql_ubench.h b/tests/tools/dhcp-ubench/mysql_ubench.h deleted file mode 100644 index be12fd6fd1..0000000000 --- a/tests/tools/dhcp-ubench/mysql_ubench.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include "benchmark.h" - -/// @brief MySQL micro-benchmark. -/// -/// That is a specific backend implementation. See \ref uBenchmark class for -/// detailed explanation of its operations. This class uses MySQL as database -/// backend. -class MySQL_uBenchmark: public uBenchmark { -public: - - /// @brief The sole MySQL micro-benchmark constructor - /// - /// To avoid influence of network performance, it is highly recommended - /// to run MySQL engine on the same host as benchmark. Thus hostname - /// is likely to be "localhost". Make sure that the selected database - /// is already created and that it follows expected schema. See mysql.schema - /// and isc-dhcp-perf-guide.html for details. - /// - /// Synchronous operation means using InnDB, async is MyISAM. - /// - /// @param hostname Name of the host to connect to - /// @param user usename used during MySQL connection - /// @param pass password used during MySQL connection - /// @param db name of the database to connect to - /// @param num_iterations number of iterations for basic operations - /// @param sync synchronous or asynchronous database writes - /// @param verbose should extra information be logged? - MySQL_uBenchmark(const std::string& hostname, const std::string& user, - const std::string& pass, const std::string& db, - uint32_t num_iterations, bool sync, - bool verbose); - - /// @brief Prints MySQL version info. - virtual void printInfo(); - - /// @brief Opens connection to the MySQL database. - virtual void connect(); - - /// @brief Closes connection to the MySQL database. - virtual void disconnect(); - - /// @brief Creates new leases. - /// - /// See uBenchmark::createLease4Test() for detailed explanation. - virtual void createLease4Test(); - - /// @brief Searches for existing leases. - /// - /// See uBenchmark::searchLease4Test() for detailed explanation. - virtual void searchLease4Test(); - - /// @brief Updates existing leases. - /// - /// See uBenchmark::updateLease4Test() for detailed explanation. - virtual void updateLease4Test(); - - /// @brief Deletes existing leases. - /// - /// See uBenchmark::deleteLease4Test() for detailed explanation. - virtual void deleteLease4Test(); - -protected: - /// @brief Used to report any database failures. - /// - /// Compared to its base version in uBenchmark class, this one logs additional - /// MySQL specific information using mysql_errno() and mysql_error() functions. - /// The outcome is the same: exception is thrown. - /// - /// @param operation brief description of the operation that caused error - /// - /// @sa stmt_failure() - void failure(const char* operation); - - /// @brief Used to report compiled statement failures. - /// - /// Compared to its base version in uBenchmark class, this one logs additional - /// MySQL specific information using mysql_stmt_errno() and mysql_stmt_error() - /// functions that are used for compiled statements error reporting. - /// - /// @param stmt MySQL compiled statement structure - /// @param operation brief description of the operation that caused error - /// - /// @sa failure() - void stmt_failure(MYSQL_STMT * stmt, const char* operation); - - - /// Handle to MySQL database connection. - MYSQL* conn_; -}; diff --git a/tests/tools/dhcp-ubench/performance-results-graph1.png b/tests/tools/dhcp-ubench/performance-results-graph1.png deleted file mode 100644 index a173aa6a832746f149ed71752adb6aff4152043b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38873 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

|a5))xXKQzwN&_-TwnyF9Z(!%^dz46#V`C7aE8b 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 N=ri#_p|4?j2k0O5*!UO@-QM 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�Vc00tY;;{4=>8;2BHUkpUF8@usZnIOwl{My< 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 DhBLik diff --git a/tests/tools/dhcp-ubench/performance-results.ods b/tests/tools/dhcp-ubench/performance-results.ods deleted file mode 100644 index b9c68bf93a37f79ce112a3f3cfe3597e6f00de96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48268 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 smoZiR&Taqd_xm96GOGVF!}@<1 -#include -#include -#include -#include -#include -#include -#include "sqlite_ubench.h" - -using namespace std; - -SQLite_uBenchmark::SQLite_uBenchmark(const string& filename, - uint32_t num_iterations, - bool sync, bool verbose) - :uBenchmark(num_iterations, filename, sync, verbose), - db_(NULL) { - -} - -void SQLite_uBenchmark::connect() { - int result = sqlite3_open(dbname_.c_str(), &db_); - if (result != SQLITE_OK) { - failure("Failed to open DB file"); - } - - result = sqlite3_exec(db_, "DELETE FROM lease4", NULL, NULL, NULL); - if (result != SQLITE_OK) { - failure("Failed to delete old entries"); - } - - if (sync_) { - sqlite3_exec(db_, "PRAGMA synchronous = ON", NULL, NULL, NULL); - } else { - sqlite3_exec(db_, "PRAGMA synchronous = OFF", NULL, NULL, NULL); - } - - // see http://www.sqlite.org/pragma.html#pragma_journal_mode - // for detailed explanation. Available modes: DELETE, TRUNCATE, - // PERSIST, MEMORY, WAL, OFF - sqlite3_exec(db_, "PRAGMA journal_mode = OFF", NULL, NULL, NULL); -} - -void SQLite_uBenchmark::disconnect() { - if (db_) { - sqlite3_close(db_); - db_ = NULL; - } else { - throw "Can't close SQLite connection: it was never open."; - } -} - -void SQLite_uBenchmark::createLease4Test() { - if (!db_) { - throw "SQLite connection is closed."; - } - - uint32_t addr = BASE_ADDR4; // Let's start with 1.0.0.0 address - const uint8_t hwaddr_len = 20; // Not a real field - char hwaddr[hwaddr_len]; - const uint8_t client_id_len = 128; - char client_id[client_id_len]; - uint32_t valid_lft = 1000; // We can use the same value for all leases - uint32_t recycle_time = 0; // Not supported in any foresable future, - // so keep this as 0 - char cltt[48]; // Timestamp - uint32_t pool_id = 0; // Let's use pools 0-99 - bool fixed = false; - string hostname("foo"); // Will generate it dynamically - bool fqdn_fwd = true; // Let's pretend to do AAAA update - bool fqdn_rev = true; // Let's pretend to do PTR update - - cout << "CREATE: "; - - for (uint8_t i = 0; i < hwaddr_len; i++) { - hwaddr[i] = 65 + i; - } - hwaddr[19] = 0; // workaround - - for (uint8_t i = 0; i < client_id_len; i++) { - client_id[i] = 33 + i; - } - client_id[6] = 'X'; // there's apostrophe here. It would confuse - // query formatting, let's get rid of it - client_id[127] = 0; // workaround - - - sqlite3_stmt *stmt = NULL; - if (compiled_stmt_) { - char query[] = "INSERT INTO lease4(addr,hwaddr,client_id," - "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," - "fqdn_fwd,fqdn_rev) VALUES(?001,?002,?003,?004,?005,?006,?007,?008,?009,?010,?011);"; - int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); - if (result != SQLITE_OK) { - failure("Failed to compile statement"); - } - } - - - - for (uint32_t i = 0; i < num_; i++) { - - sprintf(cltt, "2012-07-11 15:43:%02d", i % 60); - - addr++; - char* errorMsg = NULL; - - if (!compiled_stmt_) { - // the first address is 1.0.0.0. - char query[2000]; - /// @todo: Encode HWADDR and CLIENT-ID properly - sprintf(query, "INSERT INTO lease4(addr,hwaddr,client_id," - "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," - "fqdn_fwd,fqdn_rev) VALUES(%u,'%s','%s',%d,%d,'%s',%d,'%s','%s','%s','%s');", - addr, hwaddr, client_id, valid_lft, recycle_time, - cltt, pool_id, (fixed?"true":"false"), - hostname.c_str(), (fqdn_fwd?"true":"false"), (fqdn_rev?"true":"false")); - - int result = sqlite3_exec(db_, query, NULL, 0, &errorMsg); - - if (result != SQLITE_OK) { - stringstream tmp; - tmp << "INSERT error:" << errorMsg; - failure(tmp.str().c_str()); - } - } else { - // compiled statement - int result = sqlite3_bind_int(stmt, 1, addr); - if (result != SQLITE_OK) { - failure("sqlite3_bind_int() for column 1"); - } - - result = sqlite3_bind_blob(stmt, 2, hwaddr, hwaddr_len, NULL); - if (result != SQLITE_OK) { - failure("sqlite3_bind_blob() for column 2"); - } - - result = sqlite3_bind_blob(stmt, 3, client_id, client_id_len, NULL); - if (result != SQLITE_OK) { - failure("sqlite3_bind_blob() for column 3"); - } - - if (sqlite3_bind_int(stmt, 4, valid_lft) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 4"); - } - - if (sqlite3_bind_int(stmt, 5, recycle_time) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 5"); - } - - if (sqlite3_bind_text(stmt, 6, cltt, strlen(cltt), NULL) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 6"); - } - - if (sqlite3_bind_int(stmt, 7, pool_id) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 7"); - } - - if (sqlite3_bind_int(stmt, 7, pool_id) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 7"); - } - - if (sqlite3_bind_int(stmt, 8, fixed) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 8"); - } - - if (sqlite3_bind_text(stmt, 9, hostname.c_str(), hostname.length(), NULL) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 9"); - } - - if (sqlite3_bind_int(stmt, 10, fqdn_fwd) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 10"); - } - - if (sqlite3_bind_int(stmt, 11, fqdn_rev) != SQLITE_OK) { - failure("sqlite3_bind_int() for column 11"); - } - - result = sqlite3_step(stmt); - - if (result != SQLITE_DONE) { - failure("Failed to execute INSERT clause"); - } - - // let's reset the compiled statement, so it can be used in the - // next iteration - result = sqlite3_reset(stmt); - if (result != SQLITE_OK) { - failure("Failed to execute sqlite3_reset()"); - } - - } - - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - int result = sqlite3_finalize(stmt); - if (result != SQLITE_OK) { - failure("sqlite3_finalize() failed"); - } - } - - cout << endl; -} - -static int search_callback(void *counter, int argc, char** argv, - char** azColName){ - - int* cnt = static_cast(counter); - (*cnt)++; - - char buf[512]; - - // retrieved lease can be accessed here - for(int i = 0; i < argc; i++){ - // pretend we do something with returned lease - if (argv[i]) { - strncpy(buf, azColName[i], 512); - strncpy(buf, argv[i], 512); - } - - // Uncomment this to print out all contents - // cout << azColName[i] << "=" << (argv[i] ? argv[i] : "NULL") << endl; - } - - return (0); -} - -void SQLite_uBenchmark::searchLease4Test() { - if (!db_) { - throw "SQLite connection is closed."; - } - - cout << "RETRIEVE: "; - - sqlite3_stmt *stmt = NULL; - if (compiled_stmt_) { - const char query[] = "SELECT lease_id,addr,hwaddr,client_id,valid_lft," - "cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " - "FROM lease4 where addr=?1"; - - int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); - if (result != SQLITE_OK) { - failure("Failed to compile statement"); - } - } - - - for (uint32_t i = 0; i < num_; i++) { - - uint32_t addr = BASE_ADDR4 + random() % int(num_ / hitratio_); - - int cnt = 0; - - if (!compiled_stmt_) { - char* errorMsg = NULL; - - char query[512]; - sprintf(query, "SELECT lease_id,addr,hwaddr,client_id,valid_lft," - "cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " - "FROM lease4 where addr=%d", addr); - int result = sqlite3_exec(db_, query, search_callback, &cnt, &errorMsg); - if (result != SQLITE_OK) { - stringstream tmp; - tmp << "SELECT failed: " << errorMsg; - failure(tmp.str().c_str()); - } - } else { - // compiled statement - int result = sqlite3_bind_int(stmt, 1, addr); - if (result != SQLITE_OK) { - failure("sqlite3_bind_int() for column 1"); - } - - result = sqlite3_step(stmt); - switch (result) { - case SQLITE_ROW: - { - uint32_t lease_addr = sqlite3_column_int(stmt, 1); - const void * lease_hwaddr = sqlite3_column_blob(stmt, 2); - uint32_t lease_hwaddr_len = sqlite3_column_bytes(stmt, 2); - const void * lease_clientid = sqlite3_column_blob(stmt, 3); - uint32_t lease_clientid_len = sqlite3_column_bytes(stmt, 3); - uint32_t lease_valid_lft = sqlite3_column_int(stmt, 4); - - // cltt - const unsigned char *lease_cltt = sqlite3_column_text(stmt, 5); - - uint32_t lease_pool_id = sqlite3_column_int(stmt, 6); - uint32_t lease_fixed = sqlite3_column_int(stmt, 7); - - const unsigned char *lease_hostname = sqlite3_column_text(stmt, 8); - - uint32_t lease_fqdn_fwd = sqlite3_column_int(stmt, 9); - uint32_t lease_fqdn_rev = sqlite3_column_int(stmt, 10); - - if (lease_addr || lease_hwaddr || lease_hwaddr_len || lease_clientid || - lease_clientid_len || lease_valid_lft || lease_cltt || lease_pool_id || - lease_fixed || lease_hostname || lease_fqdn_fwd || lease_fqdn_rev) { - // we don't need this information, we just want to obtain it to measure - // the overhead. That strange if is only to quell compiler/cppcheck - // warning about unused variables. - - cnt = 1; - } - - cnt = 1; // there is at least one row - break; - } - case SQLITE_DONE: - cnt = 0; // there are no rows at all (i.e. no such lease) - break; - default: - failure("Failed to execute SELECT clause"); - } - - // let's reset the compiled statement, so it can be used in the - // next iteration - result = sqlite3_reset(stmt); - if (result != SQLITE_OK) { - failure("Failed to execute sqlite3_reset()"); - } - } - - if (verbose_) { - cout << (cnt?".":"X"); - } - } - - if (compiled_stmt_) { - int result = sqlite3_finalize(stmt); - if (result != SQLITE_OK) { - failure("sqlite3_finalize() failed"); - } - } - - cout << endl; -} - -void SQLite_uBenchmark::updateLease4Test() { - if (!db_) { - throw "SQLite connection is closed."; - } - - cout << "UPDATE: "; - - sqlite3_stmt *stmt = NULL; - if (compiled_stmt_) { - const char query[] = "UPDATE lease4 SET valid_lft=1002, cltt='now' WHERE addr=?1"; - - int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); - if (result != SQLITE_OK) { - failure("Failed to compile statement"); - } - } - - for (uint32_t i = 0; i < num_; i++) { - - uint32_t addr = BASE_ADDR4 + random() % num_; - - if (!compiled_stmt_) { - char* errorMsg = NULL; - char query[512]; - sprintf(query, "UPDATE lease4 SET valid_lft=1002, cltt='now' WHERE addr=%d", - addr); - - int result = sqlite3_exec(db_, query, NULL /* no callback here*/, 0, &errorMsg); - if (result != SQLITE_OK) { - stringstream tmp; - tmp << "UPDATE error:" << errorMsg; - failure(tmp.str().c_str()); - } - } else { - - int result = sqlite3_bind_int(stmt, 1, addr); - if (result != SQLITE_OK) { - failure("sqlite3_bind_int() for column 1"); - } - - result = sqlite3_step(stmt); - if (result != SQLITE_OK && result != SQLITE_DONE) { - failure("Failed to execute sqlite3_step() for UPDATE"); - } - - // let's reset the compiled statement, so it can be used in the - // next iteration - result = sqlite3_reset(stmt); - if (result != SQLITE_OK) { - failure("Failed to execute sqlite3_reset()"); - } - - } - - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - int result = sqlite3_finalize(stmt); - if (result != SQLITE_OK) { - failure("sqlite3_finalize() failed"); - } - } - - cout << endl; -} - -void SQLite_uBenchmark::deleteLease4Test() { - if (!db_) { - throw "SQLite connection is closed."; - } - - cout << "DELETE: "; - - sqlite3_stmt *stmt = NULL; - if (compiled_stmt_) { - const char query[] = "DELETE FROM lease4 WHERE addr=?1"; - - int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); - if (result != SQLITE_OK) { - failure("Failed to compile statement"); - } - } - - for (uint32_t i = 0; i < num_; i++) { - - uint32_t addr = BASE_ADDR4 + i; - if (!compiled_stmt_) { - char* errorMsg = NULL; - - char query[2000]; - sprintf(query, "DELETE FROM lease4 WHERE addr=%d", addr); - int result = sqlite3_exec(db_, query, NULL /* no callback here*/, 0, &errorMsg); - if (result != SQLITE_OK) { - stringstream tmp; - tmp << "DELETE error:" << errorMsg; - failure(tmp.str().c_str()); - } - } else { - // compiled statement - - int result = sqlite3_bind_int(stmt, 1, addr); - if (result != SQLITE_OK) { - failure("sqlite3_bind_int() for column 1"); - } - - result = sqlite3_step(stmt); - if (result != SQLITE_OK && result != SQLITE_DONE) { - failure("Failed to execute sqlite3_step() for UPDATE"); - } - - // let's reset the compiled statement, so it can be used in the - // next iteration - result = sqlite3_reset(stmt); - if (result != SQLITE_OK) { - failure("Failed to execute sqlite3_reset()"); - } - - } - if (verbose_) { - cout << "."; - } - } - - if (compiled_stmt_) { - int result = sqlite3_finalize(stmt); - if (result != SQLITE_OK) { - failure("sqlite3_finalize() failed"); - } - } - - cout << endl; -} - -void SQLite_uBenchmark::printInfo() { - cout << "SQLite version is " << sqlite3_libversion() - << "sourceid version is " << sqlite3_sourceid() << endl; -} - - - -int main(int argc, char* const argv[]) { - - const char* filename = "sqlite.db"; - uint32_t num = 100; - bool sync = true; - bool verbose = true; - - SQLite_uBenchmark bench(filename, num, sync, verbose); - - bench.parseCmdline(argc, argv); - - int result = bench.run(); - - return (result); -} diff --git a/tests/tools/dhcp-ubench/sqlite_ubench.h b/tests/tools/dhcp-ubench/sqlite_ubench.h deleted file mode 100644 index 366b50a860..0000000000 --- a/tests/tools/dhcp-ubench/sqlite_ubench.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -#include -#include "benchmark.h" - -/// @brief SQLite benchmark -/// -/// That is a specific backend implementation. See \ref uBenchmark class for -/// detailed explanation of its operations. This class uses SQLite as DB backend. -class SQLite_uBenchmark: public uBenchmark { -public: - - /// @brief The sole SQL benchmark constructor - /// - /// DB file must be present and appropriate database schema must - /// be used. See sqlite.schema script and isc-dhcp-perf-guide.html - /// for details. - /// - /// sync flag affects "PRAGMA synchronous" to be ON or OFF. - /// - /// @param filename name of the SQLite DB file. Must be present. - /// @param num_iterations number of iterations of basic lease operations - /// @param sync should the operations be synchronous or not? - /// @param verbose would you like extra details be logged? - SQLite_uBenchmark(const std::string& filename, - uint32_t num_iterations, - bool sync, bool verbose); - - /// @brief Prints SQLite version info. - virtual void printInfo(); - - /// @brief Opens connection to the SQLite database. - virtual void connect(); - - /// @brief Closes connection to the SQLite database. - virtual void disconnect(); - - /// @brief Creates new leases. - /// - /// See uBenchmark::createLease4Test() for detailed explanation. - virtual void createLease4Test(); - - /// @brief Searches for existing leases. - /// - /// See uBenchmark::searchLease4Test() for detailed explanation. - virtual void searchLease4Test(); - - /// @brief Updates existing leases. - /// - /// See uBenchmark::updateLease4Test() for detailed explanation. - virtual void updateLease4Test(); - - /// @brief Deletes existing leases. - /// - /// See uBenchmark::deleteLease4Test() for detailed explanation. - virtual void deleteLease4Test(); - -protected: - - /// Handle to SQLite database connection. - sqlite3 *db_; -};