/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: #ident "$Id$" #ident "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved." /* Insert a bunch of stuff */ #include #include "tokudb_common_funcs.h" #include #include #include #include #include #include #include #include #if defined(HAVE_MALLOC_H) # include #elif defined(HAVE_SYS_MALLOC_H) # include #endif #include #if !defined(DB_PRELOCKED_WRITE) #define NO_DB_PRELOCKED #define DB_PRELOCKED_WRITE 0 #endif int verbose=1; int engine_status = 0; int which; enum { SERIAL_SPACING = 1<<6 }; enum { DEFAULT_ITEMS_TO_INSERT_PER_ITERATION = 1<<20 }; enum { DEFAULT_ITEMS_PER_TRANSACTION = 1<<14 }; #define DEFAULT_N_ITEMS (1LL<<22) #define DEFAULT_N_ITERATIONS (DEFAULT_N_ITEMS/DEFAULT_ITEMS_TO_INSERT_PER_ITERATION) static void insert (long long v); #define CKERR(r) ({ int __r = r; if (__r!=0) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, __r, db_strerror(r)); assert(__r==0); }) #define CKERR2(r,rexpect) do { if (r!=rexpect) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, r, db_strerror(r)); assert(r==rexpect); } while (0) /* default test parameters */ int keysize = sizeof (long long); int valsize = sizeof (long long); int pagesize = 0; long long cachesize = 128*1024*1024; int do_1514_point_query = 0; int dupflags = 0; int insert_multiple = 0; int num_dbs = 1; int cleaner_period = 0; int cleaner_iterations = 0; int noserial = 0; // Don't do the serial stuff int norandom = 0; // Don't do the random stuff int prelock = 0; int prelockflag = 0; int items_per_transaction = DEFAULT_ITEMS_PER_TRANSACTION; int items_per_iteration = DEFAULT_ITEMS_TO_INSERT_PER_ITERATION; int finish_child_first = 0; // Commit or abort child first (before doing so to the parent). No effect if child does not exist. int singlex_child = 0; // Do a single transaction, but do all work with a child int singlex = 0; // Do a single transaction int singlex_create = 0; // Create the db using the single transaction (only valid if singlex) int insert1first = 0; // insert 1 before doing the rest int check_small_rollback = 0; // verify that the rollback logs are small (only valid if singlex) int do_transactions = 0; int if_transactions_do_logging = DB_INIT_LOG; // set this to zero if we want no logging when transactions are used int do_abort = 0; int n_insertions_since_txn_began=0; int env_open_flags = DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL; uint32_t put_flags = 0; double compressibility = -1; // -1 means make it very compressible. 1 means use random bits everywhere. 2 means half the bits are random. int do_append = 0; int do_checkpoint_period = 0; uint32_t checkpoint_period = 0; static const char *log_dir = NULL; static int commitflags = 0; static int redzone = 0; static int redzone_set = 0; static int do_optimize = 0; static int unique_checks = 0; static long long n_iterations = DEFAULT_N_ITERATIONS; static int use_random = 0; enum { MAX_RANDOM_C = 16000057 }; // prime-numbers.org static unsigned char random_c[MAX_RANDOM_C]; static int next_random_c; static void init_random_c(void) { int i; for (i=0; i= MAX_RANDOM_C) next_random_c = 0; } static unsigned char get_random_c(void) { update_random_c_index(1); return random_c[next_random_c]; } static int min_int(int a, int b) { return a > b ? b : a; } static void copy_random_c(unsigned char *p, int n) { while (n > 0) { int m = min_int(n, MAX_RANDOM_C-next_random_c); memcpy(p, &random_c[next_random_c], m); n -= m; p += m; update_random_c_index(m); } } static void do_prelock(DB* db, DB_TXN* txn) { if (prelock) { #if !defined(NO_DB_PRELOCKED) int r = db->pre_acquire_table_lock(db, txn); assert(r==0); #else (void) db; (void) txn; #endif } } #define STRINGIFY2(s) #s #define STRINGIFY(s) STRINGIFY2(s) const char *dbdir = "./bench." STRINGIFY(DIRSUF); /* DIRSUF is passed in as a -D argument to the compiler. */ const char *dbfilename = "bench.db"; char *dbname; DB_ENV *dbenv; enum {MAX_DBS=128}; DB *dbs[MAX_DBS]; uint32_t put_flagss[MAX_DBS]; DB_TXN *parenttid=0; DB_TXN *tid=0; DBT dest_keys[MAX_DBS]; DBT dest_vals[MAX_DBS]; #if defined(TOKUDB) static int put_multiple_generate(DB *dest_db, DB *src_db, DBT *dest_key, DBT *dest_val, const DBT *src_key, const DBT *src_val) { assert(src_db == NULL); assert(dest_db != NULL); dest_key->data = src_key->data; dest_key->size = src_key->size; dest_val->data = src_val->data; dest_val->size = src_val->size; return 0; } #endif static void benchmark_setup (void) { int r; if (!do_append) { char unlink_cmd[strlen(dbdir) + strlen("rm -rf ") + 1]; snprintf(unlink_cmd, sizeof(unlink_cmd), "rm -rf %s", dbdir); //printf("unlink_cmd=%s\n", unlink_cmd); r = system(unlink_cmd); CKERR(r); if (strcmp(dbdir, ".") != 0) { r = toku_os_mkdir(dbdir,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); assert(r == 0); } } r = db_env_create(&dbenv, 0); assert(r == 0); #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 4 if (dbenv->set_lk_max) { r = dbenv->set_lk_max(dbenv, items_per_transaction*2); assert(r==0); } #endif #ifndef TOKUDB if (dbenv->set_lk_max_locks) { r = dbenv->set_lk_max_locks(dbenv, items_per_transaction*2); assert(r == 0); } #endif if (dbenv->set_cachesize) { r = dbenv->set_cachesize(dbenv, cachesize / (1024*1024*1024), cachesize % (1024*1024*1024), 1); if (r != 0) printf("WARNING: set_cachesize %d\n", r); } if (log_dir) { r = dbenv->set_lg_dir(dbenv, log_dir); assert(r == 0); } #if defined(TOKUDB) if (insert_multiple) { r = dbenv->set_generate_row_callback_for_put(dbenv, put_multiple_generate); CKERR(r); } #endif #if defined(TOKUDB) if (redzone_set) { r = dbenv->set_redzone(dbenv, redzone); assert(r == 0); } #endif r = dbenv->open(dbenv, dbdir, env_open_flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); assert(r == 0); #if defined(TOKUDB) if (do_checkpoint_period) { r = dbenv->checkpointing_set_period(dbenv, checkpoint_period); assert(r == 0); uint32_t period; r = dbenv->checkpointing_get_period(dbenv, &period); assert(r == 0 && period == checkpoint_period); } #endif #if defined(TOKUDB) if (cleaner_period) { r = dbenv->cleaner_set_period(dbenv, cleaner_period); assert(r == 0); uint32_t period; r = dbenv->cleaner_get_period(dbenv, &period); assert(r == 0 && period == (uint32_t)cleaner_period); } if (cleaner_iterations) { r = dbenv->cleaner_set_iterations(dbenv, cleaner_iterations); assert(r == 0); uint32_t iterations; r = dbenv->cleaner_get_iterations(dbenv, &iterations); assert(r == 0 && iterations == (uint32_t)cleaner_iterations); } #endif for (which = 0; which < num_dbs; which++) { r = db_create(&dbs[which], dbenv, 0); assert(r == 0); } if (do_transactions) { r=dbenv->txn_begin(dbenv, 0, &tid, 0); CKERR(r); } for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; if (pagesize && db->set_pagesize) { r = db->set_pagesize(db, pagesize); assert(r == 0); } if (dupflags) { r = db->set_flags(db, dupflags); assert(r == 0); } char name[strlen(dbfilename)+10]; if (which==0) sprintf(name, "%s", dbfilename); else sprintf(name, "%s_%d", dbfilename, which); r = db->open(db, tid, name, NULL, DB_BTREE, DB_CREATE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (r!=0) fprintf(stderr, "errno=%d, %s\n", errno, strerror(errno)); assert(r == 0); } if (insert1first) { if (do_transactions) { r=tid->commit(tid, 0); assert(r==0); tid = NULL; r=dbenv->txn_begin(dbenv, 0, &tid, 0); CKERR(r); } insert(-1); if (singlex) { r=tid->commit(tid, 0); assert(r==0); tid = NULL; r=dbenv->txn_begin(dbenv, 0, &tid, 0); CKERR(r); } } else if (singlex && !singlex_create) { r=tid->commit(tid, 0); assert(r==0); tid = NULL; r=dbenv->txn_begin(dbenv, 0, &tid, 0); CKERR(r); } if (do_transactions) { if (singlex) { for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; do_prelock(db, tid); } } else { r=tid->commit(tid, 0); assert(r==0); tid = NULL; } } if (singlex_child) { parenttid = tid; tid = NULL; r=dbenv->txn_begin(dbenv, parenttid, &tid, 0); CKERR(r); } } #if defined(TOKUDB) static void test1514(void); #endif static void benchmark_shutdown (void) { int r; #if defined(TOKUDB) if (do_1514_point_query) test1514(); #endif if (do_transactions && singlex && !insert1first && (singlex_create || prelock)) { #if defined(TOKUDB) //There should be a single 'truncate' in the rollback instead of many 'insert' entries. struct txn_stat *s; r = tid->txn_stat(tid, &s); assert(r==0); //TODO: #1125 Always do the test after performance testing is done. if (singlex_child) fprintf(stderr, "SKIPPED 'small rollback' test for child txn\n"); else assert(s->rollback_raw_count < 100); // gross test, not worth investigating details toku_free(s); //system("ls -l bench.tokudb"); #endif } if (do_transactions && singlex) { if (!singlex_child || finish_child_first) { assert(tid); r = (do_abort ? tid->abort(tid) : tid->commit(tid, 0)); assert(r==0); tid = NULL; } if (singlex_child) { tid = NULL; assert(parenttid); r = (do_abort ? parenttid->abort(parenttid) : parenttid->commit(parenttid, 0)); assert(r==0); parenttid = NULL; } else assert(!parenttid); } assert(!tid); assert(!parenttid); for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; if (do_optimize) { #if defined(TOKUDB) r = db->optimize(db); assert(r == 0); #endif } r = db->close(db, 0); assert(r == 0); } if (engine_status) { print_engine_status(dbenv); typedef void (*malloc_stats_fun_t)(void); malloc_stats_fun_t malloc_stats_f = (malloc_stats_fun_t) dlsym(RTLD_DEFAULT, "malloc_stats"); if (malloc_stats_f) { malloc_stats_f(); } } r = dbenv->close(dbenv, 0); assert(r == 0); } static void long_long_to_array (unsigned char *a, int array_size, unsigned long long l) { int i; for (i=0; i<8 && i>(56-8*i))&0xff; } static DBT *fill_dbt(DBT *dbt, const void *data, int size) { memset(dbt, 0, sizeof *dbt); dbt->size = size; dbt->data = (void *) data; return dbt; } // Fill array with 0's if compressibilty==-1, otherwise fill array with data that is likely to compress by a factor of compressibility. static void fill_array (unsigned char *data, int size) { memset(data, 0, size); if (compressibility>0) { if (use_random) { int i; for (i=0; iput_multiple(dbenv, NULL, tid, &kt, &vt, num_dbs, dbs, dest_keys, dest_vals, put_flagss); #else r = EINVAL; #endif if (unique_checks) assert(r == 0 || r == DB_KEYEXIST); else CKERR(r); } else { for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; r = db->put(db, tid, &kt, &vt, put_flags); if (unique_checks) assert(r == 0 || r == DB_KEYEXIST); else CKERR(r); } } if (do_transactions) { if (n_insertions_since_txn_began>=items_per_transaction && !singlex) { n_insertions_since_txn_began=0; r = tid->commit(tid, commitflags); assert(r==0); tid = NULL; r=dbenv->txn_begin(dbenv, 0, &tid, 0); assert(r==0); for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; do_prelock(db, tid); } n_insertions_since_txn_began=0; } n_insertions_since_txn_began++; } toku_free(kc); toku_free(vc); } static void serial_insert_from (long long from) { long long i; if (do_transactions && !singlex) { int r = dbenv->txn_begin(dbenv, 0, &tid, 0); assert(r==0); for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; do_prelock(db, tid); } } for (i=0; icommit(tid, 0); assert(r==0); tid=NULL; } } static long long llrandom (void) { return (((long long)(random()))<<32) + random(); } static void random_insert_below (long long below) { long long i; if (do_transactions && !singlex) { int r = dbenv->txn_begin(dbenv, 0, &tid, 0); assert(r==0); for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; do_prelock(db, tid); } } for (i=0; icommit(tid, 0); assert(r==0); tid=NULL; } } static void biginsert (long long n_elements, struct timeval *starttime) { long long i; struct timeval t1,t2; int iteration; for (i=0, iteration=0; iput_multiple api. Requires transactions.\n"); fprintf(stderr, " --redzone N redzone in percent\n"); fprintf(stderr, " --srandom N srandom(N)\n"); fprintf(stderr, " --engine_status print engine status at end of test \n"); fprintf(stderr, " n_iterations how many iterations (default %lld)\n", DEFAULT_N_ITERATIONS); return 1; } #if defined(TOKUDB) static int nothing(DBT const* UU(key), DBT const* UU(val), void* UU(extra)) { return 0; } static void test1514(void) { assert(norandom); //Otherwise we can't know the given element is missing. unsigned char kc[keysize], vc[valsize]; DBT kt; long long v = SERIAL_SPACING - 1; fill_array(kc, sizeof kc); long_long_to_array(kc, keysize, v); // Fill in the array first, then write the long long in. fill_array(vc, sizeof vc); long_long_to_array(vc, valsize, v); int r; DBC *c; struct timeval t1,t2; for (which = 0; which < num_dbs; which++) { DB *db = dbs[which]; r = db->cursor(db, tid, &c, 0); CKERR(r); gettimeofday(&t1,0); r = c->c_getf_set(c, 0, fill_dbt(&kt, kc, keysize), nothing, NULL); gettimeofday(&t2,0); CKERR2(r, DB_NOTFOUND); r = c->c_close(c); CKERR(r); } if (verbose) printf("(#1514) Single Point Query %9.6fs\n", toku_tdiff(&t2, &t1)); } #endif static int test_main (int argc, char *const argv[]) { struct timeval t1,t2,t3; long long total_n_items = DEFAULT_N_ITEMS; char *endptr; int i; for (i=1; i MAX_DBS) { fprintf(stderr, "--numdbs needs between 1 and %d\n", MAX_DBS); return print_usage(argv[0]); } } else if (strcmp(arg, "--cleaner-period") == 0) { cleaner_period = atoi(argv[++i]); if (cleaner_period < 0) { fprintf(stderr, "--cleaner-period needs to be positive\n"); return print_usage(argv[0]); } } else if (strcmp(arg, "--cleaner-iterations") == 0) { cleaner_iterations = atoi(argv[++i]); if (cleaner_iterations < 0) { fprintf(stderr, "--cleaner-iterations needs to be positive\n"); return print_usage(argv[0]); } } else if (strcmp(arg, "--compressibility") == 0) { compressibility = atof(argv[++i]); init_random_c(); (void) get_random_c(); } else if (strcmp(arg, "--nolog") == 0) { if_transactions_do_logging = 0; } else if (strcmp(arg, "--singlex-create") == 0) { do_transactions = 1; singlex = 1; singlex_create = 1; } else if (strcmp(arg, "--finish-child-first") == 0) { finish_child_first = 1; } else if (strcmp(arg, "--singlex-child") == 0) { do_transactions = 1; singlex = 1; singlex_child = 1; } else if (strcmp(arg, "--singlex") == 0) { do_transactions = 1; singlex = 1; } else if (strcmp(arg, "--insert1first") == 0) { insert1first = 1; } else if (strcmp(arg, "--check_small_rollback") == 0) { check_small_rollback = 1; } else if (strcmp(arg, "--xcount") == 0) { if (i+1 >= argc) return print_usage(argv[0]); items_per_transaction = strtoll(argv[++i], &endptr, 10); assert(*endptr == 0); } else if (strcmp(arg, "--abort") == 0) { do_abort = 1; } else if (strcmp(arg, "--periter") == 0) { if (i+1 >= argc) return print_usage(argv[0]); items_per_iteration = strtoll(argv[++i], &endptr, 10); assert(*endptr == 0); } else if (strcmp(arg, "--cachesize") == 0) { if (i+1 >= argc) return print_usage(argv[0]); cachesize = strtoll(argv[++i], &endptr, 10); assert(*endptr == 0); } else if (strcmp(arg, "--keysize") == 0) { if (i+1 >= argc) return print_usage(argv[0]); keysize = atoi(argv[++i]); } else if (strcmp(arg, "--valsize") == 0) { if (i+1 >= argc) return print_usage(argv[0]); valsize = atoi(argv[++i]); } else if (strcmp(arg, "--pagesize") == 0) { if (i+1 >= argc) return print_usage(argv[0]); pagesize = atoi(argv[++i]); } else if (strcmp(arg, "--env") == 0) { if (i+1 >= argc) return print_usage(argv[0]); dbdir = argv[++i]; #if defined(TOKUDB) } else if (strcmp(arg, "--1514") == 0) { do_1514_point_query=1; #endif } else if (strcmp(arg, "--prelock") == 0) { prelock=1; } else if (strcmp(arg, "--prelockflag") == 0) { prelock=1; prelockflag=1; } else if (strcmp(arg, "--srandom") == 0) { if (i+1 >= argc) return print_usage(argv[0]); srandom(atoi(argv[++i])); } else if (strcmp(arg, "--append") == 0) { do_append = 1; } else if (strcmp(arg, "--checkpoint-period") == 0) { if (i+1 >= argc) return print_usage(argv[9]); do_checkpoint_period = 1; checkpoint_period = (uint32_t) atoi(argv[++i]); } else if (strcmp(arg, "--nosync") == 0) { commitflags += DB_TXN_NOSYNC; } else if (strcmp(arg, "--userandom") == 0) { use_random = 1; } else if (strcmp(arg, "--unique_checks") == 0) { if (i+1 >= argc) return print_usage(argv[0]); unique_checks = atoi(argv[++i]); if (unique_checks) put_flags = DB_NOOVERWRITE; else put_flags = 0; } else if (strcmp(arg, "--log_dir") == 0) { if (i+1 >= argc) return print_usage(argv[0]); log_dir = argv[++i]; } else if (strcmp(arg, "--redzone") == 0) { if (i+1 >= argc) return print_usage(argv[0]); redzone_set = 1; redzone = atoi(argv[++i]); } else if (strcmp(arg, "--optimize") == 0) { do_optimize = 1; } else if (strcmp(arg, "--engine_status") == 0) { engine_status = 1; } else { return print_usage(argv[0]); } } if (do_transactions) { env_open_flags |= DB_INIT_TXN | if_transactions_do_logging | DB_INIT_LOCK; } if (do_transactions && prelockflag) { put_flags |= DB_PRELOCKED_WRITE; } if (i