001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: FileManagerTest.java,v 1.65.2.3 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.io.RandomAccessFile;
014: import java.util.Set;
015:
016: import junit.framework.TestCase;
017:
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.config.EnvironmentParams;
026: import com.sleepycat.je.dbi.EnvironmentImpl;
027: import com.sleepycat.je.util.TestUtils;
028: import com.sleepycat.je.utilint.DbLsn;
029:
030: /**
031: * File Manager
032: */
033: public class FileManagerTest extends TestCase {
034:
035: static private int FILE_SIZE = 120;
036:
037: private EnvironmentImpl envImpl;
038: private FileManager fileManager;
039: private File envHome;
040:
041: public FileManagerTest() {
042: super ();
043: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
044: }
045:
046: protected void setUp() throws DatabaseException, IOException {
047:
048: /* Remove files to start with a clean slate. */
049: TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
050:
051: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
052: DbInternal.disableParameterValidation(envConfig);
053: envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX
054: .getName(), new Integer(FILE_SIZE).toString());
055: /* Yank the cache size way down. */
056: envConfig.setConfigParam(EnvironmentParams.LOG_FILE_CACHE_SIZE
057: .getName(), "3");
058: envConfig.setAllowCreate(true);
059: envImpl = new EnvironmentImpl(envHome, envConfig);
060:
061: /* Make a standalone file manager for this test. */
062: envImpl.close();
063: envImpl.open(); /* Just sets state to OPEN. */
064: fileManager = new FileManager(envImpl, envHome, false);
065:
066: /*
067: * Remove any files after the environment is created again! We want to
068: * remove the files made by recovery, so we can test the file manager
069: * in controlled cases.
070: */
071: TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
072: }
073:
074: protected void tearDown() throws IOException, DatabaseException {
075:
076: if (fileManager != null) {
077: fileManager.clear();
078: fileManager.close();
079: }
080: TestUtils.removeFiles("TearDown", envHome,
081: FileManager.JE_SUFFIX);
082: }
083:
084: /**
085: * Test LSN administration.
086: */
087: public void testLsnBumping() throws Exception {
088:
089: /*
090: We are adding these entries:
091: +----+------+---------+--------+
092: file 0: |hdr | 30 | 50 |empty |
093: +----+------+---------+--------+
094: 0 hdr hdr+30 hdr+80 99
095:
096: +----+--------+-------+-------+-----+-------+
097: file 1: |hdr | 40 | 20 | 10 | 5 | empty |
098: +----+--------+-------+-------+-----+-------+
099: 0 hdr hdr+40 hdr+60 hdr+70 hdr+75
100:
101: +-----+-----+--------+
102: file 2: | hdr | 75 | empty |
103: +-----+-----+--------+
104: 0 hdr hdr+75
105:
106: +-----+-------------------------------+
107: file 3: | hdr | 125 |
108: +-----+-------------------------------+
109: 0 hdr
110:
111: +-----+-----+------+-----+--------------+
112: file 4: | hdr | 10 | 20 | 30 | empty
113: +-----+-----+------+-----+--------------+
114: 0 hdr hdr+10 hdr+30
115:
116: */
117:
118: try {
119: /* Should start out at LSN 0. */
120:
121: /* "add" some entries to the log. */
122: long hdrSize = FileManager.firstLogEntryOffset();
123:
124: fileManager.bumpLsn(30L);
125: /* Item placed here. */
126: assertEquals(DbLsn.makeLsn(0, hdrSize), fileManager
127: .getLastUsedLsn());
128: /* prev entry. */
129: assertEquals(0, fileManager.getPrevEntryOffset());
130:
131: fileManager.bumpLsn(50L);
132: /* Item placed here. */
133: assertEquals(DbLsn.makeLsn(0, (hdrSize + 30)), fileManager
134: .getLastUsedLsn());
135: assertEquals(hdrSize, fileManager.getPrevEntryOffset());
136:
137: /* bump over to a file 1. */
138: fileManager.bumpLsn(40L);
139: /* item placed here. */
140: assertEquals(DbLsn.makeLsn(1, hdrSize), fileManager
141: .getLastUsedLsn());
142: assertEquals(0, fileManager.getPrevEntryOffset());
143:
144: fileManager.bumpLsn(20L);
145: /* Item placed here. */
146: assertEquals(DbLsn.makeLsn(1, (hdrSize + 40)), fileManager
147: .getLastUsedLsn());
148: assertEquals(hdrSize, fileManager.getPrevEntryOffset());
149:
150: fileManager.bumpLsn(10L);
151: /* Item placed here. */
152: assertEquals(DbLsn.makeLsn(1, (hdrSize + 60)), fileManager
153: .getLastUsedLsn());
154: assertEquals(hdrSize + 40, fileManager.getPrevEntryOffset());
155:
156: fileManager.bumpLsn(5L);
157: /* item placed here. */
158: assertEquals(DbLsn.makeLsn(1, (hdrSize + 70)), fileManager
159: .getLastUsedLsn());
160: assertEquals(hdrSize + 60, fileManager.getPrevEntryOffset());
161:
162: /* bump over to file 2. */
163: fileManager.bumpLsn(75L);
164: /* Item placed here. */
165: assertEquals(DbLsn.makeLsn(2, hdrSize), fileManager
166: .getLastUsedLsn());
167: assertEquals(0, fileManager.getPrevEntryOffset());
168:
169: /* Ask for something bigger than a file: bump over to file 3. */
170: fileManager.bumpLsn(125L);
171: /* item placed here. */
172: assertEquals(DbLsn.makeLsn(3, hdrSize), fileManager
173: .getLastUsedLsn());
174: assertEquals(0, fileManager.getPrevEntryOffset());
175:
176: /* bump over to file 4. */
177: fileManager.bumpLsn(10L);
178: /* Item placed here. */
179: assertEquals(DbLsn.makeLsn(4, hdrSize), fileManager
180: .getLastUsedLsn());
181: assertEquals(0, fileManager.getPrevEntryOffset());
182:
183: fileManager.bumpLsn(20L);
184: /* Item placed here. */
185: assertEquals(DbLsn.makeLsn(4, (hdrSize + 10)), fileManager
186: .getLastUsedLsn());
187: assertEquals(hdrSize, fileManager.getPrevEntryOffset());
188:
189: fileManager.bumpLsn(30L);
190: /* Item placed here. */
191: assertEquals(DbLsn.makeLsn(4, (hdrSize + 30)), fileManager
192: .getLastUsedLsn());
193: assertEquals((hdrSize + 10), fileManager
194: .getPrevEntryOffset());
195:
196: } catch (Exception e) {
197: e.printStackTrace();
198: throw e;
199: }
200: }
201:
202: /**
203: * Test initializing the last position in the logs.
204: */
205: public void testSetLastPosition() throws DatabaseException {
206:
207: /*
208: * Pretend that the last file is file 79.
209: */
210: fileManager.setLastPosition(// next available LSN
211: DbLsn.makeLsn(79L, 88L), DbLsn.makeLsn(79L, 77), 66L);
212:
213: /* Put an entry down, should fit within file 79. */
214: fileManager.bumpLsn(11L);
215: assertEquals(DbLsn.makeLsn(79L, 88L), fileManager
216: .getLastUsedLsn());
217: assertEquals(77L, fileManager.getPrevEntryOffset());
218:
219: /* Put another entry in, should go to the next file. */
220: fileManager.bumpLsn(22L);
221: assertEquals(DbLsn.makeLsn(80L, FileManager
222: .firstLogEntryOffset()), fileManager.getLastUsedLsn());
223: assertEquals(0, fileManager.getPrevEntryOffset());
224: }
225:
226: /**
227: * Test log file naming.
228: */
229: public void testFileNameFormat() throws DatabaseException {
230:
231: String filePrefix = envHome + File.separator;
232: assertEquals(filePrefix + "00000001.jdb", fileManager
233: .getFullFileNames(1L)[0]);
234: assertEquals(filePrefix + "0000007b.jdb", fileManager
235: .getFullFileNames(123L)[0]);
236: }
237:
238: /**
239: * Test log file creation.
240: */
241: public void testFileCreation() throws IOException,
242: DatabaseException {
243:
244: FileManagerTestUtils.createLogFile(fileManager, envImpl,
245: FILE_SIZE);
246: FileManagerTestUtils.createLogFile(fileManager, envImpl,
247: FILE_SIZE);
248:
249: String[] jeFiles = fileManager
250: .listFiles(FileManager.JE_SUFFIXES);
251:
252: assertEquals("Should have two files", 2, jeFiles.length);
253:
254: /* Make a fake files with confusing names. */
255: File fakeFile1 = new File(envHome, "00000abx.jdb");
256: File fakeFile2 = new File(envHome, "10.10.jdb");
257: fakeFile1.createNewFile();
258: fakeFile2.createNewFile();
259:
260: jeFiles = fileManager.listFiles(FileManager.JE_SUFFIXES);
261: assertEquals("Should have two files", 2, jeFiles.length);
262:
263: /* Open the two existing log files. */
264: FileHandle file0Handle = fileManager.getFileHandle(0L);
265: FileHandle file1Handle = fileManager.getFileHandle(1L);
266:
267: jeFiles = fileManager.listFiles(FileManager.JE_SUFFIXES);
268: assertEquals("Should have two files", 2, jeFiles.length);
269: file0Handle.release();
270: file1Handle.release();
271:
272: /* Empty the cache and get them again. */
273: fileManager.clear();
274: file0Handle = fileManager.getFileHandle(0L);
275: file1Handle = fileManager.getFileHandle(1L);
276:
277: jeFiles = fileManager.listFiles(FileManager.JE_SUFFIXES);
278: assertEquals("Should have two files", 2, jeFiles.length);
279: file0Handle.close();
280: file1Handle.close();
281: file0Handle.release();
282: file1Handle.release();
283:
284: fakeFile1.delete();
285: fakeFile2.delete();
286: }
287:
288: /**
289: * Make sure we can find the last file.
290: */
291: public void testLastFile() throws IOException, DatabaseException {
292:
293: /* There shouldn't be any files here anymore. */
294: String[] jeFiles = fileManager
295: .listFiles(FileManager.JE_SUFFIXES);
296: assertTrue(jeFiles.length == 0);
297:
298: /* No files exist, should get null. */
299: assertNull("No last file", fileManager.getLastFileNum());
300:
301: /* Create some files, ask for the largest. */
302: File fakeFile1 = new File(envHome, "108.cif");
303: fakeFile1.createNewFile();
304: FileManagerTestUtils.createLogFile(fileManager, envImpl,
305: FILE_SIZE);
306: FileManagerTestUtils.createLogFile(fileManager, envImpl,
307: FILE_SIZE);
308: FileManagerTestUtils.createLogFile(fileManager, envImpl,
309: FILE_SIZE);
310:
311: assertEquals("Should have 2 as last file", 2L, fileManager
312: .getLastFileNum().longValue());
313: fakeFile1.delete();
314: }
315:
316: /**
317: * Make sure we can find the next file in a set of files.
318: */
319: public void testFollowingFile() throws IOException,
320: DatabaseException {
321:
322: /* There shouldn't be any files here anymore. */
323: String[] jeFiles = fileManager
324: .listFiles(FileManager.JE_SUFFIXES);
325: assertTrue(jeFiles.length == 0);
326:
327: /* No files exist, should get null. */
328: assertNull("No last file", fileManager.getFollowingFileNum(0,
329: true));
330: assertNull("No last file", fileManager.getFollowingFileNum(0,
331: false));
332: assertNull("No last file", fileManager.getFollowingFileNum(1,
333: true));
334: assertNull("No last file", fileManager.getFollowingFileNum(-1,
335: false));
336:
337: /* Create some files. */
338: File okFile1 = new File(envHome, "00000001.jdb");
339: okFile1.createNewFile();
340:
341: File fakeFile3 = new File(envHome, "003.jdb");
342: fakeFile3.createNewFile();
343:
344: File okFile6 = new File(envHome, "00000006.jdb");
345: okFile6.createNewFile();
346:
347: File okFile9 = new File(envHome, "00000009.jdb");
348: okFile9.createNewFile();
349:
350: /* Test forward */
351: assertEquals("Should get 6 next", 6L, fileManager
352: .getFollowingFileNum(2, true).longValue());
353: assertEquals("Should get 9 next, testing non-existent file",
354: 9L, fileManager.getFollowingFileNum(8, true)
355: .longValue());
356: assertNull("Should get null next", fileManager
357: .getFollowingFileNum(9, true));
358: assertNull("Should get null next", fileManager
359: .getFollowingFileNum(10, true));
360:
361: /* Test prev */
362: assertEquals("Should get 6 next, testing non-existent file",
363: 6L, fileManager.getFollowingFileNum(8, false)
364: .longValue());
365: assertEquals("Should get 6 next", 6L, fileManager
366: .getFollowingFileNum(9, false).longValue());
367: assertNull("Should get null next", fileManager
368: .getFollowingFileNum(1, false));
369: assertNull("Should get null next", fileManager
370: .getFollowingFileNum(0, false));
371:
372: okFile1.delete();
373: fakeFile3.delete();
374: okFile6.delete();
375: okFile9.delete();
376: }
377:
378: /**
379: * See if we can catch a file with an invalid header.
380: */
381: public void testBadHeader() throws IOException, DatabaseException {
382:
383: /* First try a bad environment r/w. */
384: try {
385: FileManager test = new FileManager(envImpl,
386: new File("xxyy"), true);
387: fail("expect creation of " + test + "to fail.");
388: } catch (LogException e) {
389: /* should throw */
390: }
391:
392: /* Next try a bad environment r/o. */
393: try {
394: FileManager test = new FileManager(envImpl,
395: new File("xxyy"), false);
396: fail("expect creation of " + test + "to fail.");
397: } catch (DatabaseException e) {
398: /* should throw */
399: }
400:
401: /* Now create a file, but mess up the header. */
402: FileManagerTestUtils.createLogFile(fileManager, envImpl,
403: FILE_SIZE);
404:
405: byte[] badData = new byte[] { 1, 1 };
406: int headerSize = FileManager.firstLogEntryOffset();
407: RandomAccessFile file0 = new RandomAccessFile(fileManager
408: .getFullFileName(0, FileManager.JE_SUFFIX),
409: FileManager.FileMode.READWRITE_MODE.getModeValue());
410: file0.write(badData);
411: file0.close();
412: fileManager.clear();
413:
414: try {
415: FileHandle file0Handle = fileManager.getFileHandle(0L);
416: fail("expect to catch a checksum error");
417: } catch (DbChecksumException e) {
418: }
419: }
420:
421: public void testTruncatedHeader() throws IOException,
422: DatabaseException {
423:
424: /* Create a log file */
425: FileManagerTestUtils.createLogFile(fileManager, envImpl,
426: FILE_SIZE);
427:
428: /* Truncate the header */
429: RandomAccessFile file0 = new RandomAccessFile(fileManager
430: .getFullFileName(0, FileManager.JE_SUFFIX),
431: FileManager.FileMode.READWRITE_MODE.getModeValue());
432: file0.getChannel().truncate(
433: FileManager.firstLogEntryOffset() / 2);
434: file0.close();
435:
436: try {
437: fileManager.getFileHandle(0);
438: fail("Should see assertion");
439: } catch (DatabaseException e) {
440: }
441: }
442:
443: /**
444: * Test the file cache.
445: */
446: public void testCache() throws Throwable {
447:
448: try {
449:
450: /*
451: * Make five log files. The file descriptor cache should be empty.
452: */
453: FileManagerTestUtils.createLogFile(fileManager, envImpl,
454: FILE_SIZE);
455: FileManagerTestUtils.createLogFile(fileManager, envImpl,
456: FILE_SIZE);
457: FileManagerTestUtils.createLogFile(fileManager, envImpl,
458: FILE_SIZE);
459: FileManagerTestUtils.createLogFile(fileManager, envImpl,
460: FILE_SIZE);
461: FileManagerTestUtils.createLogFile(fileManager, envImpl,
462: FILE_SIZE);
463:
464: Long f0 = new Long(0L);
465: Long f1 = new Long(1L);
466: Long f2 = new Long(2L);
467: Long f3 = new Long(3L);
468: Long f4 = new Long(4L);
469:
470: Set keySet = fileManager.getCacheKeys();
471: assertEquals("should have 0 keys", 0, keySet.size());
472:
473: /*
474: * Get file descriptors for three files, expect 3 handles in the
475: * cache.
476: */
477: FileHandle f0Handle = fileManager.getFileHandle(0);
478: FileHandle f1Handle = fileManager.getFileHandle(1);
479: FileHandle f2Handle = fileManager.getFileHandle(2);
480: keySet = fileManager.getCacheKeys();
481: assertEquals("should have 3 keys", 3, keySet.size());
482: assertTrue(keySet.contains(f0));
483: assertTrue(keySet.contains(f1));
484: assertTrue(keySet.contains(f2));
485:
486: /*
487: * Ask for a fourth handle, the cache should grow even though it
488: * was set to 3 as a starting size, because all handles are
489: * locked. Do it within another thread, otherwise we'll get a
490: * latch-already-held exception when we test the other handles in
491: * the cache. The other thread will get the handle and then release
492: * it.
493: */
494: CachingThread otherThread = new CachingThread(fileManager,
495: 3);
496: otherThread.start();
497: otherThread.join();
498:
499: keySet = fileManager.getCacheKeys();
500: assertEquals("should have 4 keys", 4, keySet.size());
501: assertTrue(keySet.contains(f0));
502: assertTrue(keySet.contains(f1));
503: assertTrue(keySet.contains(f2));
504: assertTrue(keySet.contains(f3));
505:
506: /*
507: * Now ask for another file. The cache should not grow, because no
508: * handles are locked and there's room to evict one.
509: */
510: f0Handle.release();
511: f1Handle.release();
512: f2Handle.release();
513: FileHandle f4Handle = fileManager.getFileHandle(4);
514: keySet = fileManager.getCacheKeys();
515: assertEquals("should have 4 keys", 4, keySet.size());
516: assertTrue(keySet.contains(f4));
517:
518: f4Handle.release();
519:
520: /* Clearing should release all file descriptors. */
521: fileManager.clear();
522: assertEquals("should have 0 keys", 0, fileManager
523: .getCacheKeys().size());
524: } catch (Throwable t) {
525: t.printStackTrace();
526: throw t;
527: }
528: }
529:
530: public void testFlipFile() throws Throwable {
531:
532: /*
533: * The setUp() method opens a standalone FileManager, but in this test
534: * case we need a regular Environment. On Windows, we can't lock the
535: * file range twice in FileManager.lockEnvironment, so we must close
536: * the standalone FileManager here before opening a regular
537: * environment.
538: */
539: fileManager.clear();
540: fileManager.close();
541: fileManager = null;
542:
543: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
544: envConfig.setAllowCreate(true);
545: envConfig.setTransactional(true);
546: Environment env = new Environment(envHome, envConfig);
547: EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env);
548: FileManager fileManager = envImpl.getFileManager();
549:
550: DatabaseConfig dbConfig = new DatabaseConfig();
551: dbConfig.setAllowCreate(true);
552: Database exampleDb = env.openDatabase(null, "simpleDb",
553: dbConfig);
554:
555: assertEquals("Should have 0 as current file", 0L, fileManager
556: .getCurrentFileNum());
557: long flipLsn = envImpl.forceLogFileFlip();
558: assertEquals("LSN should be 1 post-flip", 1L, DbLsn
559: .getFileNumber(flipLsn));
560: DatabaseEntry key = new DatabaseEntry();
561: DatabaseEntry data = new DatabaseEntry();
562: key.setData("key".getBytes());
563: data.setData("data".getBytes());
564: exampleDb.put(null, key, data);
565: assertEquals("Should have 1 as last file", 1L, fileManager
566: .getCurrentFileNum());
567: exampleDb.close();
568: env.close();
569: }
570:
571: class CachingThread extends Thread {
572: private FileManager fileManager;
573: private long fileNum;
574:
575: private FileHandle handle;
576:
577: CachingThread(FileManager fileManager, long fileNum) {
578: this .fileManager = fileManager;
579: this .fileNum = fileNum;
580: }
581:
582: public void run() {
583: try {
584: handle = fileManager.getFileHandle(fileNum);
585: handle.release();
586: } catch (Exception e) {
587: fail(e.getMessage());
588: }
589: }
590:
591: FileHandle getHandle() {
592: return handle;
593: }
594: }
595: }
|