2007-11-29 14:18:54 +00:00
/* -*- mode: C; c-basic-offset: 4 -*- */
2008-01-24 15:10:32 +00:00
# ident "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved."
2007-11-29 14:18:54 +00:00
2007-11-23 18:27:50 +00:00
/* 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
2013-04-16 23:57:20 -04:00
# include "includes.h"
2007-11-23 20:36:03 +00:00
2008-04-07 01:30:25 +00:00
//#define DO_VERIFY_COUNTS
2008-03-14 19:14:31 +00:00
# ifdef DO_VERIFY_COUNTS
# define VERIFY_COUNTS(n) toku_verify_counts(n)
# else
# define VERIFY_COUNTS(n) ((void)0)
# endif
static DB * const null_db = 0 ;
// These data structures really should be part of a recovery data structure. Recovery could be multithreaded (on different environments...) But this is OK since recovery can only happen in one
static CACHETABLE ct ;
static struct cf_pair {
FILENUM filenum ;
CACHEFILE cf ;
BRT brt ; // set to zero on an fopen, but filled in when an fheader is seen.
} * cf_pairs ;
2013-04-16 23:57:20 -04:00
static int n_cf_pairs = 0 , max_cf_pairs = 0 ;
2008-03-14 19:14:31 +00:00
int toku_recover_init ( void ) {
int r = toku_create_cachetable ( & ct , 1 < < 25 , ( LSN ) { 0 } , 0 ) ;
return r ;
}
void toku_recover_cleanup ( void ) {
int i ;
for ( i = 0 ; i < n_cf_pairs ; i + + ) {
if ( cf_pairs [ i ] . brt ) {
2013-04-16 23:57:38 -04:00
int r = toku_close_brt ( cf_pairs [ i ] . brt , 0 , 0 ) ;
2008-03-14 19:14:31 +00:00
//r = toku_cachefile_close(&cf_pairs[i].cf);
assert ( r = = 0 ) ;
}
}
toku_free ( cf_pairs ) ;
{
int r = toku_cachetable_close ( & ct ) ;
assert ( r = = 0 ) ;
}
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_commit ( LSN UU ( lsn ) , TXNID UU ( txnid ) ) {
2008-03-14 19:14:31 +00:00
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_xabort ( LSN UU ( lsn ) , TXNID UU ( txnid ) ) {
2008-04-21 04:22:45 +00:00
}
2008-03-14 19:14:31 +00:00
2013-04-16 23:57:20 -04:00
static void
create_dir_from_file ( const char * fname ) {
2008-04-18 14:45:04 +00:00
int i ;
char * tmp = toku_strdup ( fname ) ;
char ch ;
for ( i = 0 ; ( ch = fname [ i ] ) ; i + + ) {
2013-04-16 23:57:29 -04:00
//
// TODO: this may fail in windows, double check the absolute path names
// and '/' as the directory delimiter or something
//
2008-04-18 14:45:04 +00:00
if ( ch = = ' / ' ) {
if ( i > 0 ) {
tmp [ i ] = 0 ;
mode_t oldu = umask ( 0 ) ;
2013-04-16 23:57:28 -04:00
int r = toku_os_mkdir ( tmp , S_IRWXU ) ;
2008-04-18 14:45:04 +00:00
if ( r ! = 0 & & errno ! = EEXIST ) {
printf ( " error: %s \n " , strerror ( errno ) ) ;
}
assert ( r = = 0 | | ( errno = = EEXIST ) ) ;
umask ( oldu ) ;
tmp [ i ] = ch ;
}
}
}
toku_free ( tmp ) ;
}
2013-04-16 23:57:20 -04:00
static void
2013-04-16 23:57:40 -04:00
toku_recover_fcreate ( LSN UU ( lsn ) , TXNID UU ( txnid ) , FILENUM UU ( filenum ) , BYTESTRING fname , u_int32_t mode ) {
2008-03-14 19:14:31 +00:00
char * fixed_fname = fixup_fname ( & fname ) ;
2008-04-18 14:45:04 +00:00
create_dir_from_file ( fixed_fname ) ;
2013-04-16 23:57:27 -04:00
int fd = open ( fixed_fname , O_CREAT + O_TRUNC + O_WRONLY + O_BINARY , mode ) ;
2008-03-14 19:14:31 +00:00
assert ( fd > = 0 ) ;
toku_free ( fixed_fname ) ;
toku_free_BYTESTRING ( fname ) ;
2013-04-16 23:57:35 -04:00
int r = close ( fd ) ;
assert ( r = = 0 ) ;
2008-03-14 19:14:31 +00:00
}
2013-04-16 23:57:20 -04:00
static int
toku_recover_note_cachefile ( FILENUM fnum , CACHEFILE cf , BRT brt ) {
2008-03-14 19:14:31 +00:00
if ( max_cf_pairs = = 0 ) {
n_cf_pairs = 1 ;
max_cf_pairs = 2 ;
MALLOC_N ( max_cf_pairs , cf_pairs ) ;
if ( cf_pairs = = 0 ) return errno ;
} else {
if ( n_cf_pairs > = max_cf_pairs ) {
max_cf_pairs * = 2 ;
cf_pairs = toku_realloc ( cf_pairs , max_cf_pairs * sizeof ( * cf_pairs ) ) ;
}
n_cf_pairs + + ;
}
cf_pairs [ n_cf_pairs - 1 ] . filenum = fnum ;
cf_pairs [ n_cf_pairs - 1 ] . cf = cf ;
cf_pairs [ n_cf_pairs - 1 ] . brt = brt ;
return 0 ;
}
static int find_cachefile ( FILENUM fnum , struct cf_pair * * cf_pair ) {
int i ;
for ( i = 0 ; i < n_cf_pairs ; i + + ) {
if ( fnum . fileid = = cf_pairs [ i ] . filenum . fileid ) {
* cf_pair = cf_pairs + i ;
return 0 ;
}
}
return 1 ;
}
static void toku_recover_fheader ( LSN UU ( lsn ) , TXNID UU ( txnid ) , FILENUM filenum , LOGGEDBRTHEADER header ) {
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
struct brt_header * MALLOC ( h ) ;
assert ( h ) ;
h - > dirty = 0 ;
2013-04-16 23:57:38 -04:00
h - > panic = 0 ;
h - > panic_string = 0 ;
2013-04-16 23:57:35 -04:00
XMALLOC ( h - > flags_array ) ;
2008-05-22 21:28:00 +00:00
h - > flags_array [ 0 ] = header . flags ;
2008-03-14 19:14:31 +00:00
h - > nodesize = header . nodesize ;
2013-04-16 23:57:41 -04:00
assert ( h - > blocktable /* Not initialized. Is this used? */ ) ;
toku_block_recovery_set_free_blocks ( h - > blocktable , header . free_blocks ) ;
toku_block_recovery_set_unused_blocks ( h - > blocktable , header . unused_blocks ) ;
2008-03-14 19:14:31 +00:00
h - > n_named_roots = header . n_named_roots ;
2008-04-09 02:45:27 +00:00
r = toku_fifo_create ( & h - > fifo ) ;
assert ( r = = 0 ) ;
2008-03-14 19:14:31 +00:00
if ( ( signed ) header . n_named_roots = = - 1 ) {
2008-06-18 21:38:01 +00:00
MALLOC_N ( 1 , h - > roots ) ; assert ( h - > roots ) ;
MALLOC_N ( 1 , h - > root_hashes ) ; assert ( h - > root_hashes ) ;
2008-05-22 21:28:00 +00:00
h - > roots [ 0 ] = header . u . one . root ;
2008-06-18 21:38:01 +00:00
h - > root_hashes [ 0 ] . valid = FALSE ;
2008-03-14 19:14:31 +00:00
} else {
assert ( 0 ) ;
}
2013-04-16 23:57:18 -04:00
//toku_cachetable_put(pair->cf, header_blocknum, fullhash, h, 0, toku_brtheader_flush_callback, toku_brtheader_fetch_callback, 0);
2008-03-14 19:14:31 +00:00
if ( pair - > brt ) {
2008-04-18 14:45:04 +00:00
toku_free ( pair - > brt - > h ) ;
2008-03-14 19:14:31 +00:00
} else {
2008-05-03 17:34:14 +00:00
r = toku_brt_create ( & pair - > brt ) ;
assert ( r = = 0 ) ;
2008-03-14 19:14:31 +00:00
pair - > brt - > cf = pair - > cf ;
pair - > brt - > database_name = 0 ; // Special case, we don't know or care what the database name is for recovery.
list_init ( & pair - > brt - > cursors ) ;
pair - > brt - > compare_fun = 0 ;
pair - > brt - > dup_compare = 0 ;
pair - > brt - > db = 0 ;
}
pair - > brt - > h = h ;
pair - > brt - > nodesize = h - > nodesize ;
pair - > brt - > flags = h - > nodesize ;
2013-04-16 23:57:29 -04:00
toku_cachefile_set_userdata ( pair - > cf , pair - > brt - > h , toku_brtheader_close , toku_brtheader_checkpoint ) ;
2008-03-14 19:14:31 +00:00
}
2008-05-02 14:38:35 +00:00
static void toku_recover_deqrootentry ( LSN lsn __attribute__ ( ( __unused__ ) ) , FILENUM filenum ) {
2008-04-09 02:45:27 +00:00
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
2013-04-16 23:57:18 -04:00
//void *h_v;
//r = toku_cachetable_get_and_pin(pair->cf, header_blocknum, fullhash,
// &h_v, NULL, toku_brtheader_flush_callback, toku_brtheader_fetch_callback, 0);
struct brt_header * h = 0 ;
2008-04-09 02:45:27 +00:00
bytevec storedkey , storeddata ;
ITEMLEN storedkeylen , storeddatalen ;
TXNID storedxid ;
u_int32_t storedtype ;
r = toku_fifo_peek ( h - > fifo , & storedkey , & storedkeylen , & storeddata , & storeddatalen , & storedtype , & storedxid ) ;
assert ( r = = 0 ) ;
r = toku_fifo_deq ( h - > fifo ) ;
assert ( r = = 0 ) ;
2013-04-16 23:57:38 -04:00
//r = toku_cachetable_unpin(pair->cf, header_blocknum, fullhash, CACHETABLE_DIRTY, 0);
2013-04-16 23:57:18 -04:00
//assert(r==0);
2008-04-09 02:45:27 +00:00
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_enqrootentry ( LSN lsn __attribute__ ( ( __unused__ ) ) , FILENUM filenum , TXNID xid , u_int32_t typ , BYTESTRING key , BYTESTRING val ) {
2008-04-09 02:45:27 +00:00
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
void * h_v ;
2013-04-16 23:57:18 -04:00
u_int32_t fullhash = toku_cachetable_hash ( pair - > cf , header_blocknum ) ;
2013-04-16 23:57:18 -04:00
if ( 0 ) {
//r = toku_cachetable_get_and_pin(pair->cf, header_blocknum, fullhash, &h_v, NULL, toku_brtheader_flush_callback, toku_brtheader_fetch_callback, 0);
} else {
h_v = 0 ;
assert ( 0 ) ;
}
2008-04-09 02:45:27 +00:00
assert ( r = = 0 ) ;
struct brt_header * h = h_v ;
2013-04-16 23:57:41 -04:00
r = toku_fifo_enq ( h - > fifo , key . data , key . len , val . data , val . len , typ , xid ) ;
2008-04-09 02:45:27 +00:00
assert ( r = = 0 ) ;
2013-04-16 23:57:38 -04:00
r = toku_cachetable_unpin ( pair - > cf , header_blocknum , fullhash , CACHETABLE_DIRTY , 0 ) ;
2008-04-09 02:45:27 +00:00
assert ( r = = 0 ) ;
toku_free ( key . data ) ;
toku_free ( val . data ) ;
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_fopen ( LSN UU ( lsn ) , TXNID UU ( txnid ) , BYTESTRING fname , FILENUM filenum ) {
2008-03-14 19:14:31 +00:00
char * fixedfname = fixup_fname ( & fname ) ;
CACHEFILE cf ;
2013-04-16 23:57:27 -04:00
int fd = open ( fixedfname , O_RDWR + O_BINARY , 0 ) ;
2008-03-14 19:14:31 +00:00
assert ( fd > = 0 ) ;
2008-05-04 16:56:15 +00:00
BRT brt = 0 ;
2008-05-03 17:34:14 +00:00
int r = toku_brt_create ( & brt ) ;
assert ( r = = 0 ) ;
2008-04-17 03:11:55 +00:00
brt - > fname = fixedfname ;
brt - > database_name = 0 ;
2008-03-14 19:14:31 +00:00
brt - > h = 0 ;
brt - > compare_fun = 0 ;
brt - > dup_compare = 0 ;
brt - > db = 0 ;
2008-05-03 17:34:14 +00:00
r = toku_cachetable_openfd ( & cf , ct , fd , fixedfname ) ;
2008-03-14 19:14:31 +00:00
assert ( r = = 0 ) ;
brt - > cf = cf ;
toku_recover_note_cachefile ( filenum , cf , brt ) ;
toku_free_BYTESTRING ( fname ) ;
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_brtclose ( LSN UU ( lsn ) , BYTESTRING UU ( fname ) , FILENUM filenum ) {
2008-04-17 03:11:55 +00:00
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
// Bump up the reference count
toku_cachefile_refup ( pair - > cf ) ;
2013-04-16 23:57:38 -04:00
r = toku_close_brt ( pair - > brt , 0 , 0 ) ;
2008-04-17 03:11:55 +00:00
assert ( r = = 0 ) ;
pair - > brt = 0 ;
toku_free_BYTESTRING ( fname ) ;
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_cfclose ( LSN UU ( lsn ) , BYTESTRING UU ( fname ) , FILENUM filenum ) {
2008-04-17 03:11:55 +00:00
int i ;
for ( i = 0 ; i < n_cf_pairs ; i + + ) {
if ( filenum . fileid = = cf_pairs [ i ] . filenum . fileid ) {
2013-04-16 23:57:38 -04:00
int r = toku_cachefile_close ( & cf_pairs [ i ] . cf , 0 , 0 ) ;
2008-04-17 03:11:55 +00:00
assert ( r = = 0 ) ;
cf_pairs [ i ] = cf_pairs [ n_cf_pairs - 1 ] ;
n_cf_pairs - - ;
break ;
}
}
toku_free_BYTESTRING ( fname ) ;
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_changeunnamedroot ( LSN UU ( lsn ) , FILENUM filenum , BLOCKNUM UU ( oldroot ) , BLOCKNUM newroot ) {
2008-03-14 19:14:31 +00:00
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
assert ( pair - > brt ) ;
2013-04-16 23:57:18 -04:00
assert ( pair - > brt - > h ) ;
2008-05-22 21:28:00 +00:00
pair - > brt - > h - > roots [ 0 ] = newroot ;
2008-06-18 21:38:01 +00:00
pair - > brt - > h - > root_hashes [ 0 ] . valid = FALSE ;
2008-03-14 19:14:31 +00:00
}
2013-04-16 23:57:20 -04:00
static void
toku_recover_changenamedroot ( LSN UU ( lsn ) , FILENUM UU ( filenum ) , BYTESTRING UU ( name ) , BLOCKNUM UU ( oldroot ) , BLOCKNUM UU ( newroot ) ) { assert ( 0 ) ; }
2008-03-14 19:14:31 +00:00
2013-04-16 23:57:20 -04:00
static void
toku_recover_changeunusedmemory ( LSN UU ( lsn ) , FILENUM filenum , BLOCKNUM UU ( oldunused ) , BLOCKNUM newunused ) {
2008-03-14 19:14:31 +00:00
struct cf_pair * pair = NULL ;
int r = find_cachefile ( filenum , & pair ) ;
assert ( r = = 0 ) ;
assert ( pair - > brt ) ;
2013-04-16 23:57:18 -04:00
assert ( pair - > brt - > h ) ;
2013-04-16 23:57:41 -04:00
toku_block_recovery_set_unused_blocks ( pair - > brt - > h - > blocktable , newunused ) ;
2008-03-14 19:14:31 +00:00
}
2008-03-21 19:40:32 +00:00
static int toku_recover_checkpoint ( LSN UU ( lsn ) ) {
return 0 ;
}
2008-03-14 19:14:31 +00:00
2008-03-21 21:02:30 +00:00
static int toku_recover_xbegin ( LSN UU ( lsn ) , TXNID UU ( parent ) ) {
return 0 ;
}
2013-04-16 23:57:39 -04:00
static int toku_delete_rolltmp_files ( const char * log_dir ) {
struct dirent * de ;
DIR * d = opendir ( log_dir ) ;
if ( d = = 0 ) {
return errno ;
}
int result = 0 ;
while ( ( de = readdir ( d ) ) ) {
char rolltmp_prefix [ ] = " __rolltmp. " ;
int r = memcmp ( de - > d_name , rolltmp_prefix , sizeof ( rolltmp_prefix ) - 1 ) ;
if ( r = = 0 ) {
int fnamelen = strlen ( log_dir ) + strlen ( de - > d_name ) + 2 ; // One for the slash and one for the trailing NUL.
char fname [ fnamelen ] ;
2013-04-16 23:57:42 -04:00
int l = snprintf ( fname , fnamelen , " %s/%s " , log_dir , de - > d_name ) ;
assert ( l + 1 = = fnamelen ) ;
2013-04-16 23:57:39 -04:00
r = unlink ( fname ) ;
if ( r ! = 0 ) {
result = errno ;
perror ( " Trying to delete a rolltmp file " ) ;
}
}
}
{
int r = closedir ( d ) ;
if ( r = = - 1 ) return errno ;
}
return result ;
}
2008-03-12 19:40:38 +00:00
int tokudb_recover ( const char * data_dir , const char * log_dir ) {
2013-04-16 23:57:39 -04:00
int failresult = 0 ;
2007-11-23 18:27:50 +00:00
int r ;
2007-12-26 16:52:55 +00:00
int entrycount = 0 ;
2007-11-23 18:27:50 +00:00
char * * logfiles ;
2008-03-12 19:40:38 +00:00
int lockfd ;
{
2013-04-16 23:57:27 -04:00
const char fname [ ] = " /__recoverylock_dont_delete_me " ;
2008-03-12 19:40:38 +00:00
int namelen = strlen ( data_dir ) ;
2013-04-16 23:57:27 -04:00
char lockfname [ namelen + sizeof ( fname ) ] ;
2008-03-12 19:40:38 +00:00
2013-04-16 23:57:42 -04:00
int l = snprintf ( lockfname , sizeof ( lockfname ) , " %s%s " , data_dir , fname ) ;
assert ( l + 1 = = ( signed ) ( sizeof ( lockfname ) ) ) ;
2013-04-16 23:57:28 -04:00
lockfd = toku_os_lock_file ( lockfname ) ;
2008-03-12 19:40:38 +00:00
if ( lockfd < 0 ) {
printf ( " Couldn't run recovery because some other process holds the recovery lock %s \n " , lockfname ) ;
2008-03-14 11:02:28 +00:00
return errno ;
2008-03-12 19:40:38 +00:00
}
}
2013-04-16 23:57:39 -04:00
r = toku_delete_rolltmp_files ( log_dir ) ;
if ( r ! = 0 ) { failresult = r ; goto fail ; }
2008-03-21 19:40:32 +00:00
r = toku_logger_find_logfiles ( log_dir , & logfiles ) ;
2013-04-16 23:57:39 -04:00
if ( r ! = 0 ) { failresult = r ; goto fail ; }
2007-11-23 18:27:50 +00:00
int i ;
2008-01-11 03:09:14 +00:00
toku_recover_init ( ) ;
2008-03-12 19:40:38 +00:00
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);
}
2008-03-21 19:40:32 +00:00
for ( i = 0 ; logfiles [ i ] ; i + + ) {
2007-12-22 20:56:20 +00:00
//fprintf(stderr, "Opening %s\n", logfiles[i]);
2008-03-12 19:40:38 +00:00
r = chdir ( org_wd ) ;
assert ( r = = 0 ) ;
2007-11-23 18:27:50 +00:00
FILE * f = fopen ( logfiles [ i ] , " r " ) ;
struct log_entry le ;
u_int32_t version ;
2008-03-17 02:40:59 +00:00
//printf("Reading file %s\n", logfiles[i]);
2007-11-28 19:09:24 +00:00
r = toku_read_and_print_logmagic ( f , & version ) ;
2007-11-23 18:27:50 +00:00
assert ( r = = 0 & & version = = 0 ) ;
2008-03-12 19:40:38 +00:00
r = chdir ( data_wd ) ;
assert ( r = = 0 ) ;
2007-11-29 18:14:40 +00:00
while ( ( r = toku_log_fread ( f , & le ) ) = = 0 ) {
2008-03-17 02:40:59 +00:00
//printf("%lld: Got cmd %c\n", (long long)le.u.commit.lsn.lsn, le.cmd);
2008-02-08 22:16:02 +00:00
logtype_dispatch_args ( & le , toku_recover_ ) ;
2007-12-29 19:27:01 +00:00
entrycount + + ;
2007-11-23 18:27:50 +00:00
}
if ( r ! = EOF ) {
if ( r = = DB_BADFORMAT ) {
2008-01-25 21:50:07 +00:00
fprintf ( stderr , " Bad log format at record %d \n " , entrycount ) ;
2008-03-14 11:02:28 +00:00
return r ;
2007-11-23 18:27:50 +00:00
} else {
fprintf ( stderr , " Huh? %s \n " , strerror ( r ) ) ;
2008-03-14 11:02:28 +00:00
return r ;
2007-11-23 18:27:50 +00:00
}
}
fclose ( f ) ;
}
2008-01-11 03:09:14 +00:00
toku_recover_cleanup ( ) ;
2008-03-21 19:40:32 +00:00
for ( i = 0 ; logfiles [ i ] ; i + + ) {
2007-11-23 18:27:50 +00:00
toku_free ( logfiles [ i ] ) ;
}
toku_free ( logfiles ) ;
2008-03-12 19:40:38 +00:00
2013-04-16 23:57:28 -04:00
r = toku_os_unlock_file ( lockfd ) ;
2008-03-14 11:02:28 +00:00
if ( r ! = 0 ) return errno ;
r = chdir ( org_wd ) ;
if ( r ! = 0 ) return errno ;
2008-03-12 19:40:38 +00:00
//printf("%s:%d recovery successful! ls -l says\n", __FILE__, __LINE__);
//system("ls -l");
2007-11-23 18:27:50 +00:00
return 0 ;
2013-04-16 23:57:39 -04:00
fail :
toku_os_unlock_file ( lockfd ) ;
chdir ( org_wd ) ;
return failresult ;
2007-11-23 18:27:50 +00:00
}