KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > cleaner > UtilizationTracker


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: UtilizationTracker.java,v 1.19 2006/11/03 03:07:48 mark Exp $
7  */

8
9 package com.sleepycat.je.cleaner;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.List JavaDoc;
13
14 import com.sleepycat.je.DatabaseException;
15 import com.sleepycat.je.dbi.EnvironmentImpl;
16 import com.sleepycat.je.dbi.MemoryBudget;
17 import com.sleepycat.je.log.LogEntryType;
18 import com.sleepycat.je.utilint.DbLsn;
19
20 /**
21  * Tracks changes to the utilization profile since the last checkpoint.
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 {
31
32     private EnvironmentImpl env;
33     private Cleaner cleaner;
34     private List JavaDoc files;
35     private long activeFile;
36     private TrackedFileSummary[] snapshot;
37     private long bytesSinceActivate;
38
39     /**
40      * Creates an empty tracker. The cleaner field of the environment object
41      * must be initialized before using this constructor.
42      */

43     public UtilizationTracker(EnvironmentImpl env)
44         throws DatabaseException {
45
46         this(env, env.getCleaner());
47     }
48
49     /**
50      * Constructor used by the cleaner constructor, prior to setting the
51      * cleaner field of the environment.
52      */

53     UtilizationTracker(EnvironmentImpl env, Cleaner cleaner)
54         throws DatabaseException {
55
56         assert cleaner != null;
57         this.env = env;
58         this.cleaner = cleaner;
59         files = new ArrayList JavaDoc();
60         snapshot = new TrackedFileSummary[0];
61         activeFile = -1;
62     }
63
64     public EnvironmentImpl getEnvironment() {
65         return env;
66     }
67
68     /**
69      * Evicts tracked detail if the budget for the tracker is exceeded. Evicts
70      * only one file summary LN at most to keep eviction batches small.
71      * Returns the number of bytes freed.
72      *
73      * <p>When flushFileSummary is called, the TrackedFileSummary is cleared via
74      * its reset method, which is called by FileSummaryLN.writeToLog. This is
75      * how memory is subtracted from the budget.</p>
76      */

77     public long evictMemory()
78         throws DatabaseException {
79
80         /* If not tracking detail, there is nothing to evict. */
81         if (!cleaner.trackDetail) {
82             return 0;
83         }
84
85         /*
86          * Do not start eviction until after recovery, since the
87          * UtilizationProfile will not be initialized properly. UP
88          * initialization requires that all LNs have been replayed.
89          */

90         if (!env.isOpen()) {
91             return 0;
92         }
93
94         MemoryBudget mb = env.getMemoryBudget();
95         long totalEvicted = 0;
96         long totalBytes = 0;
97         int largestBytes = 0;
98         TrackedFileSummary bestFile = null;
99
100         /*
101          * Use a local variable to access the array since the snapshot
102          * field can be changed by other threads.
103          */

104         TrackedFileSummary[] a = snapshot;
105         for (int i = 0; i < a.length; i += 1) {
106
107             TrackedFileSummary tfs = a[i];
108             int mem = tfs.getMemorySize();
109             totalBytes += mem;
110
111             if (mem > largestBytes && tfs.getAllowFlush()) {
112                 largestBytes = mem;
113                 bestFile = tfs;
114             }
115         }
116
117         if (bestFile != null && totalBytes > mb.getTrackerBudget()) {
118             env.getUtilizationProfile().flushFileSummary(bestFile);
119             totalEvicted += largestBytes;
120         }
121         return totalEvicted;
122     }
123
124     /**
125      * Wakeup the cleaner thread and reset the log byte counter.
126      */

127     public void activateCleaner() {
128         env.getCleaner().wakeup();
129         bytesSinceActivate = 0;
130     }
131
132     /**
133      * Returns a snapshot of the files being tracked as of the last time a
134      * log entry was added. The summary info returned is the delta since the
135      * last checkpoint, not the grand totals, and is approximate since it is
136      * changing in real time. This method may be called without holding the
137      * log write latch.
138      *
139      * <p>If files are added or removed from the list of tracked files in real
140      * time, the returned array will not be changed since it is a snapshot.
141      * But the objects contained in the array are live and will be updated in
142      * real time under the log write latch. The array and the objects in the
143      * array should not be modified by the caller.</p>
144      */

145     public TrackedFileSummary[] getTrackedFiles() {
146         return snapshot;
147     }
148
149     /**
150      * Returns one file from the snapshot of tracked files, or null if the
151      * given file number is not in the snapshot array.
152      * @see #getTrackedFiles
153      */

154     public TrackedFileSummary getTrackedFile(long fileNum) {
155
156         /*
157          * Use a local variable to access the array since the snapshot field
158          * can be changed by other threads.
159          */

160         TrackedFileSummary[] a = snapshot;
161         for (int i = 0; i < a.length; i += 1) {
162             if (a[i].getFileNumber() == fileNum) {
163                 return a[i];
164             }
165         }
166         return null;
167     }
168
169     /**
170      * Counts the addition of all new log entries including LNs, and returns
171      * whether the cleaner should be woken.
172      *
173      * <p>Must be called under the log write latch.</p>
174      */

175     public boolean countNewLogEntry(long lsn, LogEntryType type, int size) {
176
177         TrackedFileSummary file = getFile(DbLsn.getFileNumber(lsn));
178         file.totalCount += 1;
179         file.totalSize += size;
180         if (type.isNodeType()) {
181             if (inArray(type, LogEntryType.IN_TYPES)) {
182                 file.totalINCount += 1;
183                 file.totalINSize += size;
184             } else {
185                 file.totalLNCount += 1;
186                 file.totalLNSize += size;
187             }
188         }
189         bytesSinceActivate += size;
190         return (bytesSinceActivate >= env.getCleaner().cleanerBytesInterval);
191     }
192
193     /**
194      * Counts a node that has become obsolete and tracks the LSN offset to
195      * avoid a lookup during cleaning.
196      *
197      * <p>This method should only be called for LNs and INs (i.e, only for
198      * nodes). If type is null we assume it is an LN.</p>
199      *
200      * <p>Must be called under the log write latch.</p>
201      */

202     public void countObsoleteNode(long lsn, LogEntryType type, int size) {
203
204         TrackedFileSummary file = getFile(DbLsn.getFileNumber(lsn));
205
206         countOneNode(file, type, size);
207
208         file.trackObsolete(DbLsn.getFileOffset(lsn));
209     }
210
211     /**
212      * Counts as countObsoleteNode does, but since the LSN may be inexact, does
213      * not track the obsolete LSN offset.
214      *
215      * <p>This method should only be called for LNs and INs (i.e, only for
216      * nodes). If type is null we assume it is an LN.</p>
217      *
218      * <p>Must be called under the log write latch.</p>
219      */

220     public void countObsoleteNodeInexact(long lsn,
221                                          LogEntryType type,
222                                          int size) {
223
224         TrackedFileSummary file = getFile(DbLsn.getFileNumber(lsn));
225
226         countOneNode(file, type, size);
227     }
228
229     /**
230      * Counts an obsolete node by incrementing the obsolete count and size.
231      */

232     private void countOneNode(TrackedFileSummary file,
233                               LogEntryType type,
234                               int size) {
235
236         if (type == null || type.isNodeType()) {
237             if (type == null || !inArray(type, LogEntryType.IN_TYPES)) {
238                 file.obsoleteLNCount += 1;
239                 /* The size is optional when tracking obsolete LNs. */
240                 if (size > 0) {
241                     file.obsoleteLNSize += size;
242                     file.obsoleteLNSizeCounted += 1;
243                 }
244             } else {
245                 file.obsoleteINCount += 1;
246                 /* The size is not allowed when tracking obsolete INs. */
247                 assert size == 0;
248             }
249         }
250     }
251
252     /**
253      * Adds changes from a given TrackedFileSummary.
254      *
255      * <p>Must be called under the log write latch.</p>
256      */

257     public void addSummary(long fileNumber, TrackedFileSummary other) {
258
259         TrackedFileSummary file = getFile(fileNumber);
260         file.addTrackedSummary(other);
261     }
262
263     /**
264      * Returns a tracked summary for the given file which will not be flushed.
265      * Used for watching changes that occur while a file is being cleaned.
266      */

267     public TrackedFileSummary getUnflushableTrackedSummary(long fileNum)
268         throws DatabaseException {
269
270         TrackedFileSummary file = getFile(fileNum);
271         file.setAllowFlush(false);
272         return file;
273     }
274         
275     /**
276      * Returns a tracked file for the given file number, adding an empty one
277      * if the file is not already being tracked.
278      *
279      * <p>Must be called under the log write latch.</p>
280      */

281     private TrackedFileSummary getFile(long fileNum) {
282
283         if (activeFile < fileNum) {
284             activeFile = fileNum;
285         }
286         int size = files.size();
287         for (int i = 0; i < size; i += 1) {
288             TrackedFileSummary file = (TrackedFileSummary) files.get(i);
289             if (file.getFileNumber() == fileNum) {
290                 return file;
291             }
292         }
293
294         /*
295          * Create a new tracking object and take a snapshot of the updated file
296          * list.
297          */

298         TrackedFileSummary file = new TrackedFileSummary
299             (this, fileNum, cleaner.trackDetail);
300
301     files.add(file);
302         takeSnapshot();
303         return file;
304     }
305
306     /**
307      * Called after the FileSummaryLN is written to the log during checkpoint.
308      *
309      * <p>We keep the active file summary in the tracked file list, but we
310      * remove older files to prevent unbounded growth of the list.</p>
311      *
312      * <p>Must be called under the log write latch.</p>
313      */

314     void resetFile(TrackedFileSummary file) {
315
316         if (file.getFileNumber() < activeFile && file.getAllowFlush()) {
317             files.remove(file);
318             takeSnapshot();
319         }
320     }
321
322     /**
323      * Takes a snapshot of the tracked file list.
324      *
325      * <p>Must be called under the log write latch.</p>
326      */

327     private void takeSnapshot() {
328         /*
329          * Only assign to the snapshot field with a populated array, since it
330          * will be accessed by other threads.
331          */

332         TrackedFileSummary[] a = new TrackedFileSummary[files.size()];
333         files.toArray(a);
334         snapshot = a;
335     }
336
337     /**
338      * Returns whether an object reference is in an array.
339      */

340     private boolean inArray(Object JavaDoc o, Object JavaDoc[] a) {
341
342         for (int i = 0; i < a.length; i += 1) {
343             if (a[i] == o) {
344                 return true;
345             }
346         }
347         return false;
348     }
349 }
350
Popular Tags