Browse Source
close[t:4298] Merge from 4298j: {{{svn merge -c41355 ../tokudb.4298i}}}. Closes #4298.
close[t:4298] Merge from 4298j: {{{svn merge -c41355 ../tokudb.4298i}}}. Closes #4298.
git-svn-id: file:///svn/toku/tokudb@41357 c7de825b-a66e-492c-adef-691d508d4ae1pull/73/head
committed by
Yoni Fogel
93 changed files with 1598 additions and 332 deletions
-
2buildheader/Makefile
-
2db-benchmark-test/db-benchmark-test.c
-
2newbrt/brt-internal.h
-
17newbrt/brt.c
-
8newbrt/brt.h
-
3newbrt/brttypes.h
-
57newbrt/cachetable.c
-
10newbrt/log-internal.h
-
15newbrt/logformat.c
-
34newbrt/logger.c
-
7newbrt/logger.h
-
434newbrt/recover.c
-
21newbrt/recover.h
-
9newbrt/rollback.c
-
9newbrt/tdb-recover.c
-
2newbrt/tests/benchmark-test.c
-
4newbrt/tests/brt-serialize-sub-block-test.c
-
2newbrt/tests/brt-test-cursor-2.c
-
26newbrt/tests/brt-test-cursor.c
-
4newbrt/tests/brt-test-header.c
-
56newbrt/tests/brt-test.c
-
2newbrt/tests/brt-test0.c
-
2newbrt/tests/brt-test1.c
-
2newbrt/tests/brt-test2.c
-
2newbrt/tests/brt-test3.c
-
2newbrt/tests/brt-test4.c
-
2newbrt/tests/brt-test5.c
-
2newbrt/tests/brtloader-test-writer.c
-
2newbrt/tests/brtloader-test.c
-
8newbrt/tests/is_empty.c
-
2newbrt/tests/keyrange.c
-
4newbrt/tests/le-cursor-provdel.c
-
8newbrt/tests/le-cursor-right.c
-
4newbrt/tests/le-cursor-walk.c
-
2newbrt/tests/make-tree.c
-
2newbrt/tests/msnfilter.c
-
2newbrt/tests/orthopush-flush.c
-
9newbrt/tests/recovery-bad-last-entry.c
-
8newbrt/tests/recovery-cbegin-cend-hello.c
-
8newbrt/tests/recovery-cbegin-cend.c
-
8newbrt/tests/recovery-cbegin.c
-
8newbrt/tests/recovery-cend-cbegin.c
-
9newbrt/tests/recovery-datadir-is-file.c
-
8newbrt/tests/recovery-empty.c
-
8newbrt/tests/recovery-fopen-missing-file.c
-
8newbrt/tests/recovery-hello.c
-
8newbrt/tests/recovery-lsn-error-during-forward-scan.c
-
8newbrt/tests/recovery-no-datadir.c
-
8newbrt/tests/recovery-no-log.c
-
8newbrt/tests/recovery-no-logdir.c
-
2newbrt/tests/shortcut.c
-
2newbrt/tests/test-brt-overflow.c
-
4newbrt/tests/test-checkpoint-during-flush.c
-
4newbrt/tests/test-checkpoint-during-merge.c
-
4newbrt/tests/test-checkpoint-during-rebalance.c
-
4newbrt/tests/test-checkpoint-during-split.c
-
2newbrt/tests/test-del-inorder.c
-
2newbrt/tests/test-dirty-flushes-on-cleaner.c
-
2newbrt/tests/test-dump-brt.c
-
2newbrt/tests/test-flushes-on-cleaner.c
-
2newbrt/tests/test-inc-split.c
-
2newbrt/tests/test-merges-on-cleaner.c
-
2newbrt/tests/test-pick-child-to-flush.c
-
2newbrt/tests/test.h
-
2newbrt/tests/test3681.c
-
4newbrt/tests/test3856.c
-
12newbrt/tests/test3884.c
-
2newbrt/tests/test4115.c
-
2newbrt/tests/test4244.c
-
2newbrt/tests/verify-bad-msn.c
-
2newbrt/tests/verify-bad-pivots.c
-
2newbrt/tests/verify-dup-in-leaf.c
-
2newbrt/tests/verify-dup-pivots.c
-
2newbrt/tests/verify-misrouted-msgs.c
-
2newbrt/tests/verify-unsorted-leaf.c
-
2newbrt/tests/verify-unsorted-pivots.c
-
63newbrt/txn.c
-
10newbrt/txn.h
-
6newbrt/wbuf.h
-
2release/examples/db-insert.c
-
16src/tests/Makefile
-
102src/tests/test-prepare.c
-
123src/tests/test-prepare2.c
-
298src/tests/test-prepare3.c
-
7src/tests/test.h
-
54src/tests/test_txn_close_before_commit.c
-
57src/tests/test_txn_close_before_prepare_commit.c
-
81src/ydb.c
-
74src/ydb_db.c
-
5src/ydb_db.h
-
79src/ydb_txn.c
-
1src/ydb_txn.h
-
2windows/tests/test.h
@ -0,0 +1,102 @@ |
|||
#include "test.h" |
|||
#include <sys/wait.h> |
|||
|
|||
#define ENVDIR2 ENVDIR "2" |
|||
|
|||
static void clean_env (const char *envdir) { |
|||
const int len = strlen(envdir)+100; |
|||
char cmd[len]; |
|||
snprintf(cmd, len, "rm -rf %s", envdir); |
|||
system(cmd); |
|||
CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
static void setup_env (DB_ENV **envp, const char *envdir) { |
|||
CHK(db_env_create(envp, 0)); |
|||
(*envp)->set_errfile(*envp, stderr); |
|||
#ifdef TOKUDB |
|||
CHK((*envp)->set_redzone(*envp, 0)); |
|||
#endif |
|||
CHK((*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) { |
|||
DB *db; |
|||
DB_TXN *txn; |
|||
clean_env(envdir); |
|||
setup_env(envp, envdir); |
|||
CKERR(db_create(&db, *envp, 0)); |
|||
CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
CKERR((*envp)->txn_begin(*envp, 0, &txn, 0)); |
|||
DBT key={.size=4, .data="foo"}; |
|||
CKERR(db->put(db, txn, &key, &key, 0)); |
|||
CHK(db->close(db, 0)); |
|||
u_int8_t gid[DB_GID_SIZE]; |
|||
memset(gid, 0, DB_GID_SIZE); |
|||
gid[0]=42; |
|||
CKERR(txn->prepare(txn, gid)); |
|||
if (commit) |
|||
CKERR(txn->commit(txn, 0)); |
|||
} |
|||
|
|||
static void test1 (void) { |
|||
pid_t pid; |
|||
bool do_fork = true; |
|||
if (!do_fork || 0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env_and_prepare(&env, ENVDIR, false); |
|||
{ |
|||
DB_PREPLIST l[1]; |
|||
long count=-1; |
|||
CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST)); |
|||
printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); |
|||
assert(count==1); |
|||
assert(l[0].gid[0]==42); |
|||
} |
|||
exit(0); |
|||
} |
|||
int status; |
|||
if (do_fork) { |
|||
pid_t pid2 = wait(&status); |
|||
assert(pid2==pid); |
|||
} |
|||
|
|||
DB_ENV *env2; |
|||
setup_env_and_prepare(&env2, ENVDIR2, true); |
|||
|
|||
// Now we can look at env2 in the debugger to see if we managed to make it the same |
|||
|
|||
|
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
{ |
|||
DB_PREPLIST l[1]; |
|||
long count=-1; |
|||
int r = env->txn_recover(env, l, 1, &count, DB_FIRST); |
|||
printf("r=%d count=%ld\n", r, count); |
|||
assert(count==1); |
|||
assert(l[0].gid[0]==42); |
|||
for (int i=1; i<DB_GID_SIZE; i++) { |
|||
assert(l[0].gid[i]==0); |
|||
} |
|||
CHK(l->txn->commit(l->txn, 0)); |
|||
} |
|||
CHK(env2->close(env2, 0)); |
|||
CHK(env ->close(env, 0)); |
|||
} |
|||
|
|||
int test_main (int argc, char *const argv[]) { |
|||
default_parse_args(argc, argv); |
|||
// first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment). |
|||
test1(); |
|||
|
|||
|
|||
// second test: poen environment, a db, a txn, prepare, close the environment. Then reopen and do txn_prepare. |
|||
|
|||
// third test: make sure there is an fsync on txn_prepare, but not on the following commit. |
|||
|
|||
|
|||
// Then close the environment Find out what BDB does when ask for the txn prepares. |
|||
// Other tests: read prepared txns, 1 at a time. Then close it and read them again. |
|||
return 0; |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
#include "test.h" |
|||
#include <sys/wait.h> |
|||
|
|||
// Verify that if tokudb crashes during recovery, then the prepared transactions are still prepared. |
|||
|
|||
static void clean_env (const char *envdir) { |
|||
const int len = strlen(envdir)+100; |
|||
char cmd[len]; |
|||
snprintf(cmd, len, "rm -rf %s", envdir); |
|||
system(cmd); |
|||
CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
static void setup_env (DB_ENV **envp, const char *envdir) { |
|||
CHK(db_env_create(envp, 0)); |
|||
(*envp)->set_errfile(*envp, stderr); |
|||
#ifdef TOKUDB |
|||
CHK((*envp)->set_redzone(*envp, 0)); |
|||
#endif |
|||
CHK((*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) { |
|||
DB *db; |
|||
DB_TXN *txn; |
|||
clean_env(envdir); |
|||
setup_env(envp, envdir); |
|||
CKERR(db_create(&db, *envp, 0)); |
|||
CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
CKERR((*envp)->txn_begin(*envp, 0, &txn, 0)); |
|||
DBT key={.size=4, .data="foo"}; |
|||
CKERR(db->put(db, txn, &key, &key, 0)); |
|||
CHK(db->close(db, 0)); |
|||
u_int8_t gid[DB_GID_SIZE]; |
|||
memset(gid, 0, DB_GID_SIZE); |
|||
gid[0]=42; |
|||
CKERR(txn->prepare(txn, gid)); |
|||
if (commit) |
|||
CKERR(txn->commit(txn, 0)); |
|||
} |
|||
|
|||
static void test (void) { |
|||
pid_t pid; |
|||
|
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env_and_prepare(&env, ENVDIR, false); |
|||
{ |
|||
DB_PREPLIST l[1]; |
|||
long count=-1; |
|||
CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST)); |
|||
printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); |
|||
assert(count==1); |
|||
assert(l[0].gid[0]==42); |
|||
} |
|||
exit(0); |
|||
} |
|||
{ |
|||
int status; |
|||
pid_t pid2 = wait(&status); |
|||
assert(pid2==pid); |
|||
assert(WIFEXITED(status) && WEXITSTATUS(status)==0); |
|||
} |
|||
// Now run recovery and crash on purpose. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
|
|||
// make sure there is 1 prepared txn. |
|||
{ |
|||
DB_PREPLIST l[1]; |
|||
long count=-1; |
|||
int r = env->txn_recover(env, l, 1, &count, DB_FIRST); |
|||
printf("r=%d count=%ld\n", r, count); |
|||
assert(count==1); |
|||
assert(l[0].gid[0]==42); |
|||
for (int i=1; i<DB_GID_SIZE; i++) { |
|||
assert(l[0].gid[i]==0); |
|||
} |
|||
} |
|||
|
|||
exit(0); |
|||
} |
|||
{ |
|||
int status; |
|||
pid_t pid2 = wait(&status); |
|||
assert(pid2==pid); |
|||
assert(WIFEXITED(status) && WEXITSTATUS(status)==0); |
|||
} |
|||
|
|||
// Now see if recovery works the second time. |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
{ |
|||
DB_PREPLIST l[1]; |
|||
long count=-1; |
|||
int r = env->txn_recover(env, l, 1, &count, DB_FIRST); |
|||
printf("r=%d count=%ld\n", r, count); |
|||
assert(count==1); |
|||
assert(l[0].gid[0]==42); |
|||
for (int i=1; i<DB_GID_SIZE; i++) { |
|||
assert(l[0].gid[i]==0); |
|||
} |
|||
CHK(l->txn->commit(l->txn, 0)); |
|||
} |
|||
CHK(env ->close(env, 0)); |
|||
} |
|||
|
|||
int test_main (int argc, char *const argv[]) { |
|||
default_parse_args(argc, argv); |
|||
// first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment). |
|||
test(); |
|||
|
|||
|
|||
// second test: poen environment, a db, a txn, prepare, close the environment. Then reopen and do txn_prepare. |
|||
|
|||
// third test: make sure there is an fsync on txn_prepare, but not on the following commit. |
|||
|
|||
|
|||
// Then close the environment Find out what BDB does when ask for the txn prepares. |
|||
// Other tests: read prepared txns, 1 at a time. Then close it and read them again. |
|||
return 0; |
|||
} |
|||
@ -0,0 +1,298 @@ |
|||
#include "test.h" |
|||
#include <sys/wait.h> |
|||
|
|||
// Verify that if we prepare a transaction, then commit a bunch more transactions so that the logs may have been rotated, then the transaction can commit or abort properly on recovery. |
|||
|
|||
static void clean_env (const char *envdir) { |
|||
const int len = strlen(envdir)+100; |
|||
char cmd[len]; |
|||
snprintf(cmd, len, "rm -rf %s", envdir); |
|||
system(cmd); |
|||
CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
static void setup_env (DB_ENV **envp, const char *envdir) { |
|||
CHK(db_env_create(envp, 0)); |
|||
(*envp)->set_errfile(*envp, stderr); |
|||
#ifdef TOKUDB |
|||
CHK((*envp)->set_redzone(*envp, 0)); |
|||
#endif |
|||
CHK((*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
} |
|||
|
|||
#define NTXNS 6 |
|||
|
|||
static void setup_env_and_prepare (DB_ENV **envp, const char *envdir) { |
|||
DB *db; |
|||
clean_env(envdir); |
|||
setup_env(envp, envdir); |
|||
CKERR(db_create(&db, *envp, 0)); |
|||
CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
|
|||
{ |
|||
DB_TXN *txn; |
|||
CKERR((*envp)->txn_begin(*envp, 0, &txn, 0)); |
|||
for (int tnum=0; tnum<NTXNS; tnum++) { |
|||
for (int k=0; k<26; k++) { |
|||
#define DSIZE 200 |
|||
char data[DSIZE]; |
|||
memset(data, ' ', DSIZE); |
|||
data[0]='a'+tnum; |
|||
data[1]='a'+k; |
|||
data[DSIZE-1]=0; |
|||
DBT key={.size=DSIZE, .data=data}; |
|||
CKERR(db->put(db, txn, &key, &key, 0)); |
|||
} |
|||
} |
|||
CKERR(txn->commit(txn, 0)); |
|||
} |
|||
|
|||
for (int tnum=0; tnum<NTXNS; tnum++) { |
|||
DB_TXN *txn; |
|||
CKERR((*envp)->txn_begin(*envp, 0, &txn, 0)); |
|||
char data[3]={'a'+tnum,'_',0}; |
|||
DBT key={.size=3, .data=data}; |
|||
CKERR(db->put(db, txn, &key, &key, 0)); |
|||
u_int8_t gid[DB_GID_SIZE]; |
|||
memset(gid, 0, DB_GID_SIZE); |
|||
gid[0]='a'+tnum; |
|||
CKERR(txn->prepare(txn, gid)); |
|||
// Drop txn on the ground, since we will commit or abort it after recovery |
|||
if (tnum==0) { |
|||
//printf("commit %d\n", tnum); |
|||
CKERR(txn->commit(txn, 0)); |
|||
} else if (tnum==1) { |
|||
//printf("abort %d\n", tnum); |
|||
CKERR(txn->abort(txn)); |
|||
} else { |
|||
//printf("prepare %d\n", tnum); |
|||
} |
|||
} |
|||
CHK(db->close(db, 0)); |
|||
} |
|||
|
|||
enum prepared_state { |
|||
COMMITTED, |
|||
ABORTED, |
|||
MAYBE_COMMITTED, |
|||
MAYBE_ABORTED, |
|||
PREPARED}; |
|||
|
|||
|
|||
static void check_prepared_list (enum prepared_state ps[NTXNS], long count, DB_PREPLIST *l) { |
|||
int count_prepared=0; |
|||
int count_maybe_prepared=0; |
|||
for (int j=0; j<NTXNS; j++) { |
|||
switch (ps[j]) { |
|||
case COMMITTED: |
|||
case ABORTED: |
|||
goto next; |
|||
case PREPARED: |
|||
count_prepared++; |
|||
case MAYBE_COMMITTED: |
|||
case MAYBE_ABORTED: |
|||
count_maybe_prepared++; |
|||
goto next; |
|||
} |
|||
assert(0); |
|||
next:; |
|||
} |
|||
|
|||
assert(count>=count_prepared && count<=count_maybe_prepared); |
|||
|
|||
bool found[NTXNS]; |
|||
for (int j=0; j<NTXNS; j++) { |
|||
found[j] = (ps[j]!=PREPARED); |
|||
} |
|||
|
|||
// now found[j] is false on those transactions that I hope to find in the prepared list. |
|||
for (int j=0; j<count; j++) { |
|||
int num = l[j].gid[0]-'a'; |
|||
assert(num>=0 && num<NTXNS); |
|||
switch (ps[num]) { |
|||
case PREPARED: |
|||
assert(!found[num]); |
|||
found[num]=true; |
|||
break; |
|||
default:; |
|||
} |
|||
for (int i=1; i<DB_GID_SIZE; i++) { |
|||
assert(l[j].gid[i]==0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void get_prepared (DB_ENV *env, long *count, DB_PREPLIST *l) { |
|||
CKERR(env->txn_recover(env, l, NTXNS, count, DB_FIRST)); |
|||
//printf("%s:%d count=%ld\n", __FILE__, __LINE__, *count); |
|||
assert(*count>=0); |
|||
} |
|||
|
|||
static void check_prepared_txns (DB_ENV *env, enum prepared_state ps[NTXNS]) { |
|||
DB_PREPLIST l[NTXNS]; |
|||
long count=-1; |
|||
get_prepared(env, &count, l); |
|||
check_prepared_list(ps, count, l); |
|||
} |
|||
|
|||
static void check_state_after_full_recovery (DB_ENV *env) { |
|||
DB *db; |
|||
CKERR(db_create(&db, env, 0)); |
|||
CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO)); |
|||
|
|||
for (int tnum=0; tnum<NTXNS; tnum++) { |
|||
DB_TXN *txn; |
|||
CKERR(env->txn_begin(env, 0, &txn, 0)); |
|||
char data[3]={'a'+tnum,'_',0}; |
|||
DBT key = {.size=3, .data=data}; |
|||
DBT dbt_data = {.size=0, .data=0}; |
|||
int r = db->get(db, txn, &key, &dbt_data, 0); |
|||
if (tnum%2==0) { |
|||
assert(r==0); |
|||
assert(dbt_data.size==3 && memcmp(dbt_data.data, data, 3)==0); |
|||
} else { |
|||
assert(r==DB_NOTFOUND); |
|||
} |
|||
CKERR(txn->commit(txn, 0)); |
|||
} |
|||
CKERR(db->close(db, 0)); |
|||
} |
|||
|
|||
static void waitfor (pid_t pid) { |
|||
int status; |
|||
pid_t pid2 = wait(&status); |
|||
assert(pid2==pid); |
|||
assert(WIFEXITED(status) && WEXITSTATUS(status)==0); |
|||
} |
|||
|
|||
static void abort_number(int num, int count, DB_PREPLIST *l) { |
|||
for (int j=0; j<count; j++) { |
|||
if (l[j].gid[0]=='a'+num) { |
|||
CKERR(l[j].txn->abort(l[j].txn)); |
|||
return; |
|||
} |
|||
} |
|||
assert(0); |
|||
} |
|||
static void commit_number(int num, int count, DB_PREPLIST *l) { |
|||
for (int j=0; j<count; j++) { |
|||
if (l[j].gid[0]=='a'+num) { |
|||
CKERR(l[j].txn->commit(l[j].txn, 0)); |
|||
return; |
|||
} |
|||
} |
|||
assert(0); |
|||
} |
|||
|
|||
static void test (void) { |
|||
pid_t pid; |
|||
|
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env_and_prepare(&env, ENVDIR); |
|||
enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED}; |
|||
check_prepared_txns(env, prepared); |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
// Now run recovery and crash on purpose. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED}; |
|||
check_prepared_txns(env, prepared); |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
|
|||
// Now see if recovery works the second time. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED}; |
|||
check_prepared_txns(env, prepared); |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
|
|||
// Now see if recovery works the third time. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED}; |
|||
DB_PREPLIST l[NTXNS]; |
|||
long count=-1; |
|||
get_prepared(env, &count, l); |
|||
check_prepared_list(prepared, count, l); |
|||
abort_number(3, count, l); |
|||
commit_number(2, count, l); // do the commit second so it will make it to disk. |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
// Now see if recovery works a third time, with number 2 and 3 no longer in the prepared state. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,MAYBE_COMMITTED,MAYBE_ABORTED,PREPARED,PREPARED}; |
|||
DB_PREPLIST l[NTXNS]; |
|||
long count=-1; |
|||
//printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); // it's a little bit funky that the committed transactions in BDB (from commit_number(2,...) above) don't stay committed. But whatever... |
|||
get_prepared(env, &count, l); |
|||
check_prepared_list(prepared, count, l); |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
// Now see if recovery works a fourth time, with number 2 and 3 no longer in the prepared state. |
|||
// This time we'll do get_prepared with a short count. |
|||
if (0==(pid=fork())) { |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
//printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); // it's a little bit funky that the committed transactions in BDB (from commit_number(2,...) above) don't stay committed. But whatever... |
|||
|
|||
long actual_count=0; |
|||
|
|||
for (int recover_num=0; 1; recover_num++) { |
|||
long count=-1; |
|||
DB_PREPLIST *MALLOC_N(1, l); // use malloc so that valgrind might notice a problem |
|||
CKERR(env->txn_recover(env, l, 1, &count, recover_num==0 ? DB_FIRST : DB_NEXT)); |
|||
//printf("recover_num %d count=%ld\n", recover_num,count); |
|||
if (count==0) break; |
|||
actual_count++; |
|||
if ((l[0].gid[0]-'a')%2==0) { |
|||
CKERR(l[0].txn->commit(l[0].txn, 0)); |
|||
} else { |
|||
CKERR(l[0].txn->abort(l[0].txn)); |
|||
} |
|||
toku_free(l); |
|||
} |
|||
//printf("actual_count=%ld\n", actual_count); |
|||
|
|||
// Now let's see what the state is. |
|||
check_state_after_full_recovery(env); |
|||
|
|||
CKERR(env->close(env, 0)); |
|||
exit(0); |
|||
} |
|||
waitfor(pid); |
|||
// Now we should end up with nothing in the recovery list. |
|||
{ |
|||
DB_ENV *env; |
|||
setup_env(&env, ENVDIR); |
|||
long count=-1; |
|||
DB_PREPLIST l[1]; |
|||
CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST)); |
|||
assert(count==0); |
|||
check_state_after_full_recovery(env); |
|||
CKERR(env->close(env, 0)); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
int test_main (int argc, char *const argv[]) { |
|||
default_parse_args(argc, argv); |
|||
// first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment). |
|||
test(); |
|||
|
|||
return 0; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/* -*- mode: C; c-basic-offset: 4 -*- */ |
|||
#ident "$Id$" |
|||
#ident "Copyright (c) 2007-2012 Tokutek Inc. All rights reserved." |
|||
#include "test.h" |
|||
#include <stdio.h> |
|||
|
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#include <memory.h> |
|||
#include <sys/stat.h> |
|||
#include <db.h> |
|||
|
|||
// Recreate a mysqld crash by closing and opening a db within a transaction. |
|||
// The crash occurs when writing a dirty cachetable pair, so we insert one |
|||
// row. |
|||
static void |
|||
test_txn_close_before_commit (void) { |
|||
|
|||
CHK(system("rm -rf " ENVDIR)); |
|||
toku_os_mkdir(ENVDIR, S_IRWXU+S_IRWXG+S_IRWXO); |
|||
|
|||
int r; |
|||
DB_ENV *env; |
|||
r = db_env_create(&env, 0); assert(r == 0); |
|||
env->set_errfile(env, stdout); |
|||
r = env->open(env, ENVDIR, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); |
|||
if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r)); |
|||
assert(r == 0); |
|||
|
|||
DB *db; |
|||
r = db_create(&db, env, 0); assert(r == 0); |
|||
r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0); |
|||
|
|||
DB_TXN *txn = 0; |
|||
r = env->txn_begin(env, 0, &txn, 0); assert(r == 0); |
|||
|
|||
DBT key, val; |
|||
int k = 1, v = 1; |
|||
r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0); |
|||
assert(r == 0); |
|||
|
|||
// Close before commit |
|||
r = db->close(db, 0); assert(r == 0); |
|||
|
|||
r = txn->commit(txn, 0); assert(r == 0); |
|||
|
|||
r = env->close(env, 0); assert(r == 0); |
|||
} |
|||
|
|||
int |
|||
test_main(int UU(argc), char UU(*const argv[])) { |
|||
test_txn_close_before_commit(); |
|||
return 0; |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/* -*- mode: C; c-basic-offset: 4 -*- */ |
|||
#ident "$Id$" |
|||
#ident "Copyright (c) 2007-2012 Tokutek Inc. All rights reserved." |
|||
#include "test.h" |
|||
#include <stdio.h> |
|||
|
|||
#include <stdlib.h> |
|||
#include <unistd.h> |
|||
#include <memory.h> |
|||
#include <sys/stat.h> |
|||
#include <db.h> |
|||
|
|||
// Recreate a mysqld crash by closing and opening a db within a transaction. |
|||
// The crash occurs when writing a dirty cachetable pair, so we insert one |
|||
// row. |
|||
static void |
|||
test_txn_close_before_prepare_commit (void) { |
|||
|
|||
CHK(system("rm -rf " ENVDIR)); |
|||
toku_os_mkdir(ENVDIR, S_IRWXU+S_IRWXG+S_IRWXO); |
|||
|
|||
int r; |
|||
DB_ENV *env; |
|||
r = db_env_create(&env, 0); assert(r == 0); |
|||
env->set_errfile(env, stdout); |
|||
r = env->open(env, ENVDIR, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); |
|||
if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r)); |
|||
assert(r == 0); |
|||
|
|||
DB *db; |
|||
r = db_create(&db, env, 0); assert(r == 0); |
|||
r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0); |
|||
|
|||
DB_TXN *txn = 0; |
|||
r = env->txn_begin(env, 0, &txn, 0); assert(r == 0); |
|||
|
|||
DBT key, val; |
|||
int k = 1, v = 1; |
|||
r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0); |
|||
assert(r == 0); |
|||
|
|||
// Close before commit |
|||
r = db->close(db, 0); assert(r == 0); |
|||
|
|||
u_int8_t gid[DB_GID_SIZE]; |
|||
memset(gid, 1, DB_GID_SIZE); |
|||
r = txn->prepare(txn, gid); assert(r == 0); |
|||
r = txn->commit(txn, 0); assert(r == 0); |
|||
|
|||
r = env->close(env, 0); assert(r == 0); |
|||
} |
|||
|
|||
int |
|||
test_main(int UU(argc), char UU(*const argv[])) { |
|||
test_txn_close_before_prepare_commit(); |
|||
return 0; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue