Commit 8beace59 authored by Bradley C. Kuszmaul's avatar Bradley C. Kuszmaul

Make recovery work when called from {{{DB_ENV->open}}}. Fixes #483.

git-svn-id: file:///svn/tokudb@2768 c7de825b-a66e-492c-adef-691d508d4ae1
parent af54f9ee
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# GCOV_FLAGS = -fprofile-arcs -ftest-coverage # GCOV_FLAGS = -fprofile-arcs -ftest-coverage
# PROF_FLAGS = -pg # PROF_FLAGS = -pg
# OPTFLAGS = -O2 OPTFLAGS = -O2
ifeq ($(VERBOSE),2) ifeq ($(VERBOSE),2)
VERBVERBOSE=-v VERBVERBOSE=-v
...@@ -42,7 +42,7 @@ endif ...@@ -42,7 +42,7 @@ endif
# When debugging, try: valgrind --show-reachable=yes --leak-check=full ./brt-test # 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. # Put these one-per-line so that if we insert a new one the svn diff can understand it better.
# Also keep them sorted. # Also keep them sorted.
REGRESSION_TESTS = \ REGRESSION_TESTS = \
...@@ -84,9 +84,9 @@ tdb_logprint: LDFLAGS+=-lz ...@@ -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.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 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.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 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
......
...@@ -153,4 +153,6 @@ int toku_logger_abort(TOKUTXN); ...@@ -153,4 +153,6 @@ int toku_logger_abort(TOKUTXN);
// Return 0 if there is a live txn with that txnid. // Return 0 if there is a live txn with that txnid.
int toku_txnid2txn (TOKULOGGER logger, TXNID txnid, TOKUTXN *result); int toku_txnid2txn (TOKULOGGER logger, TXNID txnid, TOKUTXN *result);
int tokudb_recover(const char *datadir, const char *logdir);
#endif #endif
...@@ -12,29 +12,66 @@ ...@@ -12,29 +12,66 @@
#include "log-internal.h" #include "log-internal.h"
#include "log_header.h" #include "log_header.h"
#include "toku_assert.h" #include "toku_assert.h"
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
int main (int argc, char *argv[]) { int tokudb_recover(const char *data_dir, const char *log_dir) {
const char *dir;
int r; int r;
int entrycount=0; int entrycount=0;
assert(argc==2);
dir = argv[1];
int n_logfiles; int n_logfiles;
char **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); if (r!=0) exit(1);
int i; int i;
toku_recover_init(); 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<n_logfiles; i++) { for (i=0; i<n_logfiles; i++) {
//fprintf(stderr, "Opening %s\n", logfiles[i]); //fprintf(stderr, "Opening %s\n", logfiles[i]);
r=chdir(org_wd);
assert(r==0);
FILE *f = fopen(logfiles[i], "r"); FILE *f = fopen(logfiles[i], "r");
struct log_entry le; struct log_entry le;
u_int32_t version; u_int32_t version;
r=toku_read_and_print_logmagic(f, &version); r=toku_read_and_print_logmagic(f, &version);
assert(r==0 && version==0); assert(r==0 && version==0);
r=chdir(data_wd);
assert(r==0);
while ((r = toku_log_fread(f, &le))==0) { while ((r = toku_log_fread(f, &le))==0) {
//printf("%lld: Got cmd %c\n", le.u.commit.lsn.lsn, le.cmd); //printf("%lld: Got cmd %c\n", le.u.commit.lsn.lsn, le.cmd);
logtype_dispatch_args(&le, toku_recover_); logtype_dispatch_args(&le, toku_recover_);
...@@ -56,5 +93,10 @@ int main (int argc, char *argv[]) { ...@@ -56,5 +93,10 @@ int main (int argc, char *argv[]) {
toku_free(logfiles[i]); toku_free(logfiles[i]);
} }
toku_free(logfiles); toku_free(logfiles);
r=flock(lockfd, LOCK_UN);
//printf("%s:%d recovery successful! ls -l says\n", __FILE__, __LINE__);
//system("ls -l");
return 0; return 0;
} }
/* -*- mode: C; c-basic-offset: 4 -*- */
#ident "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved."
/* Recover an env. The logs are in argv[1]. The new database is created in the cwd. */
// Test:
// cd ../src/tests/tmpdir
// ../../../newbrt/recover ../dir.test_log2.c.tdb
#include "cachetable.h"
#include "key.h"
#include "log-internal.h"
#include "log_header.h"
#include "toku_assert.h"
#include <fcntl.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
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; i<n_logfiles; i++) {
//fprintf(stderr, "Opening %s\n", logfiles[i]);
FILE *f = fopen(logfiles[i], "r");
struct log_entry le;
u_int32_t version;
r=toku_read_and_print_logmagic(f, &version);
assert(r==0 && version==0);
while ((r = toku_log_fread(f, &le))==0) {
//printf("%lld: Got cmd %c\n", le.u.commit.lsn.lsn, le.cmd);
logtype_dispatch_args(&le, toku_recover_);
entrycount++;
}
if (r!=EOF) {
if (r==DB_BADFORMAT) {
fprintf(stderr, "Bad log format at record %d\n", entrycount);
exit(1);
} else {
fprintf(stderr, "Huh? %s\n", strerror(r));
exit(1);
}
}
fclose(f);
}
toku_recover_cleanup();
for (i=0; i<n_logfiles; i++) {
toku_free(logfiles[i]);
}
toku_free(logfiles);
return 0;
}
...@@ -48,7 +48,7 @@ clean: ...@@ -48,7 +48,7 @@ clean:
ydb.o: ../include/db.h ../newbrt/cachetable.h ../newbrt/brt.h ../newbrt/log.c ydb.o: ../include/db.h ../newbrt/cachetable.h ../newbrt/brt.h ../newbrt/log.c
DBBINS = ydb.o errors.o elocks.o ../newbrt/brt.o ../newbrt/brt-serialize.o ../newbrt/brt-verify.o ../newbrt/cachetable.o ../newbrt/fifo.o ../newbrt/key.o ../newbrt/memory.o ../newbrt/mempool.o ../newbrt/pma.o ../newbrt/ybt.o ../newbrt/primes.o ../newbrt/log.o ../newbrt/fingerprint.o ../newbrt/log_code.o ../newbrt/roll.o ../newbrt/toku_assert.o DBBINS = ydb.o errors.o elocks.o ../newbrt/brt.o ../newbrt/brt-serialize.o ../newbrt/brt-verify.o ../newbrt/cachetable.o ../newbrt/fifo.o ../newbrt/key.o ../newbrt/memory.o ../newbrt/mempool.o ../newbrt/pma.o ../newbrt/ybt.o ../newbrt/primes.o ../newbrt/log.o ../newbrt/fingerprint.o ../newbrt/log_code.o ../newbrt/roll.o ../newbrt/toku_assert.o ../newbrt/recover.o
RANGETREE_BINS = range_tree/rangetree.o range_tree/tokuredblack.o RANGETREE_BINS = range_tree/rangetree.o range_tree/tokuredblack.o
LOCKTREE_BINS = lock_tree/locktree.o lock_tree/rth.o lock_tree/lth.o $(RANGETREE_BINS) LOCKTREE_BINS = lock_tree/locktree.o lock_tree/rth.o lock_tree/lth.o $(RANGETREE_BINS)
......
...@@ -18,6 +18,7 @@ static void test (void) { ...@@ -18,6 +18,7 @@ static void test (void) {
DBT key,data; DBT key,data;
r=db_env_create(&env, 0); 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|DB_THREAD, 0777); CKERR(r); 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); r=db_create(&db, env, 0); CKERR(r);
...@@ -35,6 +36,7 @@ static void test (void) { ...@@ -35,6 +36,7 @@ static void test (void) {
unlink(ENVDIR "/foo.db"); unlink(ENVDIR "/foo.db");
r=db_env_create(&env, 0); 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|DB_THREAD|DB_RECOVER, 0777); CKERR(r); 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=env->txn_begin(env, 0, &tid, 0); assert(r==0);
r=db_create(&db, env, 0); CKERR(r); r=db_create(&db, env, 0); CKERR(r);
......
...@@ -23,6 +23,7 @@ static void make_db (void) { ...@@ -23,6 +23,7 @@ static void make_db (void) {
system("rm -rf " ENVDIR); system("rm -rf " ENVDIR);
r=mkdir(ENVDIR, 0777); assert(r==0); r=mkdir(ENVDIR, 0777); assert(r==0);
r=db_env_create(&env, 0); 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=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=db_create(&db, env, 0); CKERR(r);
r=env->txn_begin(env, 0, &tid, 0); assert(r==0); r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
......
...@@ -7,8 +7,11 @@ const char *toku_patent_string = "The technology is licensed by the Massachusett ...@@ -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."; const char *toku_copyright_string = "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved.";
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <libgen.h>
#include <limits.h> #include <limits.h>
#include <pthread.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -16,10 +19,8 @@ const char *toku_copyright_string = "Copyright (c) 2007, 2008 Tokutek Inc. All ...@@ -16,10 +19,8 @@ const char *toku_copyright_string = "Copyright (c) 2007, 2008 Tokutek Inc. All
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <ctype.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h>
#include <pthread.h>
#include "ydb-internal.h" #include "ydb-internal.h"
...@@ -222,9 +223,35 @@ static int env_read_config(DB_ENV *env) { ...@@ -222,9 +223,35 @@ static int env_read_config(DB_ENV *env) {
#endif #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) { static int toku_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mode) {
HANDLE_PANICKED_ENV(env); HANDLE_PANICKED_ENV(env);
int r; int r;
u_int32_t unused_flags=flags;
if (env_opened(env)) { if (env_opened(env)) {
return toku_ydb_do_error(env, EINVAL, "The environment is already open\n"); 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 ...@@ -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) || else if ((flags & DB_USE_ENVIRON) ||
((flags & DB_USE_ENVIRON_ROOT) && geteuid() == 0)) home = getenv("DB_HOME"); ((flags & DB_USE_ENVIRON_ROOT) && geteuid() == 0)) home = getenv("DB_HOME");
unused_flags &= ~DB_USE_ENVIRON & ~DB_USE_ENVIRON_ROOT;
if (!home) home = "."; if (!home) home = ".";
// Verify that the home exists. // 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 ...@@ -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)) { if (!(flags & DB_PRIVATE)) {
return toku_ydb_do_error(env, EINVAL, "TokuDB requires DB_PRIVATE when opening an env\n"); return toku_ydb_do_error(env, EINVAL, "TokuDB requires DB_PRIVATE when opening an env\n");
} }
unused_flags &= ~DB_PRIVATE;
if (env->i->dir) if (env->i->dir)
toku_free(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 ...@@ -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_flags = flags;
env->i->open_mode = mode; 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)) { if (flags & (DB_INIT_TXN | DB_INIT_LOG)) {
char* full_dir = NULL; char* full_dir = NULL;
if (env->i->lg_dir) full_dir = construct_full_name(env->i->dir, env->i->lg_dir); 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 ...@@ -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); r = toku_brt_create_cachetable(&env->i->cachetable, env->i->cachetable_size, ZERO_LSN, env->i->logger);
if (r!=0) goto died2; if (r!=0) goto died2;
......
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