KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > evictor > Evictor


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Evictor.java,v 1.86 2006/10/30 21:14:17 bostic Exp $
7  */

8
9 package com.sleepycat.je.evictor;
10
11 import java.text.NumberFormat JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.List JavaDoc;
15 import java.util.logging.Level JavaDoc;
16 import java.util.logging.Logger JavaDoc;
17
18 import com.sleepycat.je.DatabaseException;
19 import com.sleepycat.je.EnvironmentStats;
20 import com.sleepycat.je.StatsConfig;
21 import com.sleepycat.je.config.EnvironmentParams;
22 import com.sleepycat.je.dbi.DatabaseImpl;
23 import com.sleepycat.je.dbi.DbConfigManager;
24 import com.sleepycat.je.dbi.DbTree;
25 import com.sleepycat.je.dbi.EnvironmentImpl;
26 import com.sleepycat.je.dbi.INList;
27 import com.sleepycat.je.dbi.MemoryBudget;
28 import com.sleepycat.je.latch.LatchSupport;
29 import com.sleepycat.je.log.LogManager;
30 import com.sleepycat.je.recovery.Checkpointer;
31 import com.sleepycat.je.tree.BIN;
32 import com.sleepycat.je.tree.IN;
33 import com.sleepycat.je.tree.Node;
34 import com.sleepycat.je.tree.SearchResult;
35 import com.sleepycat.je.tree.Tree;
36 import com.sleepycat.je.utilint.DaemonThread;
37 import com.sleepycat.je.utilint.DbLsn;
38 import com.sleepycat.je.utilint.TestHook;
39 import com.sleepycat.je.utilint.Tracer;
40
41 /**
42  * The Evictor looks through the INList for IN's and BIN's that are worthy of
43  * eviction. Once the nodes are selected, it removes all references to them so
44  * that they can be GC'd by the JVM.
45  */

46 public class Evictor extends DaemonThread {
47     public static final String JavaDoc SOURCE_DAEMON = "daemon";
48     public static final String JavaDoc SOURCE_MANUAL = "manual";
49     public static final String JavaDoc SOURCE_CRITICAL = "critical";
50     private static final boolean DEBUG = false;
51
52     private EnvironmentImpl envImpl;
53     private LogManager logManager;
54     private Level JavaDoc detailedTraceLevel; // level value for detailed trace msgs
55
private volatile boolean active; // true if eviction is happening.
56

57     /* Round robin marker in the INList, indicates start of eviction scans. */
58     private IN nextNode;
59
60     /* The number of bytes we need to evict in order to get under budget. */
61     private long currentRequiredEvictBytes;
62
63     /* 1 node out of <nodesPerScan> are chosen for eviction. */
64     private int nodesPerScan;
65
66     /* je.evictor.evictBytes */
67     private long evictBytesSetting;
68
69     /* je.evictor.lruOnly */
70     private boolean evictByLruOnly;
71
72     /* for trace messages. */
73     private NumberFormat JavaDoc formatter;
74
75     /*
76      * Stats
77      */

78
79     /* Number of passes made to the evictor. */
80     private int nEvictPasses = 0;
81
82     /* Number of nodes selected to evict. */
83     private long nNodesSelected = 0;
84     private long nNodesSelectedThisRun;
85
86     /* Number of nodes scanned in order to select the eviction set */
87     private int nNodesScanned = 0;
88     private int nNodesScannedThisRun;
89
90     /*
91      * Number of nodes evicted on this run. This could be understated, as a
92      * whole subtree may have gone out with a single node.
93      */

94     private long nNodesEvicted = 0;
95     private long nNodesEvictedThisRun;
96
97     /* Number of BINs stripped. */
98     private long nBINsStripped = 0;
99     private long nBINsStrippedThisRun;
100
101     /* Debugging and unit test support. */
102     EvictProfile evictProfile;
103     private TestHook runnableHook;
104
105     public Evictor(EnvironmentImpl envImpl, String JavaDoc name)
106         throws DatabaseException {
107
108         super(0, name, envImpl);
109         this.envImpl = envImpl;
110         logManager = envImpl.getLogManager();
111         nextNode = null;
112
113         DbConfigManager configManager = envImpl.getConfigManager();
114         nodesPerScan = configManager.getInt
115             (EnvironmentParams.EVICTOR_NODES_PER_SCAN);
116         evictBytesSetting = configManager.getLong
117             (EnvironmentParams.EVICTOR_EVICT_BYTES);
118         evictByLruOnly = configManager.getBoolean
119             (EnvironmentParams.EVICTOR_LRU_ONLY);
120         detailedTraceLevel = Tracer.parseLevel
121             (envImpl, EnvironmentParams.JE_LOGGING_LEVEL_EVICTOR);
122
123         evictProfile = new EvictProfile();
124         formatter = NumberFormat.getNumberInstance();
125
126         active = false;
127     }
128
129     public String JavaDoc toString() {
130         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
131         sb.append("<Evictor name=\"").append(name).append("\"/>");
132         return sb.toString();
133     }
134
135     /**
136      * Evictor doesn't have a work queue so just throw an exception if it's
137      * ever called.
138      */

139     public void addToQueue(Object JavaDoc o)
140         throws DatabaseException {
141
142         throw new DatabaseException
143             ("Evictor.addToQueue should never be called.");
144     }
145
146     /**
147      * Load stats.
148      */

149     public void loadStats(StatsConfig config, EnvironmentStats stat)
150         throws DatabaseException {
151
152         stat.setNEvictPasses(nEvictPasses);
153         stat.setNNodesSelected(nNodesSelected);
154         stat.setNNodesScanned(nNodesScanned);
155         stat.setNNodesExplicitlyEvicted(nNodesEvicted);
156         stat.setNBINsStripped(nBINsStripped);
157         stat.setRequiredEvictBytes(currentRequiredEvictBytes);
158
159         if (config.getClear()) {
160             nEvictPasses = 0;
161             nNodesSelected = 0;
162             nNodesScanned = 0;
163             nNodesEvicted = 0;
164             nBINsStripped = 0;
165         }
166     }
167
168     synchronized public void clearEnv() {
169         envImpl = null;
170     }
171
172     /**
173      * Return the number of retries when a deadlock exception occurs.
174      */

175     protected int nDeadlockRetries()
176         throws DatabaseException {
177
178         return envImpl.getConfigManager().getInt
179             (EnvironmentParams.EVICTOR_RETRY);
180     }
181
182     /**
183      * Wakeup the evictor only if it's not already active.
184      */

185     public void alert() {
186         if (!active) {
187             wakeup();
188         }
189     }
190
191     /**
192      * Called whenever the daemon thread wakes up from a sleep.
193      */

194     public void onWakeup()
195         throws DatabaseException {
196
197         if (envImpl.isClosed()) {
198             return;
199         }
200
201         doEvict(SOURCE_DAEMON,
202                 false, // criticalEviction
203
true); // backgroundIO
204
}
205
206     /**
207      * May be called by the evictor thread on wakeup or programatically.
208      */

209     public void doEvict(String JavaDoc source)
210         throws DatabaseException {
211
212         doEvict(source,
213                 false, // criticalEviction
214
true); // backgroundIO
215
}
216
217     /**
218      * Allows performing eviction during shutdown, which is needed when
219      * during checkpointing and cleaner log file deletion.
220      */

221     private synchronized void doEvict(String JavaDoc source,
222                                       boolean criticalEviction,
223                                       boolean backgroundIO)
224         throws DatabaseException {
225
226         /*
227          * We use an active flag to prevent reentrant calls. This is simpler
228          * than ensuring that no reentrant eviction can occur in any caller.
229          * We also use the active flag to determine when it is unnecessary to
230          * wake up the evictor thread.
231          */

232         if (active) {
233             return;
234         }
235         active = true;
236         try {
237
238             /*
239              * Repeat as necessary to keep up with allocations. Stop if no
240              * progress is made, to prevent an infinite loop.
241              */

242             boolean progress = true;
243             while (progress &&
244                    (criticalEviction || !isShutdownRequested()) &&
245                    isRunnable(source)) {
246                 if (evictBatch
247                     (source, backgroundIO, currentRequiredEvictBytes) == 0) {
248                     progress = false;
249                 }
250             }
251         } finally {
252             active = false;
253         }
254     }
255
256     /**
257      * Do a check on whether synchronous eviction is needed.
258      */

259     public void doCriticalEviction(boolean backgroundIO)
260         throws DatabaseException {
261
262         MemoryBudget mb = envImpl.getMemoryBudget();
263         long currentUsage = mb.getCacheMemoryUsage();
264         long maxMem = mb.getCacheBudget();
265         long over = currentUsage - maxMem;
266         
267         if (over > mb.getCriticalThreshold()) {
268             if (DEBUG) {
269                 System.out.println("***critical detected:" + over);
270             }
271             doEvict(SOURCE_CRITICAL,
272                     true, // criticalEviction
273
backgroundIO);
274         }
275     }
276
277     /**
278      * Each iteration will latch and unlatch the major INList, and will attempt
279      * to evict requiredEvictBytes, but will give up after a complete pass
280      * over the major INList. Releasing the latch is important because it
281      * provides an opportunity for to add the minor INList to the major INList.
282      *
283      * @return the number of bytes evicted, or zero if no progress was made.
284      */

285     long evictBatch(String JavaDoc source,
286                     boolean backgroundIO,
287                     long requiredEvictBytes)
288         throws DatabaseException {
289
290         nNodesSelectedThisRun = 0;
291         nNodesEvictedThisRun = 0;
292         nNodesScannedThisRun = 0;
293         nBINsStrippedThisRun = 0;
294         nEvictPasses++;
295
296         assert evictProfile.clear(); // intentional side effect
297
int nBatchSets = 0;
298         boolean finished = false;
299         long evictBytes = 0;
300
301         /* Evict utilization tracking info without holding the INList latch. */
302         evictBytes += envImpl.getUtilizationTracker().evictMemory();
303
304         INList inList = envImpl.getInMemoryINs();
305         inList.latchMajor();
306         int inListStartSize = inList.getSize();
307
308         try {
309
310             /*
311              * Setup the round robin iterator. Note that because critical
312              * eviction is now called during recovery, when the INList is
313              * sometimes abruptly cleared, nextNode may not be null when the
314              * INList is empty.
315              */

316             if (inListStartSize == 0) {
317                 nextNode = null;
318                 return 0;
319             } else {
320                 if (nextNode == null) {
321                     nextNode = inList.first();
322                 }
323             }
324
325             ScanIterator scanIter = new ScanIterator(nextNode, inList);
326
327             /*
328              * Keep evicting until we've freed enough memory or we've visited
329              * the maximum number of nodes allowed. Each iteration of the while
330              * loop is called an eviction batch.
331              *
332              * In order to prevent endless evicting and not keep the INList
333              * major latch for too long, limit this run to one pass over the IN
334              * list.
335              */

336             while ((evictBytes < requiredEvictBytes) &&
337                    (nNodesScannedThisRun <= inListStartSize)) {
338
339                 IN target = selectIN(inList, scanIter);
340
341                 if (target == null) {
342                     break;
343                 } else {
344                     assert evictProfile.count(target);//intentional side effect
345
evictBytes += evict
346                         (inList, target, scanIter, backgroundIO);
347                 }
348                 nBatchSets++;
349             }
350
351             /*
352              * At the end of the scan, look at the next element in the INList
353              * and put it in nextNode for the next time we scan the INList.
354              */

355             nextNode = scanIter.mark();
356             finished = true;
357
358         } finally {
359             nNodesScanned += nNodesScannedThisRun;
360             inList.releaseMajorLatch();
361
362             Logger JavaDoc logger = envImpl.getLogger();
363             if (logger.isLoggable(detailedTraceLevel)) {
364                 /* Ugh, only create trace message when logging. */
365                 Tracer.trace(detailedTraceLevel, envImpl,
366                              "Evictor: pass=" + nEvictPasses +
367                              " finished=" + finished +
368                              " source=" + source +
369                              " requiredEvictBytes=" +
370                              formatter.format(requiredEvictBytes) +
371                              " evictBytes=" +
372                              formatter.format(evictBytes) +
373                              " inListSize=" + inListStartSize +
374                              " nNodesScanned=" + nNodesScannedThisRun +
375                              " nNodesSelected=" + nNodesSelectedThisRun +
376                              " nEvicted=" + nNodesEvictedThisRun +
377                              " nBINsStripped=" + nBINsStrippedThisRun +
378                              " nBatchSets=" + nBatchSets);
379             }
380         }
381
382         assert LatchSupport.countLatchesHeld() == 0: "latches held = " +
383             LatchSupport.countLatchesHeld();
384         return evictBytes;
385     }
386
387     /**
388      * Return true if eviction should happen.
389      */

390     boolean isRunnable(String JavaDoc source)
391         throws DatabaseException {
392
393         MemoryBudget mb = envImpl.getMemoryBudget();
394         long currentUsage = mb.getCacheMemoryUsage();
395         long maxMem = mb.getCacheBudget();
396         boolean doRun = ((currentUsage - maxMem) > 0);
397
398         /* If running, figure out how much to evict. */
399         if (doRun) {
400             currentRequiredEvictBytes =
401                 (currentUsage - maxMem) + evictBytesSetting;
402             if (DEBUG) {
403                 if (source == SOURCE_CRITICAL) {
404                     System.out.println("executed: critical runnable");
405                 }
406             }
407         }
408
409         /* unit testing, force eviction */
410         if (runnableHook != null) {
411             doRun = ((Boolean JavaDoc) runnableHook.getHookValue()).booleanValue();
412             currentRequiredEvictBytes = maxMem;
413         }
414
415         /*
416          * This trace message is expensive, only generate if tracing at this
417          * level is enabled.
418          */

419         Logger JavaDoc logger = envImpl.getLogger();
420         if (logger.isLoggable(detailedTraceLevel)) {
421
422             /*
423              * Generate debugging output. Note that Runtime.freeMemory
424              * fluctuates over time as the JVM grabs more memory, so you really
425              * have to do totalMemory - freeMemory to get stack usage. (You
426              * can't get the concept of memory available from free memory.)
427              */

428             Runtime JavaDoc r = Runtime.getRuntime();
429             long totalBytes = r.totalMemory();
430             long freeBytes= r.freeMemory();
431             long usedBytes = r.totalMemory() - r.freeMemory();
432             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
433             sb.append(" source=").append(source);
434             sb.append(" doRun=").append(doRun);
435             sb.append(" JEusedBytes=").append(formatter.format(currentUsage));
436             sb.append(" requiredEvict=").
437                 append(formatter.format(currentRequiredEvictBytes));
438             sb.append(" JVMtotalBytes= ").append(formatter.format(totalBytes));
439             sb.append(" JVMfreeBytes= ").append(formatter.format(freeBytes));
440             sb.append(" JVMusedBytes= ").append(formatter.format(usedBytes));
441             logger.log(detailedTraceLevel, sb.toString());
442         }
443
444         return doRun;
445     }
446
447     /**
448      * Select a single node to evict.
449      */

450     private IN selectIN(INList inList, ScanIterator scanIter)
451         throws DatabaseException {
452
453         /* Find the best target in the next <nodesPerScan> nodes. */
454         IN target = null;
455         long targetGeneration = Long.MAX_VALUE;
456         int targetLevel = Integer.MAX_VALUE;
457         boolean targetDirty = true;
458     boolean envIsReadOnly = envImpl.isReadOnly();
459         int scanned = 0;
460         boolean wrapped = false;
461         while (scanned < nodesPerScan) {
462             if (scanIter.hasNext()) {
463                 IN in = scanIter.next();
464                 nNodesScannedThisRun++;
465
466                 DatabaseImpl db = in.getDatabase();
467
468                 /*
469                  * We don't expect to see an IN with a database that has
470                  * finished delete processing, because it would have been
471                  * removed from the inlist during post-delete cleanup.
472                  */

473                 if (db == null || db.isDeleteFinished()) {
474                     String JavaDoc inInfo = " IN type=" + in.getLogType() + " id=" +
475                         in.getNodeId() + " not expected on INList";
476                     String JavaDoc errMsg = (db == null) ? inInfo :
477                         "Database " + db.getDebugName() + " id=" + db.getId() +
478                         inInfo;
479                     throw new DatabaseException(errMsg);
480                 }
481
482                 /* Ignore if the db is in the middle of delete processing. */
483                 if (db.isDeleted()) {
484                     continue;
485                 }
486
487                 /*
488                  * Don't evict the DatabaseImpl Id Mapping Tree (db 0), both
489                  * for object identity reasons and because the id mapping tree
490                  * should stay cached.
491                  */

492                 if (db.getId().equals(DbTree.ID_DB_ID)) {
493                     continue;
494                 }
495
496                 /*
497                  * If this is a read only database and we have at least one
498                  * target, skip any dirty INs (recovery dirties INs even in a
499                  * read-only environment). We take at least one target so we
500                  * don't loop endlessly if everything is dirty.
501                  */

502                 if (envIsReadOnly && (target != null) && in.getDirty()) {
503                     continue;
504                 }
505
506                 /*
507                  * Only scan evictable or strippable INs. This prevents higher
508                  * level INs from being selected for eviction, unless they are
509                  * part of an unused tree.
510                  */

511                 int evictType = in.getEvictionType();
512                 if (evictType == IN.MAY_NOT_EVICT) {
513                     continue;
514                 }
515
516                 /*
517                  * This node is in the scanned node set. Select according to
518                  * the configured eviction policy.
519                  */

520                 if (evictByLruOnly) {
521
522                     /*
523                      * Select the node with the lowest generation number,
524                      * irrespective of tree level or dirtyness.
525                      */

526                     if (targetGeneration > in.getGeneration()) {
527                         targetGeneration = in.getGeneration();
528                         target = in;
529                     }
530                 } else {
531
532                     /*
533                      * Select first by tree level, then by dirtyness, then by
534                      * generation/LRU.
535                      */

536                     int level = normalizeLevel(in, evictType);
537                     if (targetLevel != level) {
538                         if (targetLevel > level) {
539                             targetLevel = level;
540                             targetDirty = in.getDirty();
541                             targetGeneration = in.getGeneration();
542                             target = in;
543                         }
544                     } else if (targetDirty != in.getDirty()) {
545                         if (targetDirty) {
546                             targetDirty = false;
547                             targetGeneration = in.getGeneration();
548                             target = in;
549                         }
550                     } else {
551                         if (targetGeneration > in.getGeneration()) {
552                             targetGeneration = in.getGeneration();
553                             target = in;
554                         }
555                     }
556                 }
557                 scanned++;
558             } else {
559                 /* We wrapped around in the list. */
560                 if (wrapped) {
561                     break;
562                 } else {
563                     nextNode = inList.first();
564                     scanIter.reset(nextNode);
565                     wrapped = true;
566                 }
567             }
568         }
569
570         if (target != null) {
571             nNodesSelectedThisRun++;
572             nNodesSelected++;
573         }
574         return target;
575     }
576
577     /**
578      * Normalize the tree level of the given IN.
579      *
580      * Is public for unit testing.
581      *
582      * A BIN containing evictable LNs is given level 0, so it will be stripped
583      * first. For non-duplicate and DBMAP trees, the high order bits are
584      * cleared to make their levels correspond; that way, all bottom level
585      * nodes (BINs and DBINs) are given the same eviction priority.
586      *
587      * Note that BINs in a duplicate tree are assigned the same level as BINs
588      * in a non-duplicate tree. This isn't always optimimal, but is the best
589      * we can do considering that BINs in duplicate trees may contain a mix of
590      * LNs and DINs.
591      */

592     public int normalizeLevel(IN in, int evictType) {
593
594         int level = in.getLevel() & IN.LEVEL_MASK;
595
596         if (level == 1 && evictType == IN.MAY_EVICT_LNS) {
597             level = 0;
598         }
599
600         return level;
601     }
602
603     /**
604      * Strip or evict this node.
605      * @return number of bytes evicted.
606      */

607     private long evict(INList inList,
608                        IN target,
609                        ScanIterator scanIter,
610                        boolean backgroundIO)
611         throws DatabaseException {
612         
613     boolean envIsReadOnly = envImpl.isReadOnly();
614         long evictedBytes = 0;
615
616         /*
617          * Non-BIN INs are evicted by detaching them from their parent. For
618          * BINS, the first step is to remove deleted entries by compressing
619          * the BIN. The evictor indicates that we shouldn't fault in
620          * non-resident children during compression. After compression,
621          * LN logging and LN stripping may be performed.
622          *
623          * If LN stripping is used, first we strip the BIN by logging any dirty
624          * LN children and detaching all its resident LN targets. If we make
625          * progress doing that, we stop and will not evict the BIN itself until
626          * possibly later. If it has no resident LNs then we evict the BIN
627          * itself using the "regular" detach-from-parent routine.
628          *
629          * If the cleaner is doing clustering, we don't do BIN stripping if we
630          * can write out the BIN. Specifically LN stripping is not performed
631          * if the BIN is dirty AND the BIN is evictable AND cleaner
632          * clustering is enabled. In this case the BIN is going to be written
633          * out soon, and with clustering we want to be sure to write out the
634          * LNs with the BIN; therefore we don't do stripping
635          */

636
637         /*
638          * Use latchNoWait because if it's latched we don't want the cleaner
639          * to hold up eviction while it migrates an entire BIN. Latched INs
640          * have a high generation value, so not evicting makes sense. Pass
641          * false because we don't want to change the generation during the
642          * eviction process.
643          */

644         if (target.latchNoWait(false)) {
645         boolean targetIsLatched = true;
646             try {
647                 if (target instanceof BIN) {
648                     /* first attempt to compress deleted, resident children.*/
649                     envImpl.lazyCompress(target);
650
651                     /*
652                      * Strip any resident LN targets right now. This may dirty
653                      * the BIN if dirty LNs were written out. Note that
654                      * migrated BIN entries cannot be stripped.
655                      */

656                     evictedBytes = ((BIN) target).evictLNs();
657                     if (evictedBytes > 0) {
658                         nBINsStrippedThisRun++;
659                         nBINsStripped++;
660                     }
661                 }
662
663                 /*
664                  * If we were able to free any memory by LN stripping above,
665                  * then we postpone eviction of the BIN until a later pass.
666                  * The presence of migrated entries would have inhibited LN
667                  * stripping. In that case, the BIN can still be evicted,
668                  * but the marked entries will have to be migrated. That would
669                  * happen when the target is logged in evictIN.
670                  */

671                 if (evictedBytes == 0 && target.isEvictable()) {
672                     /* Regular eviction. */
673                     Tree tree = target.getDatabase().getTree();
674
675                     /* getParentINForChildIN unlatches target. */
676             targetIsLatched = false;
677                     SearchResult result =
678                         tree.getParentINForChildIN
679                         (target,
680                          true, // requireExactMatch
681
false); // updateGeneration
682
if (result.exactParentFound) {
683                         evictedBytes = evictIN(target, result.parent,
684                                                 result.index,
685                                                 inList, scanIter,
686                                                 envIsReadOnly,
687                                                 backgroundIO);
688                     }
689                 }
690             } finally {
691         if (targetIsLatched) {
692             target.releaseLatchIfOwner();
693         }
694             }
695         }
696             
697         return evictedBytes;
698     }
699
700     /**
701      * Evict an IN. Dirty nodes are logged before they're evicted. inlist is
702      * latched with the major latch by the caller.
703      */

704     private long evictIN(IN child,
705                          IN parent,
706                          int index,
707                          INList inlist,
708                          ScanIterator scanIter,
709              boolean envIsReadOnly,
710                          boolean backgroundIO)
711         throws DatabaseException {
712
713         long evictBytes = 0;
714         try {
715             assert parent.isLatchOwnerForWrite();
716
717             long oldGenerationCount = child.getGeneration();
718             
719             /*
720              * Get a new reference to the child, in case the reference
721              * saved in the selection list became out of date because of
722              * changes to that parent.
723              */

724             IN renewedChild = (IN) parent.getTarget(index);
725             
726             /*
727              * See the evict() method in this class for an explanation for
728              * calling latchNoWait(false).
729              */

730             if ((renewedChild != null) &&
731                 (renewedChild.getGeneration() <= oldGenerationCount) &&
732                 renewedChild.latchNoWait(false)) {
733
734                 try {
735                     if (renewedChild.isEvictable()) {
736
737                         /*
738                          * Log the child if dirty and env is not r/o. Remove
739                          * from IN list.
740                          */

741                         long renewedChildLsn = DbLsn.NULL_LSN;
742                         boolean newChildLsn = false;
743                         if (renewedChild.getDirty()) {
744                             if (!envIsReadOnly) {
745                 boolean logProvisional =
746                                     isProvisionalRequired(renewedChild);
747
748                                 /*
749                                  * Log a full version (no deltas) and with
750                                  * cleaner migration allowed.
751                                  */

752                                 renewedChildLsn = renewedChild.log
753                                     (logManager,
754                                      false, // allowDeltas
755
logProvisional,
756                                      true, // proactiveMigration
757
backgroundIO,
758                                      parent);
759                                 newChildLsn = true;
760                             }
761                         } else {
762                             renewedChildLsn = parent.getLsn(index);
763                         }
764
765                         if (renewedChildLsn != DbLsn.NULL_LSN) {
766                             /* Take this off the inlist. */
767                             scanIter.mark();
768                             inlist.removeLatchAlreadyHeld(renewedChild);
769                             scanIter.resetToMark();
770
771                             evictBytes = renewedChild.getInMemorySize();
772                             if (newChildLsn) {
773
774                                 /*
775                                  * Update the parent so its reference is
776                                  * null and it has the proper LSN.
777                                  */

778                                 parent.updateEntry
779                                     (index, null, renewedChildLsn);
780                             } else {
781
782                                 /*
783                                  * Null out the reference, but don't dirty
784                                  * the node since only the reference
785                                  * changed.
786                                  */

787                                 parent.updateEntry(index, (Node) null);
788                             }
789
790                             /* Stats */
791                             nNodesEvictedThisRun++;
792                             nNodesEvicted++;
793                         }
794                     }
795                 } finally {
796                     renewedChild.releaseLatch();
797                 }
798             }
799         } finally {
800             parent.releaseLatch();
801         }
802
803         return evictBytes;
804     }
805
806     /*
807      * @return true if the node must be logged provisionally.
808      */

809     private boolean isProvisionalRequired(IN target) {
810
811         /*
812          * The evictor has to log provisionally in two cases:
813          * a - the checkpointer is in progress, and is at a level above the
814          * target eviction victim. We don't want the evictor's actions to
815          * introduce an IN that has not cascaded up properly.
816          * b - the eviction target is part of a deferred write database.
817          */

818         if (target.getDatabase().isDeferredWrite()) {
819             return true;
820         }
821
822         /*
823          * The checkpointer could be null if it was shutdown or never
824          * started.
825          */

826         Checkpointer ckpter = envImpl.getCheckpointer();
827         if ((ckpter != null) &&
828             (target.getLevel() < ckpter.getHighestFlushLevel())) {
829             return true;
830         }
831
832         return false;
833     }
834
835     /**
836      * Used by unit tests.
837      */

838     IN getNextNode() {
839         return nextNode;
840     }
841
842     /* For unit testing only. */
843     public void setRunnableHook(TestHook hook) {
844         runnableHook = hook;
845     }
846
847     /* For debugging and unit tests. */
848     static public class EvictProfile {
849         /* Keep a list of candidate nodes. */
850         private List JavaDoc candidates = new ArrayList JavaDoc();
851
852         /* Remember that this node was targetted. */
853         public boolean count(IN target) {
854             candidates.add(new Long JavaDoc(target.getNodeId()));
855             return true;
856         }
857
858         public List JavaDoc getCandidates() {
859             return candidates;
860         }
861
862         public boolean clear() {
863             candidates.clear();
864             return true;
865         }
866     }
867
868     /*
869      * ScanIterator keeps a handle onto the current round robin INList
870      * iterator. It's deliberately not a member of the class in order to keep
871      * less common state in the class.
872      */

873     private static class ScanIterator {
874         private INList inList;
875         private Iterator JavaDoc iter;
876         private IN nextMark;
877
878         ScanIterator(IN startingIN, INList inList)
879             throws DatabaseException {
880
881             this.inList = inList;
882             reset(startingIN);
883         }
884
885         void reset(IN startingIN)
886             throws DatabaseException {
887
888             iter = inList.tailSet(startingIN).iterator();
889         }
890
891         IN mark()
892             throws DatabaseException {
893
894             if (iter.hasNext()) {
895                 nextMark = (IN) iter.next();
896             } else {
897                 nextMark = (IN) inList.first();
898             }
899             return (IN) nextMark;
900         }
901
902         void resetToMark()
903             throws DatabaseException {
904
905             reset(nextMark);
906         }
907
908         boolean hasNext() {
909             return iter.hasNext();
910         }
911
912         IN next() {
913             return (IN) iter.next();
914         }
915
916         void remove() {
917             iter.remove();
918         }
919     }
920 }
921
Popular Tags