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.cleaner; 9 10 import com.sleepycat.je.DatabaseException; 11 import com.sleepycat.je.dbi.DatabaseImpl; 12 import com.sleepycat.je.dbi.EnvironmentImpl; 13 import com.sleepycat.je.dbi.MemoryBudget; 14 import com.sleepycat.je.log.LogEntryType; 15 16 /** 17 * Tracks changes to the utilization profile since the last checkpoint. This 18 * is the "global" tracker for an environment that tracks changes as they 19 * occur in live operations. Other "local" tracker classes are used to count 20 * utilization locally and then later transfer the information to the global 21 * tracker, this tracker. 22 * 23 * <p>All changes to this object occur must under the log write latch. It is 24 * possible to read tracked info without holding the latch. This is done by 25 * the cleaner when selecting a file and by the checkpointer when determining 26 * what FileSummaryLNs need to be written. To read tracked info outside the 27 * log write latch, call getTrackedFile or getTrackedFiles. activateCleaner 28 * can also be called outside the latch.</p> 29 */ 30 public class UtilizationTracker extends BaseUtilizationTracker { 31 32 /** 33 * Creates an empty tracker. The cleaner field of the environment object 34 * must be initialized before using this constructor. 35 */ UtilizationTracker(EnvironmentImpl env)36 public UtilizationTracker(EnvironmentImpl env) { 37 super(env, env.getCleaner()); 38 } 39 40 /** 41 * Constructor used by the cleaner constructor, prior to setting the 42 * cleaner field of the environment. 43 */ UtilizationTracker(EnvironmentImpl env, Cleaner cleaner)44 UtilizationTracker(EnvironmentImpl env, Cleaner cleaner) { 45 super(env, cleaner); 46 } 47 48 @Override getEnvironment()49 public EnvironmentImpl getEnvironment() { 50 return env; 51 } 52 53 /** 54 * Evicts tracked detail if the budget for the tracker is exceeded. Evicts 55 * only one file summary LN at most to keep eviction batches small. 56 * Returns the number of bytes freed. 57 * 58 * <p>When flushFileSummary is called, the TrackedFileSummary is cleared 59 * via its reset method, which is called by FileSummaryLN.writeToLog. This 60 * is how memory is subtracted from the budget.</p> 61 */ evictMemory()62 public long evictMemory() 63 throws DatabaseException { 64 65 /* If not tracking detail, there is nothing to evict. */ 66 if (!cleaner.trackDetail) { 67 return 0; 68 } 69 70 /* 71 * Do not start eviction until after recovery, since the 72 * UtilizationProfile will not be initialized properly. UP 73 * initialization requires that all LNs have been replayed. 74 */ 75 if (!env.isValid()) { 76 return 0; 77 } 78 79 /* 80 * In a read-only env, we cannot free memory by flushing a 81 * FileSummaryLN. Normally utilization information is not accumulated 82 * in a read-only env, but this may ocur during recovery. 83 */ 84 if (env.isReadOnly()) { 85 return 0; 86 } 87 88 MemoryBudget mb = env.getMemoryBudget(); 89 long totalEvicted = 0; 90 long totalBytes = 0; 91 int largestBytes = 0; 92 TrackedFileSummary bestFile = null; 93 final int ONE_MB = 1024 * 1024; 94 95 for (TrackedFileSummary tfs : getTrackedFiles()) { 96 int mem = tfs.getMemorySize(); 97 if (mem >= ONE_MB) { 98 env.getUtilizationProfile().flushFileSummary(tfs); 99 totalEvicted += mem; 100 continue; 101 } 102 totalBytes += mem; 103 if (mem > largestBytes && tfs.getAllowFlush()) { 104 largestBytes = mem; 105 bestFile = tfs; 106 } 107 } 108 109 if (bestFile != null && totalBytes > mb.getTrackerBudget()) { 110 env.getUtilizationProfile().flushFileSummary(bestFile); 111 totalEvicted += largestBytes; 112 } 113 return totalEvicted; 114 } 115 116 /** 117 * Wakeup the cleaner thread and reset the log byte counter. 118 */ activateCleaner()119 public void activateCleaner() { 120 env.getCleaner().wakeup(); 121 bytesSinceActivate = 0; 122 } 123 124 /** 125 * Counts the addition of all new log entries including LNs, and returns 126 * whether the cleaner should be woken. 127 * 128 * <p>Must be called under the log write latch.</p> 129 */ countNewLogEntry(long lsn, LogEntryType type, int size, DatabaseImpl db)130 public boolean countNewLogEntry(long lsn, 131 LogEntryType type, 132 int size, 133 DatabaseImpl db) { 134 return countNew(lsn, db, type, size); 135 } 136 137 /** 138 * Counts a node that has become obsolete and tracks the LSN offset, if 139 * non-zero, to avoid a lookup during cleaning. 140 * 141 * <p>A zero LSN offset is used as a special value when obsolete offset 142 * tracking is not desired. [#15365] The file header entry (at offset 143 * zero) is never counted as obsolete, it is assumed to be obsolete by the 144 * cleaner.</p> 145 * 146 * <p>This method should only be called for LNs and INs (i.e, only for 147 * nodes). If type is null we assume it is an LN.</p> 148 * 149 * <p>Must be called under the log write latch.</p> 150 */ countObsoleteNode(long lsn, LogEntryType type, int size, DatabaseImpl db)151 public void countObsoleteNode(long lsn, 152 LogEntryType type, 153 int size, 154 DatabaseImpl db) { 155 countObsolete 156 (lsn, db, type, size, 157 true, // countPerFile 158 true, // countPerDb 159 true, // trackOffset 160 true); // checkDupOffsets 161 } 162 163 /** 164 * Counts as countObsoleteNode does, but since the LSN may be inexact, does 165 * not track the obsolete LSN offset. 166 * 167 * <p>This method should only be called for LNs and INs (i.e, only for 168 * nodes). If type is null we assume it is an LN.</p> 169 * 170 * <p>Must be called under the log write latch.</p> 171 */ countObsoleteNodeInexact(long lsn, LogEntryType type, int size, DatabaseImpl db)172 public void countObsoleteNodeInexact(long lsn, 173 LogEntryType type, 174 int size, 175 DatabaseImpl db) { 176 countObsolete 177 (lsn, db, type, size, 178 true, // countPerFile 179 true, // countPerDb 180 false, // trackOffset 181 false); // checkDupOffsets 182 } 183 184 /** 185 * Counts as countObsoleteNode does, tracks the obsolete LSN offset, but 186 * does not fire an assert if the offset has already been counted. Use 187 * this method when the same LSN offset may be counted twice in certain 188 * circumstances. 189 * 190 * <p>This method should only be called for LNs and INs (i.e, only for 191 * nodes). If type is null we assume it is an LN.</p> 192 * 193 * <p>Must be called under the log write latch.</p> 194 */ countObsoleteNodeDupsAllowed(long lsn, LogEntryType type, int size, DatabaseImpl db)195 public void countObsoleteNodeDupsAllowed(long lsn, 196 LogEntryType type, 197 int size, 198 DatabaseImpl db) { 199 countObsolete 200 (lsn, db, type, size, 201 true, // countPerFile 202 true, // countPerDb 203 true, // trackOffset 204 false); // checkDupOffsets 205 } 206 207 /** 208 * Returns a tracked summary for the given file which will not be flushed. 209 */ getUnflushableTrackedSummary(long fileNum)210 public TrackedFileSummary getUnflushableTrackedSummary(long fileNum) { 211 TrackedFileSummary file = getFileSummary(fileNum); 212 file.setAllowFlush(false); 213 return file; 214 } 215 216 /** 217 * Allocates DbFileSummary information in the DatabaseImpl, which is the 218 * database key. 219 * 220 * <p>Must be called under the log write latch, and the returned object 221 * may only be accessed under the log write latch.</p> 222 * 223 * @return the summary, or null if the DB should not be tracked because 224 * the file has been deleted, or null if the databaseKey param is null. 225 */ getDbFileSummary(Object databaseKey, long fileNum)226 DbFileSummary getDbFileSummary(Object databaseKey, long fileNum) { 227 DatabaseImpl db = (DatabaseImpl) databaseKey; 228 if (db != null) { 229 return db.getDbFileSummary 230 (Long.valueOf(fileNum), true /*willModify*/); 231 } else { 232 return null; 233 } 234 } 235 } 236