1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.log; 9 10 import java.io.File; 11 import java.io.RandomAccessFile; 12 13 import com.sleepycat.je.EnvironmentConfig; 14 import com.sleepycat.je.dbi.EnvironmentImpl; 15 import com.sleepycat.je.utilint.DbLsn; 16 import com.sleepycat.je.utilint.LoggerUtils; 17 18 /** 19 * Warm-up the file system cache during startup, for some portion of the log 20 * that is not being read by recovery. 21 * 22 * This functionality is documented for the most part by {@link 23 * EnvironmentConfig#LOG_FILE_WARM_UP_SIZE}. 24 * 25 * One thing not mentioned is that cleaner log deletion is disabled during the 26 * warm-up. This is necessary to avoid dealing with the possibility of files 27 * being deleted while being read for warm-up. Although it is extremely likely 28 * that warm-up will finish before a cleaner-checkpoint cycle, we disable file 29 * deletions simply to avoid having to code for this remote possibility. This 30 * isn't documented because file deletions will probably never be prevented by 31 * warm-up, documenting this would only cause unnecessary confusion. 32 */ 33 public class FileCacheWarmer extends Thread { 34 35 private final EnvironmentImpl envImpl; 36 private final long recoveryStartLsn; 37 private final long endOfLogLsn; 38 private final int warmUpSize; 39 private final int bufSize; 40 private volatile boolean stop; 41 FileCacheWarmer(final EnvironmentImpl envImpl, final long recoveryStartLsn, final long endOfLogLsn, final int warmUpSize, final int bufSize)42 FileCacheWarmer(final EnvironmentImpl envImpl, 43 final long recoveryStartLsn, 44 final long endOfLogLsn, 45 final int warmUpSize, 46 final int bufSize) { 47 this.envImpl = envImpl; 48 this.recoveryStartLsn = recoveryStartLsn; 49 this.endOfLogLsn = endOfLogLsn; 50 this.warmUpSize = warmUpSize; 51 this.bufSize = bufSize; 52 stop = false; 53 } 54 55 /** 56 * Stops this thread. At most one read will occur after calling this 57 * method, and then the thread will exit. 58 */ shutdown()59 void shutdown() { 60 stop = true; 61 } 62 63 @Override run()64 public void run() { 65 try { 66 doRun(); 67 } catch (Throwable e) { 68 69 /* 70 * Log error as SEVERE but do not invalidate environment since it 71 * perfectly usable. 72 */ 73 LoggerUtils.traceAndLogException( 74 envImpl, FileCacheWarmer.class.getName(), "run", 75 "Unable to warm file system cache due to exception", e); 76 77 } finally { 78 /* Ensure that this thread can be GC'd after it stops. */ 79 envImpl.getFileManager().clearFileCacheWarmer(); 80 } 81 } 82 doRun()83 private void doRun() 84 throws Throwable { 85 86 final FileManager fm = envImpl.getFileManager(); 87 88 final long ONE_MB = 1L << 20; 89 90 long remaining = (warmUpSize * ONE_MB) - 91 DbLsn.getTrueDistance(recoveryStartLsn, endOfLogLsn, fm); 92 93 if (remaining <= 0) { 94 return; 95 } 96 97 // System.out.println("FileCacheWarmer start " + remaining); 98 99 final byte[] buf = new byte[bufSize]; 100 101 long fileNum = DbLsn.getFileNumber(recoveryStartLsn); 102 long fileOff = DbLsn.getFileOffset(recoveryStartLsn); 103 104 String filePath = fm.getFullFileName(fileNum); 105 File file = new File(filePath); 106 RandomAccessFile raf = null; 107 108 envImpl.getCleaner().addProtectedFileRange(0L); 109 try { 110 raf = new RandomAccessFile(file, "r"); 111 112 while (!stop && remaining > 0) { 113 114 if (fileOff <= 0) { 115 raf.close(); 116 raf = null; 117 118 final Long nextFileNum = fm.getFollowingFileNum( 119 fileNum, false /*forward*/); 120 121 if (nextFileNum == null) { 122 throw new RuntimeException( 123 "No file preceding " + fileNum); 124 } 125 126 fileNum = nextFileNum; 127 filePath = fm.getFullFileName(fileNum); 128 file = new File(filePath); 129 raf = new RandomAccessFile(file, "r"); 130 fileOff = raf.length(); 131 } 132 133 final long pos = Math.max(0L, fileOff - bufSize); 134 raf.seek(pos); 135 136 final int bytes = (int) (fileOff - pos); 137 final int read = raf.read(buf, 0, bytes); 138 139 if (read != bytes) { 140 throw new IllegalStateException( 141 "Requested " + bytes + " bytes but read " + read); 142 } 143 144 remaining -= bytes; 145 fileOff = pos; 146 } 147 148 raf.close(); 149 raf = null; 150 151 } finally { 152 153 // System.out.println( 154 // "FileCacheWarmer finish " + remaining + " " + stop); 155 156 envImpl.getCleaner().removeProtectedFileRange(0L); 157 158 if (raf != null) { 159 try { 160 raf.close(); 161 } catch (Exception e) { 162 /* Ignore this. Another exception is in flight. */ 163 } 164 } 165 } 166 } 167 } 168