2006-12-19 08:27:03 +03:00
/*
simple ctdb benchmark
Copyright ( C ) Andrew Tridgell 2006
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 2 of the License , or ( at your option ) any later version .
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "includes.h"
# include "lib/events/events.h"
# include "system/filesys.h"
# include "popt.h"
# include <sys/time.h>
# include <time.h>
static struct timeval tp1 , tp2 ;
static void start_timer ( void )
{
gettimeofday ( & tp1 , NULL ) ;
}
static double end_timer ( void )
{
gettimeofday ( & tp2 , NULL ) ;
return ( tp2 . tv_sec + ( tp2 . tv_usec * 1.0e-6 ) ) -
( tp1 . tv_sec + ( tp1 . tv_usec * 1.0e-6 ) ) ;
}
static int timelimit = 10 ;
static int num_records = 10 ;
2007-02-20 06:57:13 +03:00
static int num_msgs = 1 ;
2006-12-19 08:27:03 +03:00
static int num_repeats = 100 ;
enum my_functions { FUNC_INCR = 1 , FUNC_FETCH = 2 } ;
/*
ctdb call function to increment an integer
*/
2007-01-25 07:19:16 +03:00
static int incr_func ( struct ctdb_call_info * call )
2006-12-19 08:27:03 +03:00
{
if ( call - > record_data . dsize = = 0 ) {
call - > new_data = talloc ( call , TDB_DATA ) ;
if ( call - > new_data = = NULL ) {
return CTDB_ERR_NOMEM ;
}
call - > new_data - > dptr = talloc_size ( call , 4 ) ;
call - > new_data - > dsize = 4 ;
* ( uint32_t * ) call - > new_data - > dptr = 0 ;
} else {
call - > new_data = & call - > record_data ;
}
( * ( uint32_t * ) call - > new_data - > dptr ) + + ;
return 0 ;
}
/*
ctdb call function to fetch a record
*/
2007-01-25 07:19:16 +03:00
static int fetch_func ( struct ctdb_call_info * call )
2006-12-19 08:27:03 +03:00
{
call - > reply_data = & call - > record_data ;
return 0 ;
}
/*
benchmark incrementing an integer
*/
2007-04-03 13:41:00 +04:00
static void bench_incr ( struct ctdb_context * ctdb , struct ctdb_db_context * ctdb_db )
2006-12-19 08:27:03 +03:00
{
int loops = 0 ;
int ret , i ;
2007-01-25 08:13:17 +03:00
struct ctdb_call call ;
ZERO_STRUCT ( call ) ;
2006-12-19 08:27:03 +03:00
start_timer ( ) ;
while ( 1 ) {
uint32_t v = loops % num_records ;
2007-01-25 08:13:17 +03:00
call . call_id = FUNC_INCR ;
call . key . dptr = ( uint8_t * ) & v ;
call . key . dsize = 4 ;
2006-12-19 08:27:03 +03:00
for ( i = 0 ; i < num_repeats ; i + + ) {
2007-04-03 13:41:00 +04:00
ret = ctdb_call ( ctdb_db , & call ) ;
2006-12-19 08:27:03 +03:00
if ( ret ! = 0 ) {
printf ( " incr call failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
return ;
}
}
if ( num_repeats * ( + + loops ) % 10000 = = 0 ) {
if ( end_timer ( ) > timelimit ) break ;
printf ( " Incr: %.2f ops/sec \r " , num_repeats * loops / end_timer ( ) ) ;
fflush ( stdout ) ;
}
}
2007-01-25 08:13:17 +03:00
call . call_id = FUNC_FETCH ;
2007-04-03 13:41:00 +04:00
ret = ctdb_call ( ctdb_db , & call ) ;
2006-12-19 08:27:03 +03:00
if ( ret = = - 1 ) {
printf ( " ctdb_call FUNC_FETCH failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
return ;
}
printf ( " Incr: %.2f ops/sec (loops=%d val=%d) \n " ,
2007-01-25 08:13:17 +03:00
num_repeats * loops / end_timer ( ) , loops , * ( uint32_t * ) call . reply_data . dptr ) ;
2006-12-19 08:27:03 +03:00
}
2007-02-09 04:45:58 +03:00
static int msg_count ;
static int msg_plus , msg_minus ;
/*
handler for messages in bench_ring ( )
*/
static void ring_message_handler ( struct ctdb_context * ctdb , uint32_t srvid ,
2007-04-13 14:38:24 +04:00
TDB_DATA data , void * private_data )
2007-02-09 04:45:58 +03:00
{
int incr = * ( int * ) data . dptr ;
2007-04-13 14:38:24 +04:00
int * count = ( int * ) private_data ;
2007-02-09 04:45:58 +03:00
int dest ;
( * count ) + + ;
dest = ( ctdb_get_vnn ( ctdb ) + incr ) % ctdb_get_num_nodes ( ctdb ) ;
ctdb_send_message ( ctdb , dest , srvid , data ) ;
if ( incr = = 1 ) {
msg_plus + + ;
} else {
msg_minus + + ;
}
}
/*
benchmark sending messages in a ring around the nodes
*/
static void bench_ring ( struct ctdb_context * ctdb , struct event_context * ev )
{
2007-02-16 06:48:27 +03:00
int vnn = ctdb_get_vnn ( ctdb ) ;
2007-02-09 04:45:58 +03:00
if ( vnn = = 0 ) {
2007-02-20 06:57:13 +03:00
int i ;
2007-02-09 04:45:58 +03:00
/* two messages are injected into the ring, moving
in opposite directions */
2007-02-16 06:48:27 +03:00
int dest , incr ;
TDB_DATA data ;
data . dptr = ( uint8_t * ) & incr ;
data . dsize = sizeof ( incr ) ;
2007-02-20 06:57:13 +03:00
for ( i = 0 ; i < num_msgs ; i + + ) {
incr = 1 ;
dest = ( ctdb_get_vnn ( ctdb ) + incr ) % ctdb_get_num_nodes ( ctdb ) ;
ctdb_send_message ( ctdb , dest , 0 , data ) ;
incr = - 1 ;
dest = ( ctdb_get_vnn ( ctdb ) + incr ) % ctdb_get_num_nodes ( ctdb ) ;
ctdb_send_message ( ctdb , dest , 0 , data ) ;
}
2007-02-09 04:45:58 +03:00
}
start_timer ( ) ;
while ( end_timer ( ) < timelimit ) {
2007-02-20 06:57:13 +03:00
if ( vnn = = 0 & & msg_count % 10000 = = 0 ) {
2007-02-09 04:45:58 +03:00
printf ( " Ring: %.2f msgs/sec (+ve=%d -ve=%d) \r " ,
msg_count / end_timer ( ) , msg_plus , msg_minus ) ;
fflush ( stdout ) ;
}
event_loop_once ( ev ) ;
}
printf ( " Ring: %.2f msgs/sec (+ve=%d -ve=%d) \n " ,
msg_count / end_timer ( ) , msg_plus , msg_minus ) ;
}
2006-12-19 08:27:03 +03:00
/*
main program
*/
int main ( int argc , const char * argv [ ] )
{
struct ctdb_context * ctdb ;
2007-04-03 13:41:00 +04:00
struct ctdb_db_context * ctdb_db ;
2006-12-19 08:27:03 +03:00
const char * nlist = NULL ;
const char * transport = " tcp " ;
const char * myaddress = NULL ;
int self_connect = 0 ;
2007-04-10 08:46:32 +04:00
int daemon_mode = 0 ;
2006-12-19 08:27:03 +03:00
struct poptOption popt_options [ ] = {
POPT_AUTOHELP
{ " nlist " , 0 , POPT_ARG_STRING , & nlist , 0 , " node list file " , " filename " } ,
{ " listen " , 0 , POPT_ARG_STRING , & myaddress , 0 , " address to listen on " , " address " } ,
{ " transport " , 0 , POPT_ARG_STRING , & transport , 0 , " protocol transport " , NULL } ,
{ " self-connect " , 0 , POPT_ARG_NONE , & self_connect , 0 , " enable self connect " , " boolean " } ,
2007-04-10 08:46:32 +04:00
{ " daemon " , 0 , POPT_ARG_NONE , & daemon_mode , 0 , " spawn a ctdb daemon " , " boolean " } ,
2006-12-19 08:27:03 +03:00
{ " timelimit " , ' t ' , POPT_ARG_INT , & timelimit , 0 , " timelimit " , " integer " } ,
{ " num-records " , ' r ' , POPT_ARG_INT , & num_records , 0 , " num_records " , " integer " } ,
2007-02-20 06:57:13 +03:00
{ " num-msgs " , ' n ' , POPT_ARG_INT , & num_msgs , 0 , " num_msgs " , " integer " } ,
2006-12-19 08:27:03 +03:00
POPT_TABLEEND
} ;
int opt ;
const char * * extra_argv ;
int extra_argc = 0 ;
int ret ;
poptContext pc ;
struct event_context * ev ;
pc = poptGetContext ( argv [ 0 ] , argc , argv , popt_options , POPT_CONTEXT_KEEP_FIRST ) ;
while ( ( opt = poptGetNextOpt ( pc ) ) ! = - 1 ) {
switch ( opt ) {
default :
fprintf ( stderr , " Invalid option %s: %s \n " ,
poptBadOption ( pc , 0 ) , poptStrerror ( opt ) ) ;
exit ( 1 ) ;
}
}
/* setup the remaining options for the main program to use */
extra_argv = poptGetArgs ( pc ) ;
if ( extra_argv ) {
extra_argv + + ;
while ( extra_argv [ extra_argc ] ) extra_argc + + ;
}
if ( nlist = = NULL | | myaddress = = NULL ) {
printf ( " You must provide a node list with --nlist and an address with --listen \n " ) ;
exit ( 1 ) ;
}
ev = event_context_init ( NULL ) ;
/* initialise ctdb */
ctdb = ctdb_init ( ev ) ;
if ( ctdb = = NULL ) {
printf ( " Failed to init ctdb \n " ) ;
exit ( 1 ) ;
}
if ( self_connect ) {
ctdb_set_flags ( ctdb , CTDB_FLAG_SELF_CONNECT ) ;
}
2007-04-10 08:46:32 +04:00
if ( daemon_mode ) {
ctdb_set_flags ( ctdb , CTDB_FLAG_DAEMON_MODE ) ;
}
2006-12-19 08:27:03 +03:00
ret = ctdb_set_transport ( ctdb , transport ) ;
if ( ret = = - 1 ) {
printf ( " ctdb_set_transport failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
exit ( 1 ) ;
}
/* tell ctdb what address to listen on */
ret = ctdb_set_address ( ctdb , myaddress ) ;
if ( ret = = - 1 ) {
printf ( " ctdb_set_address failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
exit ( 1 ) ;
}
/* tell ctdb what nodes are available */
ret = ctdb_set_nlist ( ctdb , nlist ) ;
if ( ret = = - 1 ) {
printf ( " ctdb_set_nlist failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
exit ( 1 ) ;
}
/* attach to a specific database */
2007-04-03 13:41:00 +04:00
ctdb_db = ctdb_attach ( ctdb , " test.tdb " , TDB_DEFAULT , O_RDWR | O_CREAT | O_TRUNC , 0666 ) ;
if ( ! ctdb_db ) {
2006-12-19 08:27:03 +03:00
printf ( " ctdb_attach failed - %s \n " , ctdb_errstr ( ctdb ) ) ;
exit ( 1 ) ;
}
2007-04-03 13:41:00 +04:00
/* setup a ctdb call function */
ret = ctdb_set_call ( ctdb_db , incr_func , FUNC_INCR ) ;
ret = ctdb_set_call ( ctdb_db , fetch_func , FUNC_FETCH ) ;
2006-12-19 08:27:03 +03:00
/* start the protocol running */
ret = ctdb_start ( ctdb ) ;
2007-04-11 07:43:15 +04:00
ctdb_set_message_handler ( ctdb , 0 , ring_message_handler , & msg_count ) ;
2007-04-11 05:01:42 +04:00
2006-12-19 08:27:03 +03:00
/* wait until all nodes are connected (should not be needed
outside of test code ) */
ctdb_connect_wait ( ctdb ) ;
2007-02-09 04:45:58 +03:00
bench_ring ( ctdb , ev ) ;
2006-12-19 08:27:03 +03:00
/* shut it down */
talloc_free ( ctdb ) ;
return 0 ;
}