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