File: SR13034Test.java

package info (click to toggle)
libdb-je-java 3.3.98-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,052 kB
  • sloc: java: 153,077; xml: 2,034; makefile: 3
file content (174 lines) | stat: -rw-r--r-- 5,785 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2005,2008 Oracle.  All rights reserved.
 *
 * $Id: SR13034Test.java,v 1.8 2008/01/07 14:29:13 cwl Exp $
 */

package com.sleepycat.je.tree;

import java.io.File;
import java.io.IOException;

import junit.framework.TestCase;

import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.util.TestUtils;

/**
 * Reproduce a bug where fetchEntry rather than fetchEntryIgnoreKnownDeleted
 * was being called when searching the duplicate tree by LN node ID during
 * recovery.
 *
 * The trick is to create a DBIN with a KnownDeleted flag set on an entry.  And
 * to cause recovery to search that DBIN by node ID during redo of a deleted
 * LN.  This deleted LN log entry must not have any data -- it must have been
 * deleted before creation of the dup tree as in SR 8984.
 *
 * In addition, the deleted LN must appear after the entries with KnownDeleted
 * set in the BIN, otherwise the search by node ID will find the LN before
 * it encounters a KnownDeleted entry.

 * The sequence in the test is as follows.  I'm not positive this was the same
 * sequence as seen by the user, since the user did not send their logs, but
 * I believe the bug fix is general enough to cover similar cases.
 *
 * 1) Insert {A, C} (LN with key A, data C) in T1.
 * 2) Delete {A, C} in T1.  The LN log entry will not have any data.
 * 3) Commit T1 so these log entries will be replayed during recovery redo.
 * 4) Insert {A, A} and {A, B} in T2.
 * 5) Abort T2 so that the KnownDeleted flag will be set on these DBIN entries
 * during recovery.
 * 6) Close without a checkpoint and recover.  When replaying the deleted LN
 * {A, C}, we don't have a dup key because it was deleted before the dup tree
 * was created.  So we search the dup tree by LN node ID.  Calling fetchEntry
 * on {A, A} (or {A, B}) throws an exception because KnownDeleted is set.  We
 * neglected to check KnownDeleted.
 */
public class SR13034Test extends TestCase {

    private File envHome;
    private Environment env;
    private Database db;

    public SR13034Test() {
        envHome = new File(System.getProperty(TestUtils.DEST_DIR));
    }

    public void setUp()
        throws IOException {

        TestUtils.removeLogFiles("Setup", envHome, false);
    }

    public void tearDown()
        throws Exception {

        try {
            if (env != null) {
		env.close();
            }
        } catch (Exception e) {
            System.out.println("During tearDown: " + e);
        }

        env = null;
        db = null;

        TestUtils.removeLogFiles("TearDown", envHome, false);
    }

    private void open()
	throws DatabaseException {

        EnvironmentConfig envConfig = TestUtils.initEnvConfig();
        envConfig.setAllowCreate(true);
        envConfig.setTransactional(true);
        /* Do not run the daemons to avoid timing considerations. */
        envConfig.setConfigParam
            (EnvironmentParams.ENV_RUN_CLEANER.getName(), "false");
        envConfig.setConfigParam
            (EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false");
        envConfig.setConfigParam
	    (EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(), "false");
        envConfig.setConfigParam
            (EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(), "false");
        env = new Environment(envHome, envConfig);

        DatabaseConfig dbConfig = new DatabaseConfig();
        dbConfig.setAllowCreate(true);
        dbConfig.setTransactional(true);
        dbConfig.setSortedDuplicates(true);
        db = env.openDatabase(null, "foo", dbConfig);
    }

    private void close()
	throws DatabaseException {

        db.close();
        db = null;

        env.close();
        env = null;
    }

    public void testSR13034()
	throws DatabaseException {

        open();

        DatabaseEntry key = new DatabaseEntry();
        DatabaseEntry data = new DatabaseEntry();
        OperationStatus status;
        Transaction txn;

        /*
         * Insert {A, C}, then delete it.  No dup tree has been created, so
         * this logs a deleted LN with no data.
         */
        txn = env.beginTransaction(null, null);
        StringBinding.stringToEntry("A", key);
        StringBinding.stringToEntry("C", data);
        status = db.putNoOverwrite(txn, key, data);
        assertEquals(OperationStatus.SUCCESS, status);
        status = db.delete(txn, key);
        assertEquals(OperationStatus.SUCCESS, status);
        txn.commit();

        /*
         * Insert {A, A}, {A, B}, which creates a dup tree.  Then abort to set
         * KnownDeleted on these entries.
         */
        txn = env.beginTransaction(null, null);
        StringBinding.stringToEntry("A", key);
        StringBinding.stringToEntry("A", data);
        status = db.putNoDupData(txn, key, data);
        StringBinding.stringToEntry("A", key);
        StringBinding.stringToEntry("B", data);
        status = db.putNoDupData(txn, key, data);
        assertEquals(OperationStatus.SUCCESS, status);
        txn.abort();

        /*
         * Close without a checkpoint and recover.  Before the bug fix, the
         * recovery would throw DatabaseException "attempt to fetch a deleted
         * entry".
         */
        db.close();
        DbInternal.envGetEnvironmentImpl(env).close(false);
        open();

        close();
    }
}