1 package simpledb.log; 2 3 import simpledb.server.SimpleDB; 4 import simpledb.file.*; 5 import static simpledb.file.Page.*; 6 import java.util.*; 7 8 /** 9 * The low-level log manager. 10 * This log manager is responsible for writing log records 11 * into a log file. 12 * A log record can be any sequence of integer and string values. 13 * The log manager does not understand the meaning of these 14 * values, which are written and read by the 15 * {@link simpledb.tx.recovery.RecoveryMgr recovery manager}. 16 * @author Edward Sciore 17 */ 18 public class LogMgr implements Iterable<BasicLogRecord> { 19 /** 20 * The location where the pointer to the last integer in the page is. 21 * A value of 0 means that the pointer is the first value in the page. 22 */ 23 public static final int LAST_POS = 0; 24 25 private String logfile; 26 private Page mypage = new Page(); 27 private Block currentblk; 28 private int currentpos; 29 30 /** 31 * Creates the manager for the specified log file. 32 * If the log file does not yet exist, it is created 33 * with an empty first block. 34 * This constructor depends on a {@link FileMgr} object 35 * that it gets from the method 36 * {@link simpledb.server.SimpleDB#fileMgr()}. 37 * That object is created during system initialization. 38 * Thus this constructor cannot be called until 39 * {@link simpledb.server.SimpleDB#initFileMgr(String)} 40 * is called first. 41 * @param logfile the name of the log file 42 */ 43 public LogMgr(String logfile) { 44 this.logfile = logfile; 45 int logsize = SimpleDB.fileMgr().size(logfile); 46 if (logsize == 0) 47 appendNewBlock(); 48 else { 49 currentblk = new Block(logfile, logsize-1); 50 mypage.read(currentblk); 51 currentpos = getLastRecordPosition() + INT_SIZE; 52 } 53 } 54 55 /** 56 * Ensures that the log records corresponding to the 57 * specified LSN has been written to disk. 58 * All earlier log records will also be written to disk. 59 * @param lsn the LSN of a log record 60 */ 61 public void flush(int lsn) { 62 if (lsn >= currentLSN()) 63 flush(); 64 } 65 66 /** 67 * Returns an iterator for the log records, 68 * which will be returned in reverse order starting with the most recent. 69 * @see java.lang.Iterable#iterator() 70 */ 71 public synchronized Iterator<BasicLogRecord> iterator() { 72 flush(); 73 return new LogIterator(currentblk); 74 } 75 76 /** 77 * Appends a log record to the file. 78 * The record contains an arbitrary array of strings and integers. 79 * The method also writes an integer to the end of each log record whose value 80 * is the offset of the corresponding integer for the previous log record. 81 * These integers allow log records to be read in reverse order. 82 * @param rec the list of values 83 * @return the LSN of the final value 84 */ 85 public synchronized int append(Object[] rec) { 86 int recsize = INT_SIZE; // 4 bytes for the integer that points to the previous log record 87 for (Object obj : rec) 88 recsize += size(obj); 89 if (currentpos + recsize >= BLOCK_SIZE){ // the log record doesn't fit, 90 flush(); // so move to the next block. 91 appendNewBlock(); 92 } 93 for (Object obj : rec) 94 appendVal(obj); 95 finalizeRecord(); 96 return currentLSN(); 97 } 98 99 /** 100 * Adds the specified value to the page at the position denoted by 101 * currentpos. Then increments currentpos by the size of the value. 102 * @param val the integer or string to be added to the page 103 */ 104 private void appendVal(Object val) { 105 if (val instanceof String) 106 mypage.setString(currentpos, (String)val); 107 else 108 mypage.setInt(currentpos, (Integer)val); 109 currentpos += size(val); 110 } 111 112 /** 113 * Calculates the size of the specified integer or string. 114 * @param val the value 115 * @return the size of the value, in bytes 116 */ 117 private int size(Object val) { 118 if (val instanceof String) { 119 String sval = (String) val; 120 return STR_SIZE(sval.length()); 121 } 122 else 123 return INT_SIZE; 124 } 125 126 /** 127 * Returns the LSN of the most recent log record. 128 * As implemented, the LSN is the block number where the record is stored. 129 * Thus every log record in a block has the same LSN. 130 * @return the LSN of the most recent log record 131 */ 132 private int currentLSN() { 133 return currentblk.number(); 134 } 135 136 /** 137 * Writes the current page to the log file. 138 */ 139 private void flush() { 140 mypage.write(currentblk); 141 } 142 143 /** 144 * Clear the current page, and append it to the log file. 145 */ 146 private void appendNewBlock() { 147 setLastRecordPosition(0); 148 currentpos = INT_SIZE; 149 currentblk = mypage.append(logfile); 150 } 151 152 /** 153 * Sets up a circular chain of pointers to the records in the page. 154 * There is an integer added to the end of each log record 155 * whose value is the offset of the previous log record. 156 * The first four bytes of the page contain an integer whose value 157 * is the offset of the integer for the last log record in the page. 158 */ 159 private void finalizeRecord() { 160 mypage.setInt(currentpos, getLastRecordPosition()); 161 setLastRecordPosition(currentpos); 162 currentpos += INT_SIZE; 163 } 164 165 private int getLastRecordPosition() { 166 return mypage.getInt(LAST_POS); 167 } 168 169 private void setLastRecordPosition(int pos) { 170 mypage.setInt(LAST_POS, pos); 171 } 172 }