001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: StatsFileReader.java,v 1.15.2.4 2008/01/07 15:14:13 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.io.IOException;
012: import java.nio.ByteBuffer;
013: import java.text.NumberFormat;
014: import java.util.ArrayList;
015: import java.util.Comparator;
016: import java.util.Iterator;
017: import java.util.Map;
018: import java.util.TreeMap;
019:
020: import com.sleepycat.je.DatabaseException;
021: import com.sleepycat.je.config.EnvironmentParams;
022: import com.sleepycat.je.dbi.EnvironmentImpl;
023: import com.sleepycat.je.utilint.DbLsn;
024:
025: /**
026: * The StatsFileReader generates stats about the log entries read, such as the
027: * count of each type of entry, the number of bytes, minimum and maximum sized
028: * log entry.
029: */
030: public class StatsFileReader extends DumpFileReader {
031:
032: /* Keyed by LogEntryType, data is EntryInfo. */
033: private Map entryInfoMap;
034: private long totalLogBytes;
035: private long totalCount;
036:
037: /* Keep stats on log composition in terms of ckpt intervals. */
038: private ArrayList ckptList;
039: private CheckpointCounter ckptCounter;
040: private long firstLsnRead;
041:
042: /**
043: * Create this reader to start at a given LSN.
044: */
045: public StatsFileReader(EnvironmentImpl envImpl, int readBufferSize,
046: long startLsn, long finishLsn, String entryTypes,
047: String txnIds, boolean verbose) throws IOException,
048: DatabaseException {
049:
050: super (envImpl, readBufferSize, startLsn, finishLsn, entryTypes,
051: txnIds, verbose);
052: entryInfoMap = new TreeMap(new LogEntryTypeComparator());
053: totalLogBytes = 0;
054: totalCount = 0;
055:
056: ckptCounter = new CheckpointCounter();
057: ckptList = new ArrayList();
058: if (verbose) {
059: ckptList.add(ckptCounter);
060: }
061: }
062:
063: /**
064: * This reader collects stats about the log entry.
065: */
066: protected boolean processEntry(ByteBuffer entryBuffer)
067: throws DatabaseException {
068:
069: byte currentType = currentEntryHeader.getType();
070: byte version = currentEntryHeader.getVersion();
071: int itemSize = currentEntryHeader.getItemSize();
072: int headerSize = currentEntryHeader.getSize();
073:
074: /*
075: * Record various stats based on the entry header, then move the buffer
076: * forward to skip ahead.
077: */
078: LogEntryType lastEntryType = LogEntryType.findType(currentType,
079: version);
080: entryBuffer.position(entryBuffer.position() + itemSize);
081:
082: /*
083: * Get the info object for it, if this is the first time it's seen,
084: * create an info object and insert it.
085: */
086: EntryInfo info = (EntryInfo) entryInfoMap.get(lastEntryType);
087: if (info == null) {
088: info = new EntryInfo();
089: entryInfoMap.put(lastEntryType, info);
090: }
091:
092: /* Update counts. */
093: info.count++;
094: totalCount++;
095: if (LogEntryType.isEntryProvisional(version)) {
096: info.provisionalCount++;
097: }
098: int size = itemSize + headerSize;
099: info.totalBytes += size;
100: info.headerBytes += headerSize;
101: totalLogBytes += size;
102:
103: if ((info.minBytes == 0) || (info.minBytes > size)) {
104: info.minBytes = size;
105: }
106: if (info.maxBytes < size) {
107: info.maxBytes = size;
108: }
109:
110: if (verbose) {
111: if (firstLsnRead == DbLsn.NULL_LSN) {
112: firstLsnRead = getLastLsn();
113: }
114:
115: if (currentType == LogEntryType.LOG_CKPT_END.getTypeNum()) {
116: /* start counting a new interval */
117: ckptCounter.endCkptLsn = getLastLsn();
118: ckptCounter = new CheckpointCounter();
119: ckptList.add(ckptCounter);
120: } else {
121: ckptCounter.increment(this , currentType);
122: }
123: }
124:
125: return true;
126: }
127:
128: public void summarize() {
129: System.out.println("Log statistics:");
130: Iterator iter = entryInfoMap.entrySet().iterator();
131:
132: NumberFormat form = NumberFormat.getIntegerInstance();
133: NumberFormat percentForm = NumberFormat.getInstance();
134: percentForm.setMaximumFractionDigits(1);
135: System.out.println(pad("type") + pad("total")
136: + pad("provisional") + pad("total") + pad("min")
137: + pad("max") + pad("avg") + pad("entries"));
138:
139: System.out.println(pad("") + pad("count") + pad("count")
140: + pad("bytes") + pad("bytes") + pad("bytes")
141: + pad("bytes") + pad("as % of log"));
142:
143: long realTotalBytes = 0;
144:
145: while (iter.hasNext()) {
146: Map.Entry m = (Map.Entry) iter.next();
147: EntryInfo info = (EntryInfo) m.getValue();
148: StringBuffer sb = new StringBuffer();
149: LogEntryType entryType = (LogEntryType) m.getKey();
150: sb.append(pad(entryType.toString()));
151: sb.append(pad(form.format(info.count)));
152: sb.append(pad(form.format(info.provisionalCount)));
153: sb.append(pad(form.format(info.totalBytes)));
154: sb.append(pad(form.format(info.minBytes)));
155: sb.append(pad(form.format(info.maxBytes)));
156: sb.append(pad(form
157: .format((long) (info.totalBytes / info.count))));
158: double entryPercent = ((double) (info.totalBytes * 100) / totalLogBytes);
159: sb.append(pad(percentForm.format(entryPercent)));
160: System.out.println(sb.toString());
161:
162: /* Calculate key/data size for transactional LN */
163: if (entryType == LogEntryType.LOG_LN_TRANSACTIONAL) {
164: /*
165: LN_TX entry overhead:
166: 8 bytes node id
167: 1 byte boolean (whether data exists or is null)
168: 4 bytes data size
169: 4 bytes key size
170: 4 bytes database id
171: 8 bytes abort LSN
172: 1 byte abortKnownDeleted
173: 8 bytes txn id
174: 8 bytes lastlogged LSN (backpointer for txn)
175: */
176:
177: int overhead = (info.count * 46) + info.headerBytes;
178: realTotalBytes += (info.totalBytes - overhead);
179: }
180:
181: /* Calculate key/data size for non-transactional LN */
182: if (entryType == LogEntryType.LOG_LN) {
183: /*
184: LN_TX entry overhead:
185: 8 bytes node id
186: 1 byte boolean (whether data exists or is null)
187: 4 bytes data size
188: 4 bytes key size
189: 4 bytes database id
190: */
191: int overhead = (info.count * 21) + info.headerBytes;
192: realTotalBytes += (info.totalBytes - overhead);
193: }
194: }
195:
196: /* Print special line for key/data */
197: StringBuffer sb = new StringBuffer();
198: sb.append(pad("key/data"));
199: sb.append(pad(""));
200: sb.append(pad(""));
201: sb.append(pad(form.format(realTotalBytes)));
202: sb.append(pad(""));
203: sb.append(pad(""));
204: sb.append(pad(""));
205: String realSize = "("
206: + percentForm.format((double) (realTotalBytes * 100)
207: / totalLogBytes) + ")";
208: sb.append(pad(realSize));
209: System.out.println(sb.toString());
210:
211: System.out.println("\nTotal bytes in portion of log read: "
212: + form.format(totalLogBytes));
213: System.out.println("Total number of entries: "
214: + form.format(totalCount));
215:
216: if (verbose) {
217: summarizeCheckpointInfo();
218: }
219: }
220:
221: private String pad(String result) {
222: int spaces = 15 - result.length();
223: StringBuffer sb = new StringBuffer();
224: for (int i = 0; i < spaces; i++) {
225: sb.append(" ");
226: }
227: sb.append(result);
228: return sb.toString();
229: }
230:
231: private void summarizeCheckpointInfo() {
232: System.out.println("\nPer checkpoint interval info:");
233:
234: /*
235: * Print out checkpoint interval info.
236: * If the log looks like this:
237: *
238: * start of log
239: * ckpt1 start
240: * ckpt1 end
241: * ckpt2 start
242: * ckpt2 end
243: * end of log
244: *
245: * There are 3 ckpt intervals
246: * start of log->ckpt1 end
247: * ckpt1 end -> ckpt2 end
248: * ckpt2 end -> end of log
249: */
250: System.out.println(pad("lnTxn") + pad("ln") + pad("mapLNTxn")
251: + pad("mapLN") + pad("end-end") + // ckpt n-1 end -> ckpt n end
252: pad("end-start") + // ckpt n-1 end -> ckpt n start
253: pad("start-end") + // ckpt n start -> ckpt n end
254: pad("maxLNReplay") + pad("ckptEnd"));
255:
256: long logFileMax;
257: try {
258: logFileMax = envImpl.getConfigManager().getLong(
259: EnvironmentParams.LOG_FILE_MAX);
260: } catch (DatabaseException e) {
261: e.printStackTrace();
262: return;
263: }
264:
265: Iterator iter = ckptList.iterator();
266: CheckpointCounter prevCounter = null;
267: NumberFormat form = NumberFormat.getInstance();
268: while (iter.hasNext()) {
269: CheckpointCounter c = (CheckpointCounter) iter.next();
270: StringBuffer sb = new StringBuffer();
271:
272: /* Entry type counts. */
273: int maxTxnLNs = c.preStartLNTxnCount
274: + c.postStartLNTxnCount;
275: sb.append(pad(form.format(maxTxnLNs)));
276: int maxLNs = c.preStartLNCount + c.postStartLNCount;
277: sb.append(pad(form.format(maxLNs)));
278: sb.append(pad(form.format(c.preStartMapLNTxnCount
279: + c.postStartMapLNTxnCount)));
280: sb.append(pad(form.format(c.preStartMapLNCount
281: + c.postStartMapLNCount)));
282:
283: /* Checkpoint interval distance. */
284: long end = (c.endCkptLsn == DbLsn.NULL_LSN) ? getLastLsn()
285: : c.endCkptLsn;
286: long endToEndDistance = 0;
287:
288: FileManager fileManager = envImpl.getFileManager();
289: if (prevCounter == null) {
290: endToEndDistance = DbLsn.getWithCleaningDistance(end,
291: fileManager, firstLsnRead, logFileMax);
292: } else {
293: endToEndDistance = DbLsn
294: .getWithCleaningDistance(end, fileManager,
295: prevCounter.endCkptLsn, logFileMax);
296: }
297: sb.append(pad(form.format(endToEndDistance)));
298:
299: /*
300: * Interval between last checkpoint end and
301: * this checkpoint start.
302: */
303: long start = (c.startCkptLsn == DbLsn.NULL_LSN) ? getLastLsn()
304: : c.startCkptLsn;
305: long endToStartDistance = 0;
306:
307: if (prevCounter == null) {
308: endToStartDistance = DbLsn.getWithCleaningDistance(
309: start, fileManager, firstLsnRead, logFileMax);
310: } else {
311: endToStartDistance = DbLsn.getWithCleaningDistance(
312: start, fileManager, prevCounter.endCkptLsn,
313: logFileMax);
314: }
315: sb.append(pad(form.format(endToStartDistance)));
316:
317: /*
318: * Interval between ckpt start and ckpt end.
319: */
320: long startToEndDistance = 0;
321: if ((c.startCkptLsn != DbLsn.NULL_LSN)
322: && (c.endCkptLsn != DbLsn.NULL_LSN)) {
323: startToEndDistance = DbLsn.getWithCleaningDistance(
324: c.endCkptLsn, fileManager, c.startCkptLsn,
325: logFileMax);
326: }
327: sb.append(pad(form.format(startToEndDistance)));
328:
329: /*
330: * The maximum number of LNs to replay includes the portion of LNs
331: * from checkpoint start to checkpoint end of the previous
332: * interval.
333: */
334: int maxReplay = maxLNs + maxTxnLNs;
335: if (prevCounter != null) {
336: maxReplay += prevCounter.postStartLNTxnCount;
337: maxReplay += prevCounter.postStartLNCount;
338: }
339: sb.append(pad(form.format(maxReplay)));
340:
341: if (c.endCkptLsn == DbLsn.NULL_LSN) {
342: sb.append(" ").append(
343: DbLsn.getNoFormatString(getLastLsn()));
344: } else {
345: sb.append(" ").append(
346: DbLsn.getNoFormatString(c.endCkptLsn));
347: }
348:
349: System.out.println(sb.toString());
350: prevCounter = c;
351: }
352: }
353:
354: static class EntryInfo {
355: public int count;
356: public int provisionalCount;
357: public long totalBytes;
358: public int headerBytes;
359: public int minBytes;
360: public int maxBytes;
361:
362: EntryInfo() {
363: count = 0;
364: provisionalCount = 0;
365: totalBytes = 0;
366: headerBytes = 0;
367: minBytes = 0;
368: maxBytes = 0;
369: }
370: }
371:
372: static class LogEntryTypeComparator implements Comparator {
373: public int compare(Object o1, Object o2) {
374: if (o1 == null) {
375: return -1;
376: }
377:
378: if (o2 == null) {
379: return 1;
380: }
381:
382: if (o1 instanceof LogEntryType
383: && o2 instanceof LogEntryType) {
384: Byte t1 = new Byte(((LogEntryType) o1).getTypeNum());
385: Byte t2 = new Byte(((LogEntryType) o2).getTypeNum());
386: return t1.compareTo(t2);
387: } else {
388: throw new IllegalArgumentException(
389: "non LogEntryType passed to LogEntryType.compare");
390: }
391: }
392: }
393:
394: /*
395: * Accumulate the count of items from checkpoint end->checkpoint end.
396: */
397: static class CheckpointCounter {
398: public long startCkptLsn = DbLsn.NULL_LSN;
399: public long endCkptLsn = DbLsn.NULL_LSN;
400: public int preStartLNTxnCount;
401: public int preStartLNCount;
402: public int preStartMapLNTxnCount;
403: public int preStartMapLNCount;
404: public int postStartLNTxnCount;
405: public int postStartLNCount;
406: public int postStartMapLNTxnCount;
407: public int postStartMapLNCount;
408:
409: public void increment(FileReader reader,
410: byte currentEntryTypeNum) {
411: if (currentEntryTypeNum == LogEntryType.LOG_CKPT_START
412: .getTypeNum()) {
413: startCkptLsn = reader.getLastLsn();
414: } else if (currentEntryTypeNum == LogEntryType.LOG_LN_TRANSACTIONAL
415: .getTypeNum()) {
416: if (startCkptLsn == DbLsn.NULL_LSN) {
417: preStartLNTxnCount++;
418: } else {
419: postStartLNTxnCount++;
420: }
421: } else if (currentEntryTypeNum == LogEntryType.LOG_LN
422: .getTypeNum()) {
423: if (startCkptLsn == DbLsn.NULL_LSN) {
424: preStartLNCount++;
425: } else {
426: postStartLNCount++;
427: }
428: } else if (currentEntryTypeNum == LogEntryType.LOG_MAPLN
429: .getTypeNum()) {
430: if (startCkptLsn == DbLsn.NULL_LSN) {
431: preStartMapLNCount++;
432: } else {
433: postStartMapLNCount++;
434: }
435: } else if (currentEntryTypeNum == LogEntryType.LOG_MAPLN_TRANSACTIONAL
436: .getTypeNum()) {
437: if (startCkptLsn == DbLsn.NULL_LSN) {
438: preStartMapLNTxnCount++;
439: } else {
440: postStartMapLNTxnCount++;
441: }
442: }
443: }
444: }
445: }
|