001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: SR12978Test.java,v 1.5.2.3 2008/01/07 15:14:25 cwl Exp $
007: */
008:
009: package com.sleepycat.je.cleaner;
010:
011: import java.io.File;
012: import java.io.IOException;
013:
014: import junit.framework.TestCase;
015:
016: import com.sleepycat.bind.tuple.IntegerBinding;
017: import com.sleepycat.je.CheckpointConfig;
018: import com.sleepycat.je.Database;
019: import com.sleepycat.je.DatabaseConfig;
020: import com.sleepycat.je.DatabaseEntry;
021: import com.sleepycat.je.DatabaseException;
022: import com.sleepycat.je.DbInternal;
023: import com.sleepycat.je.Environment;
024: import com.sleepycat.je.EnvironmentConfig;
025: import com.sleepycat.je.OperationStatus;
026: import com.sleepycat.je.config.EnvironmentParams;
027: import com.sleepycat.je.log.FileManager;
028: import com.sleepycat.je.util.TestUtils;
029:
030: /**
031: * Tests a fix to 12978, which was a ClassCastException in the following
032: * sequence.
033: *
034: * 1) A LN's BIN entry has the MIGRATE flag set.
035: *
036: * 2) Another LN with the same key is inserted (duplicates are allowed) and the
037: * first LN is moved to a dup tree,
038: *
039: * 3) The MIGRATE flag on the BIN entry is not cleared, and this entry now
040: * contains a DIN.
041: *
042: * 4) A split of the BIN occurs, logging the BIN with DIN entry. During a
043: * split we can't do migration, so we attempt to put the DIN onto the cleaner's
044: * pending list. We cast from DIN to LN, causing the exception.
045: *
046: * The fix was to clear the MIGRATE flag on the BIN entry at the time we update
047: * it to contain the DIN.
048: *
049: * This bug also left latches unreleased if a runtime exception occurred during
050: * a split, and that problem was fixed also.
051: */
052: public class SR12978Test extends TestCase {
053:
054: private static final String DB_NAME = "foo";
055:
056: private static final CheckpointConfig forceConfig = new CheckpointConfig();
057: static {
058: forceConfig.setForce(true);
059: }
060:
061: private File envHome;
062: private Environment env;
063: private Database db;
064:
065: public SR12978Test() {
066: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
067: }
068:
069: public void setUp() throws IOException, DatabaseException {
070:
071: TestUtils.removeLogFiles("Setup", envHome, false);
072: TestUtils.removeFiles("Setup", envHome, FileManager.DEL_SUFFIX);
073: }
074:
075: public void tearDown() throws IOException, DatabaseException {
076:
077: try {
078: if (env != null) {
079: env.close();
080: }
081: } catch (Throwable e) {
082: System.out.println("tearDown: " + e);
083: }
084:
085: try {
086: TestUtils.removeLogFiles("tearDown", envHome, true);
087: TestUtils.removeFiles("tearDown", envHome,
088: FileManager.DEL_SUFFIX);
089: } catch (Throwable e) {
090: System.out.println("tearDown: " + e);
091: }
092:
093: db = null;
094: env = null;
095: envHome = null;
096: }
097:
098: /**
099: * Opens the environment and database.
100: */
101: private void open() throws DatabaseException {
102:
103: EnvironmentConfig config = TestUtils.initEnvConfig();
104: DbInternal.disableParameterValidation(config);
105: config.setAllowCreate(true);
106: /* Do not run the daemons. */
107: config.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER
108: .getName(), "false");
109: config.setConfigParam(EnvironmentParams.ENV_RUN_EVICTOR
110: .getName(), "false");
111: config.setConfigParam(EnvironmentParams.ENV_RUN_CHECKPOINTER
112: .getName(), "false");
113: config.setConfigParam(EnvironmentParams.ENV_RUN_INCOMPRESSOR
114: .getName(), "false");
115: /* Configure to make cleaning more frequent. */
116: config.setConfigParam(EnvironmentParams.LOG_FILE_MAX.getName(),
117: "10240");
118: config.setConfigParam(EnvironmentParams.CLEANER_MIN_UTILIZATION
119: .getName(), "90");
120:
121: env = new Environment(envHome, config);
122:
123: DatabaseConfig dbConfig = new DatabaseConfig();
124: dbConfig.setAllowCreate(true);
125: dbConfig.setSortedDuplicates(true);
126: db = env.openDatabase(null, DB_NAME, dbConfig);
127: }
128:
129: /**
130: * Closes the environment and database.
131: */
132: private void close() throws DatabaseException {
133:
134: if (db != null) {
135: db.close();
136: db = null;
137: }
138: if (env != null) {
139: env.close();
140: env = null;
141: }
142: }
143:
144: /**
145: */
146: public void testSR12978() throws DatabaseException {
147:
148: open();
149:
150: final int COUNT = 500;
151: DatabaseEntry key = new DatabaseEntry();
152: DatabaseEntry data = new DatabaseEntry();
153: OperationStatus status;
154:
155: /*
156: * Insert enough non-dup records to write a few log files. Delete
157: * every other so that cleaning will occur. Leave key space so we can
158: * insert below to cause splits.
159: */
160: IntegerBinding.intToEntry(0, data);
161: for (int i = 0; i < COUNT; i += 4) {
162:
163: IntegerBinding.intToEntry(i + 0, key);
164: status = db.putNoOverwrite(null, key, data);
165: assertEquals(OperationStatus.SUCCESS, status);
166:
167: IntegerBinding.intToEntry(i + 1, key);
168: status = db.putNoOverwrite(null, key, data);
169: assertEquals(OperationStatus.SUCCESS, status);
170:
171: status = db.delete(null, key);
172: assertEquals(OperationStatus.SUCCESS, status);
173: }
174:
175: /* Clean to set the MIGRATE flag on some LN entries. */
176: env.checkpoint(forceConfig);
177: int nCleaned = env.cleanLog();
178: assertTrue(nCleaned > 0);
179:
180: /* Add dups to cause the LNs to be moved to a dup tree. */
181: IntegerBinding.intToEntry(1, data);
182: for (int i = 0; i < COUNT; i += 4) {
183:
184: IntegerBinding.intToEntry(i + 0, key);
185: status = db.putNoDupData(null, key, data);
186: assertEquals(OperationStatus.SUCCESS, status);
187: }
188:
189: /*
190: * Insert more unique keys to cause BIN splits. Before the fix to
191: * 12978, a CastCastException would occur during a split.
192: */
193: IntegerBinding.intToEntry(0, data);
194: for (int i = 0; i < COUNT; i += 4) {
195:
196: IntegerBinding.intToEntry(i + 2, key);
197: status = db.putNoOverwrite(null, key, data);
198: assertEquals(OperationStatus.SUCCESS, status);
199:
200: IntegerBinding.intToEntry(i + 3, key);
201: status = db.putNoOverwrite(null, key, data);
202: assertEquals(OperationStatus.SUCCESS, status);
203: }
204:
205: close();
206: }
207: }
|