blob: ed11d5c243e2293e0599223bf760b1ff9ed2f46e [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.cleaner;
import java.nio.ByteBuffer;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.Loggable;
import com.sleepycat.je.log.entry.LNLogEntry;
/**
* Per-file utilization counters. The UtilizationProfile stores a persistent
* map of file number to FileSummary.
*/
public class FileSummary implements Loggable, Cloneable {
/* Persistent fields. */
public int totalCount; // Total # of log entries
public int totalSize; // Total bytes in log file
public int totalINCount; // Number of IN log entries
public int totalINSize; // Byte size of IN log entries
public int totalLNCount; // Number of LN log entries
public int totalLNSize; // Byte size of LN log entries
public int maxLNSize; // Byte size of largest LN log entry
public int obsoleteINCount; // Number of obsolete IN log entries
public int obsoleteLNCount; // Number of obsolete LN log entries
public int obsoleteLNSize; // Byte size of obsolete LN log entries
public int obsoleteLNSizeCounted; // Number obsolete LNs with size counted
/**
* Creates an empty summary.
*/
public FileSummary() {
}
public FileSummary clone() {
try {
return (FileSummary) super.clone();
} catch (CloneNotSupportedException willNeverOccur) {
return null;
}
}
/**
* Returns whether this summary contains any non-zero totals.
*/
public boolean isEmpty() {
return totalCount == 0 &&
totalSize == 0 &&
obsoleteINCount == 0 &&
obsoleteLNCount == 0;
}
/**
* Returns the approximate byte size of all obsolete LN entries, using the
* average LN size for LN sizes that were not counted.
*/
public int getObsoleteLNSize() {
if (totalLNCount == 0) {
return 0;
}
/* Normalize obsolete amounts to account for double-counting. */
final int obsLNCount = Math.min(obsoleteLNCount, totalLNCount);
final int obsLNSize = Math.min(obsoleteLNSize, totalLNSize);
final int obsLNSizeCounted = Math.min(obsoleteLNSizeCounted,
obsLNCount);
/*
* Use the tracked obsolete size for all entries for which the size was
* counted, plus the average size for all obsolete entries whose size
* was not counted.
*/
long obsSize = obsLNSize;
final int obsCountNotCounted = obsLNCount - obsLNSizeCounted;
if (obsCountNotCounted > 0) {
/*
* When there are any obsolete LNs with sizes uncounted, we add an
* obsolete amount that is the product of the number of LNs
* uncounted and the average LN size.
*/
final float avgLNSizeNotCounted = getAvgObsoleteLNSizeNotCounted();
if (!Float.isNaN(avgLNSizeNotCounted)) {
obsSize += (int) (obsCountNotCounted * avgLNSizeNotCounted);
}
}
/* Don't return an impossibly large estimate. */
return (obsSize > totalLNSize) ? totalLNSize : (int) obsSize;
}
/**
* Returns the average size for LNs with sizes not counted, or NaN if
* there are no such LNs.
*
* In FileSummaryLN version 3 and greater the obsolete size is normally
* counted, but not in exceptional circumstances such as recovery. If it
* is not counted, obsoleteLNSizeCounted will be less than obsoleteLNCount.
*
* In log version 8 and greater, we don't count the size when the LN is not
* resident in cache during update/delete, and CLEANER_FETCH_OBSOLETE_SIZE
* is false (the default setting).
*
* We added maxLNSize in version 8 for use in estimating obsolete LN sizes.
*
* To compute the average LN size, we only consider the LNs (both obsolete
* and non-obsolete) for which the size has not been counted. This
* increases accuracy when counted and uncounted LN sizes are not uniform.
* An example is when large LNs are inserted and deleted. The size of the
* deleted LN log entry (which is small) is always counted, but the
* previous version (which has a large size) may not be counted.
*/
public float getAvgObsoleteLNSizeNotCounted() {
/* Normalize obsolete amounts to account for double-counting. */
final int obsLNCount = Math.min(obsoleteLNCount, totalLNCount);
final int obsLNSize = Math.min(obsoleteLNSize, totalLNSize);
final int obsLNSizeCounted = Math.min(obsoleteLNSizeCounted,
obsLNCount);
final int obsCountNotCounted = obsLNCount - obsLNSizeCounted;
if (obsCountNotCounted <= 0) {
return Float.NaN;
}
final int totalSizeNotCounted = totalLNSize - obsLNSize;
final int totalCountNotCounted = totalLNCount - obsLNSizeCounted;
if (totalSizeNotCounted <= 0 || totalCountNotCounted <= 0) {
return Float.NaN;
}
return totalSizeNotCounted / ((float) totalCountNotCounted);
}
/**
* Returns the maximum possible obsolete LN size, using the maximum LN size
* for LN sizes that were not counted.
*/
public int getMaxObsoleteLNSize() {
/*
* In log version 7 and earlier the maxLNSize is not available. It is
* safe to use getObsoleteLNSize in that case, because LSN locking was
* not used and the obsolete size was counted for updates and deletes.
*/
if (maxLNSize == 0) {
return getObsoleteLNSize();
}
if (totalLNCount == 0) {
return 0;
}
/* Normalize obsolete amounts to account for double-counting. */
final int obsLNCount = Math.min(obsoleteLNCount, totalLNCount);
final int obsLNSize = Math.min(obsoleteLNSize, totalLNSize);
final int obsLNSizeCounted = Math.min(obsoleteLNSizeCounted,
obsLNCount);
/*
* Use the tracked obsolete size for all entries for which the size was
* counted, plus the maximum possible size for all obsolete entries
* whose size was not counted.
*/
long obsSize = obsLNSize;
final long obsCountNotCounted = obsLNCount - obsLNSizeCounted;
if (obsCountNotCounted > 0) {
/*
* When there are any obsolete LNs with sizes uncounted, we add an
* obsolete amount that is the minimum of two values. Either value
* may be much higher than the true obsolete amount, but by taking
* their minimum we use a much more realistic obsolete amount.
*
* maxLNSizeNotCounted is the maximum obsolete not counted, based
* on the multiplying maxLNSize and the number of obsolete LNs not
* counted.
*
* maxObsSizeNotCounted is also an upper bound on the obsolete size
* not not counted. The (totalLNSize - obsLNSize) gives the amount
* non-obsolete plus the obsolete amount not counted. From this we
* subtract the minimum non-obsolete size, based on the minimum
* size of any LN. This leaves the maximum obsolete size not
* counted.
*
* Note that the mutiplication immediately below would overflow if
* type 'int' instead of 'long' were used for the operands. This
* was fixed in [#21106].
*/
final long maxLNSizeNotCounted = obsCountNotCounted * maxLNSize;
final long maxObsSizeNotCounted = totalLNSize - obsLNSize -
((totalLNCount - obsLNCount) * LNLogEntry.MIN_LOG_SIZE);
obsSize += Math.min(maxLNSizeNotCounted, maxObsSizeNotCounted);
}
/* Don't return an impossibly large estimate. */
return (obsSize > totalLNSize) ? totalLNSize : (int) obsSize;
}
/**
* Returns the approximate byte size of all obsolete IN entries.
*/
public int getObsoleteINSize() {
if (totalINCount == 0) {
return 0;
}
/* Normalize obsolete amounts to account for double-counting. */
final int obsINCount = Math.min(obsoleteINCount, totalINCount);
/* Use average IN size to compute total. */
final float size = totalINSize;
final float avgSizePerIN = size / totalINCount;
return (int) (obsINCount * avgSizePerIN);
}
/**
* Returns an estimate of the total bytes that are obsolete, using
* getObsoleteLNSize instead of getMaxObsoleteLNSize.
*/
public int getObsoleteSize() {
return calculateObsoleteSize(getObsoleteLNSize());
}
/**
* Returns an estimate of the total bytes that are obsolete, using
* getMaxObsoleteLNSize instead of getObsoleteLNSize.
*/
public int getMaxObsoleteSize() {
return calculateObsoleteSize(getMaxObsoleteLNSize());
}
private int calculateObsoleteSize(int lnObsoleteSize) {
if (totalSize <= 0) {
return 0;
}
/* Leftover (non-IN non-LN) space is considered obsolete. */
final int leftoverSize = totalSize - (totalINSize + totalLNSize);
int obsoleteSize = lnObsoleteSize +
getObsoleteINSize() +
leftoverSize;
/*
* Don't report more obsolete bytes than the total. We may
* calculate more than the total because of (intentional)
* double-counting during recovery.
*/
if (obsoleteSize > totalSize) {
obsoleteSize = totalSize;
}
return obsoleteSize;
}
/**
* Returns the total number of entries counted. This value is guaranteed
* to increase whenever the tracking information about a file changes. It
* is used a key discriminator for FileSummaryLN records.
*/
int getEntriesCounted() {
return totalCount + obsoleteLNCount + obsoleteINCount;
}
/**
* Calculates utilization percentage using average LN sizes.
*/
public int utilization() {
return utilization(getObsoleteSize(), totalSize);
}
/**
* Calculates a utilization percentage.
*/
public static int utilization(long obsoleteSize, long totalSize) {
if (totalSize == 0) {
return 0;
}
return Math.round(((100.0F * (totalSize - obsoleteSize)) / totalSize));
}
/**
* Reset all totals to zero.
*/
public void reset() {
totalCount = 0;
totalSize = 0;
totalINCount = 0;
totalINSize = 0;
totalLNCount = 0;
totalLNSize = 0;
maxLNSize = 0;
obsoleteINCount = 0;
obsoleteLNCount = 0;
obsoleteLNSize = 0;
obsoleteLNSizeCounted = 0;
}
/**
* Add the totals of the given summary object to the totals of this object.
*/
public void add(FileSummary o) {
totalCount += o.totalCount;
totalSize += o.totalSize;
totalINCount += o.totalINCount;
totalINSize += o.totalINSize;
totalLNCount += o.totalLNCount;
totalLNSize += o.totalLNSize;
if (maxLNSize < o.maxLNSize) {
maxLNSize = o.maxLNSize;
}
obsoleteINCount += o.obsoleteINCount;
obsoleteLNCount += o.obsoleteLNCount;
obsoleteLNSize += o.obsoleteLNSize;
obsoleteLNSizeCounted += o.obsoleteLNSizeCounted;
}
public int getLogSize() {
return 11 * LogUtils.getIntLogSize();
}
public void writeToLog(ByteBuffer buf) {
LogUtils.writeInt(buf, totalCount);
LogUtils.writeInt(buf, totalSize);
LogUtils.writeInt(buf, totalINCount);
LogUtils.writeInt(buf, totalINSize);
LogUtils.writeInt(buf, totalLNCount);
LogUtils.writeInt(buf, totalLNSize);
LogUtils.writeInt(buf, maxLNSize);
LogUtils.writeInt(buf, obsoleteINCount);
LogUtils.writeInt(buf, obsoleteLNCount);
LogUtils.writeInt(buf, obsoleteLNSize);
LogUtils.writeInt(buf, obsoleteLNSizeCounted);
}
public void readFromLog(ByteBuffer buf, int entryVersion) {
totalCount = LogUtils.readInt(buf);
totalSize = LogUtils.readInt(buf);
totalINCount = LogUtils.readInt(buf);
totalINSize = LogUtils.readInt(buf);
totalLNCount = LogUtils.readInt(buf);
totalLNSize = LogUtils.readInt(buf);
if (entryVersion >= 8) {
maxLNSize = LogUtils.readInt(buf);
}
obsoleteINCount = LogUtils.readInt(buf);
if (obsoleteINCount == -1) {
/*
* If INs were not counted in an older log file written by 1.5.3 or
* earlier, consider all INs to be obsolete. This causes the file
* to be cleaned, and then IN counting will be accurate.
*/
obsoleteINCount = totalINCount;
}
obsoleteLNCount = LogUtils.readInt(buf);
/*
* obsoleteLNSize and obsoleteLNSizeCounted were added in FileSummaryLN
* version 3.
*/
if (entryVersion >= 3) {
obsoleteLNSize = LogUtils.readInt(buf);
obsoleteLNSizeCounted = LogUtils.readInt(buf);
} else {
obsoleteLNSize = 0;
obsoleteLNSizeCounted = 0;
}
}
public void dumpLog(StringBuilder buf, boolean verbose) {
buf.append("<summary totalCount=\"");
buf.append(totalCount);
buf.append("\" totalSize=\"");
buf.append(totalSize);
buf.append("\" totalINCount=\"");
buf.append(totalINCount);
buf.append("\" totalINSize=\"");
buf.append(totalINSize);
buf.append("\" totalLNCount=\"");
buf.append(totalLNCount);
buf.append("\" totalLNSize=\"");
buf.append(totalLNSize);
buf.append("\" maxLNSize=\"");
buf.append(maxLNSize);
buf.append("\" obsoleteINCount=\"");
buf.append(obsoleteINCount);
buf.append("\" obsoleteLNCount=\"");
buf.append(obsoleteLNCount);
buf.append("\" obsoleteLNSize=\"");
buf.append(obsoleteLNSize);
buf.append("\" obsoleteLNSizeCounted=\"");
buf.append(obsoleteLNSizeCounted);
buf.append("\" getObsoleteSize=\"");
buf.append(getObsoleteSize());
buf.append("\" getObsoleteINSize=\"");
buf.append(getObsoleteINSize());
buf.append("\" getObsoleteLNSize=\"");
buf.append(getObsoleteLNSize());
buf.append("\" getMaxObsoleteSize=\"");
buf.append(getMaxObsoleteSize());
buf.append("\" getMaxObsoleteLNSize=\"");
buf.append(getMaxObsoleteLNSize());
buf.append("\" getAvgObsoleteLNSizeNotCounted=\"");
buf.append(getAvgObsoleteLNSizeNotCounted());
buf.append("\"/>");
}
/**
* Never called.
*/
public long getTransactionId() {
return 0;
}
/**
* Always return false, this item should never be compared.
*/
public boolean logicalEquals(Loggable other) {
return false;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
dumpLog(buf, true);
return buf.toString();
}
}