001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: INFileReaderTest.java,v 1.72.2.5 2008/01/07 15:14:29 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.io.File;
012: import java.io.IOException;
013: import java.util.ArrayList;
014: import java.util.Arrays;
015: import java.util.HashMap;
016: import java.util.List;
017: import java.util.Map;
018:
019: import junit.framework.TestCase;
020:
021: import com.sleepycat.je.Database;
022: import com.sleepycat.je.DatabaseConfig;
023: import com.sleepycat.je.DatabaseException;
024: import com.sleepycat.je.DbInternal;
025: import com.sleepycat.je.Environment;
026: import com.sleepycat.je.EnvironmentConfig;
027: import com.sleepycat.je.config.EnvironmentParams;
028: import com.sleepycat.je.dbi.DbConfigManager;
029: import com.sleepycat.je.dbi.EnvironmentImpl;
030: import com.sleepycat.je.log.entry.SingleItemEntry;
031: import com.sleepycat.je.tree.BIN;
032: import com.sleepycat.je.tree.ChildReference;
033: import com.sleepycat.je.tree.IN;
034: import com.sleepycat.je.tree.INDeleteInfo;
035: import com.sleepycat.je.tree.Key;
036: import com.sleepycat.je.tree.Key.DumpType;
037: import com.sleepycat.je.tree.LN;
038: import com.sleepycat.je.util.TestUtils;
039: import com.sleepycat.je.utilint.DbLsn;
040: import com.sleepycat.je.utilint.Tracer;
041:
042: /**
043: *
044: */
045: public class INFileReaderTest extends TestCase {
046:
047: static private final boolean DEBUG = false;
048:
049: private File envHome;
050: private Environment env;
051: /*
052: * Need a handle onto the true environment in order to create
053: * a reader
054: */
055: private EnvironmentImpl envImpl;
056: private Database db;
057: private long maxNodeId;
058: private List checkList;
059:
060: public INFileReaderTest() {
061: super ();
062: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
063: Key.DUMP_TYPE = DumpType.BINARY;
064: }
065:
066: public void setUp() throws IOException, DatabaseException {
067:
068: /*
069: * Note that we use the official Environment class to make the
070: * environment, so that everything is set up, but we then go a
071: * backdoor route to get to the underlying EnvironmentImpl class
072: * so that we don't require that the Environment.getDbEnvironment
073: * method be unnecessarily public.
074: */
075: TestUtils.removeLogFiles("Setup", envHome, false);
076:
077: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
078: envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
079: "6");
080: envConfig.setConfigParam(EnvironmentParams.BIN_DELTA_PERCENT
081: .getName(), "75");
082: envConfig.setAllowCreate(true);
083:
084: /* Disable noisy UtilizationProfile database creation. */
085: DbInternal.setCreateUP(envConfig, false);
086: /* Don't checkpoint utilization info for this test. */
087: DbInternal.setCheckpointUP(envConfig, false);
088: /* Don't run the cleaner without a UtilizationProfile. */
089: envConfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER
090: .getName(), "false");
091:
092: env = new Environment(envHome, envConfig);
093:
094: envImpl = DbInternal.envGetEnvironmentImpl(env);
095:
096: }
097:
098: public void tearDown() throws IOException, DatabaseException {
099:
100: envImpl = null;
101: env.close();
102: TestUtils.removeFiles("TearDown", envHome,
103: FileManager.JE_SUFFIX);
104: }
105:
106: /**
107: * Test no log file
108: */
109: public void testNoFile() throws IOException, DatabaseException {
110:
111: /* Make a log file with a valid header, but no data. */
112: INFileReader reader = new INFileReader(envImpl, 1000,
113: DbLsn.NULL_LSN, DbLsn.NULL_LSN, false, false,
114: DbLsn.NULL_LSN, null);
115: reader.addTargetType(LogEntryType.LOG_IN);
116: reader.addTargetType(LogEntryType.LOG_BIN);
117: reader.addTargetType(LogEntryType.LOG_IN_DELETE_INFO);
118:
119: int count = 0;
120: while (reader.readNextEntry()) {
121: count += 1;
122: }
123: assertEquals("Empty file should not have entries", 0, count);
124: }
125:
126: /**
127: * Run with an empty file
128: */
129: public void testEmpty() throws IOException, DatabaseException {
130:
131: /* Make a log file with a valid header, but no data. */
132: FileManager fileManager = envImpl.getFileManager();
133: fileManager.bumpLsn(1000000);
134: FileManagerTestUtils.createLogFile(fileManager, envImpl, 10000);
135: fileManager.clear();
136:
137: INFileReader reader = new INFileReader(envImpl, 1000,
138: DbLsn.NULL_LSN, DbLsn.NULL_LSN, false, false,
139: DbLsn.NULL_LSN, null);
140: reader.addTargetType(LogEntryType.LOG_IN);
141: reader.addTargetType(LogEntryType.LOG_BIN);
142: reader.addTargetType(LogEntryType.LOG_IN_DELETE_INFO);
143:
144: int count = 0;
145: while (reader.readNextEntry()) {
146: count += 1;
147: }
148: assertEquals("Empty file should not have entries", 0, count);
149: }
150:
151: /**
152: * Run with defaults, read whole log
153: */
154: public void testBasic() throws IOException, DatabaseException {
155:
156: DbConfigManager cm = envImpl.getConfigManager();
157: doTest(50, cm.getInt(EnvironmentParams.LOG_ITERATOR_READ_SIZE),
158: 0, false);
159: }
160:
161: /**
162: * Run with very small buffers and track node ids
163: */
164: public void testTracking() throws IOException, DatabaseException {
165:
166: doTest(50, // num iterations
167: 10, // tiny buffer
168: 0, // start lsn index
169: true); // track node ids
170: }
171:
172: /**
173: * Start in the middle of the file
174: */
175: public void testMiddleStart() throws IOException, DatabaseException {
176:
177: doTest(50, 100, 40, true);
178: }
179:
180: private void doTest(int numIters, int bufferSize,
181: int startLsnIndex, boolean trackNodeIds)
182: throws IOException, DatabaseException {
183:
184: /* Fill up a fake log file. */
185: createLogFile(numIters);
186:
187: /* Decide where to start. */
188: long startLsn = DbLsn.NULL_LSN;
189: int checkIndex = 0;
190: if (startLsnIndex >= 0) {
191: startLsn = ((CheckInfo) checkList.get(startLsnIndex)).lsn;
192: checkIndex = startLsnIndex;
193: }
194:
195: /* Use an empty utilization map for testing tracking. */
196: Map fileSummaryLsns = trackNodeIds ? (new HashMap()) : null;
197:
198: INFileReader reader = new INFileReader(envImpl, bufferSize,
199: startLsn, DbLsn.NULL_LSN, trackNodeIds, false,
200: DbLsn.NULL_LSN, fileSummaryLsns);
201: reader.addTargetType(LogEntryType.LOG_IN);
202: reader.addTargetType(LogEntryType.LOG_BIN);
203: reader.addTargetType(LogEntryType.LOG_BIN_DELTA);
204: reader.addTargetType(LogEntryType.LOG_IN_DELETE_INFO);
205:
206: /* Read. */
207: checkLogFile(reader, checkIndex, trackNodeIds);
208: }
209:
210: /**
211: * Write a logfile of entries, then read the end
212: */
213: private void createLogFile(int numIters) throws IOException,
214: DatabaseException {
215:
216: /*
217: * Create a log file full of INs, INDeleteInfo, BINDeltas and
218: * Debug Records
219: */
220: DatabaseConfig dbConfig = new DatabaseConfig();
221: dbConfig.setAllowCreate(true);
222: db = env.openDatabase(null, "foo", dbConfig);
223: LogManager logManager = envImpl.getLogManager();
224: maxNodeId = 0;
225:
226: checkList = new ArrayList();
227:
228: for (int i = 0; i < numIters; i++) {
229: /* Add a debug record. */
230: Tracer rec = new Tracer("Hello there, rec " + (i + 1));
231: rec.log(logManager);
232:
233: /* Create, log, and save an IN. */
234: byte[] data = new byte[i + 1];
235: Arrays.fill(data, (byte) (i + 1));
236:
237: byte[] key = new byte[i + 1];
238: Arrays.fill(key, (byte) (i + 1));
239:
240: IN in = new IN(DbInternal.dbGetDatabaseImpl(db), key, 5, 10);
241: in.latch(false);
242: long lsn = in.log(logManager);
243: in.releaseLatch();
244: checkList.add(new CheckInfo(lsn, in));
245:
246: if (DEBUG) {
247: System.out.println("LSN " + i + " = " + lsn);
248: System.out.println("IN " + i + " = " + in.getNodeId());
249: }
250:
251: /* Add other types of INs. */
252: BIN bin = new BIN(DbInternal.dbGetDatabaseImpl(db), key, 2,
253: 1);
254: bin.latch(false);
255: lsn = bin.log(logManager);
256: checkList.add(new CheckInfo(lsn, bin));
257:
258: /* Add provisional entries, which should get ignored. */
259: lsn = bin.log(logManager, false, // allowDeltas,
260: true, // isProvisional,
261: false, // proactiveMigration,
262: false, // backgroundIO
263: in);
264:
265: bin.releaseLatch();
266:
267: /* Add a LN, to stress the node tracking. */
268: LN ln = new LN(data);
269: lsn = ln.log(envImpl, DbInternal.dbGetDatabaseImpl(db)
270: .getId(), key, null, DbLsn.NULL_LSN, 0, null,
271: false, false);
272:
273: /*
274: * Add an IN delete entry, it should get picked up by the reader.
275: */
276: INDeleteInfo info = new INDeleteInfo(i, key, DbInternal
277: .dbGetDatabaseImpl(db).getId());
278: lsn = logManager.log(new SingleItemEntry(
279: LogEntryType.LOG_IN_DELETE_INFO, info));
280: checkList.add(new CheckInfo(lsn, info));
281:
282: /*
283: * Add an BINDelta. Generate it by making the first, full version
284: * provisional so the test doesn't pick it up, and then log a
285: * delta.
286: */
287: BIN binDeltaBin = new BIN(DbInternal.dbGetDatabaseImpl(db),
288: key, 10, 1);
289: maxNodeId = binDeltaBin.getNodeId();
290: binDeltaBin.latch();
291: ChildReference newEntry = new ChildReference(null, key,
292: DbLsn.makeLsn(0, 0));
293: assertTrue(binDeltaBin.insertEntry(newEntry));
294:
295: lsn = binDeltaBin.log(logManager, false, // allowDeltas,
296: true, // isProvisional,
297: false, // proactiveMigration,
298: false, // backgroundIO
299: in); // parent
300:
301: /* Modify the bin with one entry so there can be a delta. */
302:
303: byte[] keyBuf2 = new byte[2];
304: Arrays.fill(keyBuf2, (byte) (i + 2));
305: ChildReference newEntry2 = new ChildReference(null,
306: keyBuf2, DbLsn.makeLsn(100, 101));
307: assertTrue(binDeltaBin.insertEntry(newEntry2));
308:
309: assertTrue(binDeltaBin.log(logManager, true, // allowDeltas
310: false, // isProvisional
311: false, // proactiveMigration,
312: false, // backgroundIO
313: in) == DbLsn.NULL_LSN);
314: lsn = binDeltaBin.getLastDeltaVersion();
315: if (DEBUG) {
316: System.out.println("delta =" + binDeltaBin.getNodeId()
317: + " at LSN " + lsn);
318: }
319: checkList.add(new CheckInfo(lsn, binDeltaBin));
320:
321: /*
322: * Reset the generation to 0 so this version of the BIN, which gets
323: * saved for unit test comparison, will compare to the version read
324: * from the log, which is initialized to 0.
325: */
326: binDeltaBin.setGeneration(0);
327: binDeltaBin.releaseLatch();
328: }
329:
330: /* Flush the log, files. */
331: logManager.flush();
332: envImpl.getFileManager().clear();
333: }
334:
335: private void checkLogFile(INFileReader reader, int checkIndex,
336: boolean checkMaxNodeId) throws IOException,
337: DatabaseException {
338:
339: try {
340: /* Read all the INs. */
341: int i = checkIndex;
342:
343: while (reader.readNextEntry()) {
344: if (DEBUG) {
345: System.out.println("i = " + i
346: + " reader.isDeleteInfo="
347: + reader.isDeleteInfo() + " LSN = "
348: + reader.getLastLsn());
349: }
350:
351: CheckInfo check = (CheckInfo) checkList.get(i);
352:
353: if (reader.isDeleteInfo()) {
354: assertEquals(check.info.getDeletedNodeId(), reader
355: .getDeletedNodeId());
356: assertTrue(Arrays.equals(check.info
357: .getDeletedIdKey(), reader
358: .getDeletedIdKey()));
359: assertTrue(check.info.getDatabaseId().equals(
360: reader.getDatabaseId()));
361:
362: } else {
363:
364: /*
365: * When comparing the check data against the data from the
366: * log, make the dirty bits match so that they compare
367: * equal.
368: */
369: IN inFromLog = reader.getIN();
370: inFromLog.latch(false);
371: inFromLog.setDirty(true);
372: inFromLog.releaseLatch();
373: IN testIN = check.in;
374: testIN.latch(false);
375: testIN.setDirty(true);
376: testIN.releaseLatch();
377:
378: /*
379: * Only check the INs we created in the test. (The others
380: * are from the map db.
381: */
382: if (reader.getDatabaseId().equals(
383: DbInternal.dbGetDatabaseImpl(db).getId())) {
384: // The IN should match
385: String inFromLogString = inFromLog.toString();
386: String testINString = testIN.toString();
387: if (DEBUG) {
388: System.out
389: .println("testIN=" + testINString);
390: System.out.println("inFromLog="
391: + inFromLogString);
392: }
393:
394: assertEquals("IN " + inFromLog.getNodeId()
395: + " at index " + i
396: + " should match.\nTestIN=" + testIN
397: + "\nLogIN=" + inFromLog, testINString,
398: inFromLogString);
399: }
400: }
401: /* The LSN should match. */
402: assertEquals("LSN " + i + " should match", check.lsn,
403: reader.getLastLsn());
404:
405: i++;
406: }
407: assertEquals(i, checkList.size());
408: if (checkMaxNodeId) {
409: assertEquals(maxNodeId, reader.getMaxNodeId());
410: }
411: } finally {
412: db.close();
413: }
414: }
415:
416: private class CheckInfo {
417: long lsn;
418: IN in;
419: INDeleteInfo info;
420:
421: CheckInfo(long lsn, IN in) {
422: this .lsn = lsn;
423: this .in = in;
424: this .info = null;
425: }
426:
427: CheckInfo(long lsn, INDeleteInfo info) {
428: this.lsn = lsn;
429: this.in = null;
430: this.info = info;
431: }
432: }
433: }
|