Commit 076ee90f authored by Barry Perlman's avatar Barry Perlman Committed by Yoni Fogel

[t:2892] Add test of upgrading dictionary created by 4.1.1 loader.

git-svn-id: file:///svn/toku/tokudb@25885 c7de825b-a66e-492c-adef-691d508d4ae1
parent 38967db1
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "Copyright (c) 2010 Tokutek Inc. All rights reserved."
#ident "$Id$"
// Purpose of this test is to verify that a dictionary created by the 4.1.1
// loader can be properly read with 5.0.
// This file was derived from the 4.1.1 version of loader-stress-test.c,
// which was used to create the dictionary.
// This test only reads (and upgrades) the dictionary, it does not load it.
// Need to use malloc for the malloc instrumentation tests
#define TOKU_ALLOW_DEPRECATED
#include "test.h"
#include "toku_pthread.h"
#include "toku_atomic.h"
#include <db.h>
#include <sys/stat.h>
#include "ydb-internal.h"
DB_ENV *env;
enum {MAX_NAME=128};
enum {MAX_DBS=256};
int NUM_DBS=1;
int NUM_ROWS=250000;
int CHECK_RESULTS=0;
int USE_PUTS=0;
enum { old_default_cachesize=1024 }; // MB
int CACHESIZE=old_default_cachesize;
int ALLOW_DUPS=0;
enum {MAGIC=311};
char *datadir = NULL;
BOOL check_est = TRUE; // do check the estimates by default
BOOL footprint_print = FALSE; // print memory footprint info
// Code for showing memory footprint information.
pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
size_t hiwater;
size_t water;
size_t hiwater_start;
static long long mcount = 0, fcount=0;
size_t malloc_usable_size(void *p);
static void my_free(void*p) {
if (p) {
water-=malloc_usable_size(p);
}
free(p);
}
static void *my_malloc(size_t size) {
void *r = malloc(size);
if (r) {
water += malloc_usable_size(r);
if (water>hiwater) hiwater=water;
}
return r;
}
static void *my_realloc(void *p, size_t size) {
size_t old_usable = p ? malloc_usable_size(p) : 0;
void *r = realloc(p, size);
if (r) {
water -= old_usable;
water += malloc_usable_size(r);
}
return r;
}
//
// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
//
// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
int a[MAX_DBS][32];
int inv[MAX_DBS][32];
#if defined(__cilkplusplus) || defined (__cplusplus)
extern "C" {
#endif
// rotate right and left functions
static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
const unsigned int n = num % 32;
return (x >> n) | ( x << (32 - n));
}
static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
const unsigned int n = num % 32;
return (x << n) | ( x >> (32 - n));
}
static void generate_permute_tables(void) {
int i, j, tmp;
for(int db=0;db<MAX_DBS;db++) {
for(i=0;i<32;i++) {
a[db][i] = i;
}
for(i=0;i<32;i++) {
j = random() % (i + 1);
tmp = a[db][j];
a[db][j] = a[db][i];
a[db][i] = tmp;
}
// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
for(i=0;i<32;i++) {
inv[db][a[db][i]] = i;
}
}
}
// permute bits of x based on inverse permute table bitmap
static unsigned int inv_twiddle32(unsigned int x, int db)
{
unsigned int b = 0;
for(int i=0;i<32;i++) {
b |= (( x >> i ) & 1) << inv[db][i];
}
return b;
}
static unsigned int pkey_for_val(int key, int i) {
return rotr32(key, i) - MAGIC;
}
#if defined(__cilkplusplus) || defined(__cplusplus)
} // extern "C"
#endif
static void check_results(DB **dbs)
{
for(int j=0;j<NUM_DBS;j++){
DBT key, val;
unsigned int k=0, v=0;
dbt_init(&key, &k, sizeof(unsigned int));
dbt_init(&val, &v, sizeof(unsigned int));
int r;
unsigned int pkey_for_db_key;
DB_TXN *txn;
r = env->txn_begin(env, NULL, &txn, 0);
CKERR(r);
DBC *cursor;
r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
CKERR(r);
for(int i=0;i<NUM_ROWS;i++) {
r = cursor->c_get(cursor, &key, &val, DB_NEXT);
CKERR(r);
k = *(unsigned int*)key.data;
pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
v = *(unsigned int*)val.data;
// test that we have the expected keys and values
assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
}
{printf("."); fflush(stdout);}
r = cursor->c_close(cursor);
CKERR(r);
r = txn->commit(txn, 0);
CKERR(r);
}
printf("\nCheck OK\n");
}
static void *expect_poll_void = &expect_poll_void;
static struct progress_info {
double time;
double progress;
} *progress_infos=NULL;
static int progress_infos_count=0;
static void test_loader(DB **dbs)
{
int r;
DB_TXN *txn;
// this is the essential part of the upgrade test
check_results(dbs);
for (int i=0; i<NUM_DBS; i++) {
r = env->txn_begin(env, NULL, &txn, 0);
CKERR(r);
DB_BTREE_STAT64 stats;
r = dbs[i]->stat64(dbs[i], txn, &stats);
CKERR(r);
if (verbose)
printf("n_keys=%" PRIu64 " n_data=%" PRIu64 " dsize=%" PRIu64 " fsize=%" PRIu64 "\n",
stats.bt_nkeys, stats.bt_ndata, stats.bt_dsize, stats.bt_fsize);
assert(stats.bt_nkeys == (u_int64_t)NUM_ROWS);
assert(stats.bt_ndata == (u_int64_t)NUM_ROWS);
assert(stats.bt_dsize == ((u_int64_t)NUM_ROWS) * 2 * sizeof(unsigned int));
r = txn->commit(txn, 0);
CKERR(r);
}
}
char *free_me = NULL;
char *env_dir = ENVDIR; // the default env_dir.
char *tmp_subdir = "tmp.subdir";
#define OLDDATADIR "../../../../tokudb.data/"
char *db_v4_dir = OLDDATADIR "env_preload.4.1.1.loader250kd1.cleanshutdown";
static void setup(void) {
int r;
int len = 256;
char syscmd[len];
char * src_db_dir;
src_db_dir = db_v4_dir;
r = snprintf(syscmd, len, "rm -rf %s", env_dir);
assert(r<len);
r = system(syscmd);
CKERR(r);
r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
assert(r<len);
r = system(syscmd);
CKERR(r);
}
static void run_test(void)
{
int r;
setup();
{
char len = strlen(env_dir) + strlen(tmp_subdir) + 10;
char tmpdir[len];
r = snprintf(tmpdir, len, "%s/%s", env_dir, tmp_subdir);
assert(r<len);
r = db_env_create(&env, 0); CKERR(r);
r = env->set_tmp_dir(env, tmp_subdir); CKERR(r);
}
r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
if (datadir) {
r = env->set_data_dir(env, datadir); CKERR(r);
}
int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
env->set_errfile(env, stderr);
r = env->checkpointing_set_period(env, 60); CKERR(r);
DBT desc;
dbt_init(&desc, "foo", sizeof("foo"));
char name[MAX_NAME*2];
DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
assert(dbs != NULL);
int idx[MAX_DBS];
for(int i=0;i<NUM_DBS;i++) {
idx[i] = i;
r = db_create(&dbs[i], env, 0); CKERR(r);
r = dbs[i]->set_descriptor(dbs[i], 1, &desc); CKERR(r);
dbs[i]->app_private = &idx[i];
snprintf(name, sizeof(name), "db_%04x", i);
r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
}
generate_permute_tables();
// -------------------------- //
test_loader(dbs);
// -------------------------- //
for(int i=0;i<NUM_DBS;i++) {
dbs[i]->close(dbs[i], 0); CKERR(r);
dbs[i] = NULL;
}
if (verbose >= 2)
print_engine_status(env);
r = env->close(env, 0); CKERR(r);
toku_free(dbs);
}
// ------------ infrastructure ----------
static void do_args(int argc, char * const argv[]);
int test_main(int argc, char * const *argv) {
do_args(argc, argv);
run_test();
if (free_me) toku_free(free_me);
if (progress_infos) {
if (verbose>=2) {
double ratio=progress_infos[progress_infos_count-1].time/progress_infos[progress_infos_count-1].progress;
printf("Progress ratios:\n");
for (int i=0; i<progress_infos_count; i++) {
printf(" %5.3f\n", (progress_infos[i].time/progress_infos[i].progress)/ratio);
}
}
toku_free(progress_infos);
}
if (footprint_print) {
printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM) mcount=%lld fcount=%lld\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024), mcount, fcount);
extern void malloc_stats(void);
malloc_stats();
}
return 0;
}
static void do_args(int argc, char * const argv[]) {
// Must look for "-f" right away before we malloc anything.
for (int i=1; i<argc; i++) {
if (strcmp(argv[i], "-f")) {
db_env_set_func_malloc(my_malloc);
db_env_set_func_realloc(my_realloc);
db_env_set_func_free(my_free);
}
}
int resultcode;
char *cmd = argv[0];
argc--; argv++;
CACHESIZE = (toku_os_get_phys_memory_size() / (1024*1024))/2; //MB
while (argc>0) {
if (strcmp(argv[0], "-v")==0) {
verbose++;
} else if (strcmp(argv[0],"-q")==0) {
verbose--;
if (verbose<0) verbose=0;
} else if (strcmp(argv[0], "-h")==0) {
resultcode=0;
do_usage:
fprintf(stderr, "Usage: -h -d <num_dbs> -r <num_rows> [-m <megabytes>] [-M]\n%s\n", cmd);
fprintf(stderr, " where -d <num_dbs> is the number of dictionaries to build (primary & secondary). (Default=%d)\n", NUM_DBS);
fprintf(stderr, " -m <m> use m MB of memory for the cachetable (default is %d MB)\n", CACHESIZE);
fprintf(stderr, " -M use %d MB of memory for the cachetable\n", old_default_cachesize);
fprintf(stderr, " -f print memory footprint information at various points in the load\n");
exit(resultcode);
} else if (strcmp(argv[0], "-d")==0) {
argc--; argv++;
NUM_DBS = atoi(argv[0]);
if ( NUM_DBS > MAX_DBS ) {
fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
resultcode=1;
goto do_usage;
}
} else if (strcmp(argv[0], "-v")==0) {
verbose++;
} else if (strcmp(argv[0],"-q")==0) {
verbose--;
if (verbose<0) verbose=0;
} else if (strcmp(argv[0], "-f")==0) {
footprint_print = TRUE;
} else if (strcmp(argv[0], "-r")==0) {
argc--; argv++;
NUM_ROWS = atoi(argv[0]);
} else if (strcmp(argv[0], "-m")==0) {
argc--; argv++;
CACHESIZE = atoi(argv[0]);
} else if (strcmp(argv[0], "-M")==0) {
CACHESIZE = old_default_cachesize;
} else {
fprintf(stderr, "Unknown arg: %s\n", argv[0]);
resultcode=1;
goto do_usage;
}
argc--;
argv++;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment