From 68e8036c754a3886dcbc7b9cc1605ca720730012 Mon Sep 17 00:00:00 2001 From: "Bradley C. Kuszmaul" Date: Wed, 12 Mar 2008 19:40:38 +0000 Subject: [PATCH] Make recovery work when called from {{{DB_ENV->open}}}. Fixes #483. git-svn-id: file:///svn/tokudb@2768 c7de825b-a66e-492c-adef-691d508d4ae1 --- newbrt/Makefile | 8 ++-- newbrt/log.h | 2 + newbrt/recover.c | 52 ++++++++++++++++++++++--- newbrt/tdb-recover.c | 82 +++++++++++++++++++++++++++++++++++++++ src/Makefile | 2 +- src/tests/test-recover1.c | 2 + src/tests/test_log2.c | 1 + src/ydb.c | 60 ++++++++++++++++++++++++++-- 8 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 newbrt/tdb-recover.c diff --git a/newbrt/Makefile b/newbrt/Makefile index 47abdaa84f8..c366792bdcc 100644 --- a/newbrt/Makefile +++ b/newbrt/Makefile @@ -8,7 +8,7 @@ # GCOV_FLAGS = -fprofile-arcs -ftest-coverage # PROF_FLAGS = -pg -# OPTFLAGS = -O2 +OPTFLAGS = -O2 ifeq ($(VERBOSE),2) VERBVERBOSE=-v @@ -42,7 +42,7 @@ endif # When debugging, try: valgrind --show-reachable=yes --leak-check=full ./brt-test -default: bins libs recover tdb_logprint +default: bins libs tdb-recover tdb_logprint # Put these one-per-line so that if we insert a new one the svn diff can understand it better. # Also keep them sorted. REGRESSION_TESTS = \ @@ -84,9 +84,9 @@ tdb_logprint: LDFLAGS+=-lz tdb_logprint.o: log-internal.h brttypes.h yerror.h log.h kv-pair.h tdb_logprint: log_code.o memory.o log.o brt-serialize.o fifo.o pma.o ybt.o fingerprint.o mempool.o primes.o toku_assert.o roll.o brt.o cachetable.o brt-verify.o key.o -recover: LDFLAGS+=-lz +tdb-recover: LDFLAGS+=-lz recover.o: log_header.h log-internal.h log.h yerror.h brttypes.h kv-pair.h memory.h key.h -recover: recover.o log_code.o memory.o log.o brt-serialize.o fifo.o pma.o ybt.o fingerprint.o mempool.o primes.o toku_assert.o cachetable.o brt.o brt-verify.o key.o roll.o +tdb-recover: tdb-recover.o recover.o log_code.o memory.o log.o brt-serialize.o fifo.o pma.o ybt.o fingerprint.o mempool.o primes.o toku_assert.o cachetable.o brt.o brt-verify.o key.o roll.o roll.o: log_header.h log-internal.h log.h yerror.h brttypes.h kv-pair.h memory.h key.h cachetable.h pma.h diff --git a/newbrt/log.h b/newbrt/log.h index 676c20473be..71b712b637b 100644 --- a/newbrt/log.h +++ b/newbrt/log.h @@ -153,4 +153,6 @@ int toku_logger_abort(TOKUTXN); // Return 0 if there is a live txn with that txnid. int toku_txnid2txn (TOKULOGGER logger, TXNID txnid, TOKUTXN *result); +int tokudb_recover(const char *datadir, const char *logdir); + #endif diff --git a/newbrt/recover.c b/newbrt/recover.c index a8c3bef56b3..ec15a978be1 100644 --- a/newbrt/recover.c +++ b/newbrt/recover.c @@ -12,29 +12,66 @@ #include "log-internal.h" #include "log_header.h" #include "toku_assert.h" + #include #include +#include +#include #include -int main (int argc, char *argv[]) { - const char *dir; +int tokudb_recover(const char *data_dir, const char *log_dir) { int r; int entrycount=0; - assert(argc==2); - dir = argv[1]; int n_logfiles; char **logfiles; - r = toku_logger_find_logfiles(dir, &n_logfiles, &logfiles); + + int lockfd; + + { + int namelen=strlen(data_dir); + char lockfname[namelen+20]; + + snprintf(lockfname, sizeof(lockfname), "%s/__recoverylock_dont_delete_me", data_dir); + lockfd = open(lockfname, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR); + if (lockfd<0) { + printf("Couldn't open %s\n", lockfname); + exit(1); + } + r=flock(lockfd, LOCK_EX | LOCK_NB); + if (r!=0) { + printf("Couldn't run recovery because some other process holds the recovery lock %s\n", lockfname); + exit(1); + } + } + + r = toku_logger_find_logfiles(log_dir, &n_logfiles, &logfiles); if (r!=0) exit(1); int i; toku_recover_init(); + char org_wd[1000]; + { + char *wd=getcwd(org_wd, sizeof(org_wd)); + assert(wd!=0); + //printf("%s:%d org_wd=\"%s\"\n", __FILE__, __LINE__, org_wd); + } + char data_wd[1000]; + { + r=chdir(data_dir); assert(r==0); + char *wd=getcwd(data_wd, sizeof(data_wd)); + assert(wd!=0); + //printf("%s:%d data_wd=\"%s\"\n", __FILE__, __LINE__, data_wd); + } for (i=0; i +#include +#include +#include +#include + +int main (int argc, char *argv[]) { + const char *dir; + int r; + int entrycount=0; + assert(argc==2); + dir = argv[1]; + int n_logfiles; + char **logfiles; + + int lockfd; + + { + int namelen=strlen(dir); + char lockfname[namelen+20]; + snprintf(lockfname, sizeof(lockfname), "%s/__recoverylock", dir); + lockfd = open(lockfname, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR); + if (lockfd<0) { + printf("Couldn't open %s\n", lockfname); + exit(1); + } + r=flock(lockfd, LOCK_EX | LOCK_NB); + if (r!=0) { + printf("Couldn't run recovery because some other process holds the recovery lock %s\n", lockfname); + exit(1); + } + } + + r = toku_logger_find_logfiles(dir, &n_logfiles, &logfiles); + if (r!=0) exit(1); + int i; + toku_recover_init(); + for (i=0; iset_errfile(env, stderr); r=env->open(env, ENVDIR, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, 0777); CKERR(r); r=db_create(&db, env, 0); CKERR(r); @@ -35,6 +36,7 @@ static void test (void) { unlink(ENVDIR "/foo.db"); r=db_env_create(&env, 0); assert(r==0); + env->set_errfile(env, stderr); r=env->open(env, ENVDIR, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD|DB_RECOVER, 0777); CKERR(r); r=env->txn_begin(env, 0, &tid, 0); assert(r==0); r=db_create(&db, env, 0); CKERR(r); diff --git a/src/tests/test_log2.c b/src/tests/test_log2.c index 48a15fadb9d..d20ef8b69ee 100644 --- a/src/tests/test_log2.c +++ b/src/tests/test_log2.c @@ -23,6 +23,7 @@ static void make_db (void) { system("rm -rf " ENVDIR); r=mkdir(ENVDIR, 0777); assert(r==0); r=db_env_create(&env, 0); assert(r==0); + env->set_errfile(env, stderr); r=env->open(env, ENVDIR, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, 0777); CKERR(r); r=db_create(&db, env, 0); CKERR(r); r=env->txn_begin(env, 0, &tid, 0); assert(r==0); diff --git a/src/ydb.c b/src/ydb.c index c0124ff0a4e..e948b5cf991 100644 --- a/src/ydb.c +++ b/src/ydb.c @@ -7,8 +7,11 @@ const char *toku_patent_string = "The technology is licensed by the Massachusett const char *toku_copyright_string = "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved."; #include +#include #include +#include #include +#include #include #include #include @@ -16,10 +19,8 @@ const char *toku_copyright_string = "Copyright (c) 2007, 2008 Tokutek Inc. All #include #include #include -#include +#include #include -#include -#include #include "ydb-internal.h" @@ -222,9 +223,35 @@ cleanup: #endif +static int do_recovery (DB_ENV *env) { + const char *datadir=env->i->dir; + char *logdir; + if (env->i->lg_dir) { + logdir = construct_full_name(env->i->dir, env->i->lg_dir); + } else { + logdir = strdup(env->i->dir); + } + + // want to do recovery in its own process + pid_t pid; + if ((pid=fork())==0) { + int r=tokudb_recover(datadir, logdir); + assert(r==0); + exit(0); + } + int status; + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status)!=0) { + return toku_ydb_do_error(env, -1, "Recovery failed\n"); + } + toku_free(logdir); + return 0; +} + static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mode) { HANDLE_PANICKED_ENV(env); int r; + u_int32_t unused_flags=flags; if (env_opened(env)) { return toku_ydb_do_error(env, EINVAL, "The environment is already open\n"); @@ -242,6 +269,8 @@ static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mo else if ((flags & DB_USE_ENVIRON) || ((flags & DB_USE_ENVIRON_ROOT) && geteuid() == 0)) home = getenv("DB_HOME"); + unused_flags &= ~DB_USE_ENVIRON & ~DB_USE_ENVIRON_ROOT; + if (!home) home = "."; // Verify that the home exists. @@ -256,6 +285,7 @@ static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mo if (!(flags & DB_PRIVATE)) { return toku_ydb_do_error(env, EINVAL, "TokuDB requires DB_PRIVATE when opening an env\n"); } + unused_flags &= ~DB_PRIVATE; if (env->i->dir) toku_free(env->i->dir); @@ -277,6 +307,13 @@ static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mo env->i->open_flags = flags; env->i->open_mode = mode; + unused_flags &= ~DB_INIT_TXN & ~DB_INIT_LOG; + + if (flags&DB_RECOVER) { + r=do_recovery(env); + if (r!=0) return r; + } + if (flags & (DB_INIT_TXN | DB_INIT_LOG)) { char* full_dir = NULL; if (env->i->lg_dir) full_dir = construct_full_name(env->i->dir, env->i->lg_dir); @@ -291,6 +328,23 @@ static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mo } } + unused_flags &= ~DB_INIT_MPOOL; // we always init an mpool. + unused_flags &= ~DB_CREATE; // we always do DB_CREATE + unused_flags &= ~DB_INIT_LOCK; // we check this later (e.g. in db->open) + unused_flags &= ~DB_RECOVER; + +// This is probably correct, but it will be pain... +// if ((flags & DB_THREAD)==0) { +// return toku_ydb_do_error(env, EINVAL, "TokuDB requires DB_THREAD"); +// } + unused_flags &= ~DB_THREAD; + + if (unused_flags!=0) { + static char string[100]; + snprintf(string, 100, "Extra flags not understood by tokudb: %d\n", unused_flags); + return toku_ydb_do_error(env, EINVAL, string); + } + r = toku_brt_create_cachetable(&env->i->cachetable, env->i->cachetable_size, ZERO_LSN, env->i->logger); if (r!=0) goto died2;