KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Cleaner.java,v 1.183 2006/11/17 23:47:21 mark Exp $
7  */

8
9 package com.sleepycat.je.cleaner;
10
11 import java.io.IOException JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.util.Collections JavaDoc;
14 import java.util.Comparator JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Set JavaDoc;
17 import java.util.logging.Level JavaDoc;
18 import java.util.logging.Logger JavaDoc;
19
20 import com.sleepycat.je.DatabaseException;
21 import com.sleepycat.je.EnvironmentStats;
22 import com.sleepycat.je.StatsConfig;
23 import com.sleepycat.je.cleaner.FileSelector.CheckpointStartCleanerState;
24 import com.sleepycat.je.config.EnvironmentParams;
25 import com.sleepycat.je.dbi.DatabaseId;
26 import com.sleepycat.je.dbi.DatabaseImpl;
27 import com.sleepycat.je.dbi.DbConfigManager;
28 import com.sleepycat.je.dbi.DbTree;
29 import com.sleepycat.je.dbi.EnvConfigObserver;
30 import com.sleepycat.je.dbi.EnvironmentImpl;
31 import com.sleepycat.je.log.FileManager;
32 import com.sleepycat.je.tree.BIN;
33 import com.sleepycat.je.tree.ChildReference;
34 import com.sleepycat.je.tree.DIN;
35 import com.sleepycat.je.tree.LN;
36 import com.sleepycat.je.tree.Node;
37 import com.sleepycat.je.tree.Tree;
38 import com.sleepycat.je.tree.TreeLocation;
39 import com.sleepycat.je.txn.BasicLocker;
40 import com.sleepycat.je.txn.LockGrantType;
41 import com.sleepycat.je.txn.LockResult;
42 import com.sleepycat.je.txn.LockType;
43 import com.sleepycat.je.utilint.DaemonRunner;
44 import com.sleepycat.je.utilint.DbLsn;
45 import com.sleepycat.je.utilint.PropUtil;
46 import com.sleepycat.je.utilint.Tracer;
47
48 /**
49  * The Cleaner is responsible for effectively garbage collecting the JE log.
50  * It looks through log files and locates log records (IN's and LN's of all
51  * flavors) that are superceded by later versions. Those that are "current"
52  * are propagated to a newer log file so that older log files can be deleted.
53  */

54 public class Cleaner implements DaemonRunner, EnvConfigObserver {
55     /* From cleaner */
56     static final String JavaDoc CLEAN_IN = "CleanIN:";
57     static final String JavaDoc CLEAN_LN = "CleanLN:";
58     static final String JavaDoc CLEAN_MIGRATE_LN = "CleanMigrateLN:";
59     static final String JavaDoc CLEAN_PENDING_LN = "CleanPendingLN:";
60
61     /**
62      * Whether to fetch LNs for files in the to-be-cleaned set during lazy
63      * migration. This is currently enabled because we do not support the
64      * dynamic addition of cleaner threads; that way, if the configured cleaner
65      * threads cannot keep up, we use proactive migration to keep up.
66      */

67     static final boolean PROACTIVE_MIGRATION = true;
68
69     /**
70      * Whether to update the IN generation count during searches. This is
71      * currently disabled because 1) we update the generation of the BIN when
72      * we set a MIGRATE flag and 2) if the BIN is not evicted its parents will
73      * not be, so not updating the generation during the search has no benefit.
74      * By not updating the generation during searches for which we do NOT set
75      * the MIGRATE flag, we avoid holding INs in the cache that are not needed
76      * for lazy migration. However, we do very few searches for obsolete LNs
77      * because the obsolete tracking info prevents this, so the benefit of not
78      * updating the generation during searches is questionable. In other
79      * words, changing this setting will have little effect.
80      */

81     static final boolean UPDATE_GENERATION = false;
82
83     /**
84      * Whether the cleaner should participate in critical eviction. Ideally
85      * the cleaner would not participate in eviction, since that would reduce
86      * the cost of cleaning. However, the cleaner can add large numbers of
87      * nodes to the cache. By not participating in eviction, other threads
88      * could be kept in a constant state of eviction and would effectively
89      * starve. Therefore, this setting is currently enabled.
90      */

91     static final boolean DO_CRITICAL_EVICTION = true;
92
93     /*
94      * Cumulative counters. Updates to these counters occur in multiple
95      * threads, including FileProcessor threads, and are not synchronized.
96      * This could produce errors in counting, but avoids contention around stat
97      * updates.
98      */

99     int nBacklogFiles = 0;
100     int nCleanerRuns = 0;
101     int nCleanerDeletions = 0;
102     int nINsObsolete = 0;
103     int nINsCleaned = 0;
104     int nINsDead = 0;
105     int nINsMigrated = 0;
106     int nLNsObsolete = 0;
107     int nLNsCleaned = 0;
108     int nLNsDead = 0;
109     int nLNsLocked = 0;
110     int nLNsMigrated = 0;
111     int nLNsMarked = 0;
112     int nLNQueueHits = 0;
113     int nPendingLNsProcessed = 0;
114     int nMarkedLNsProcessed = 0;
115     int nToBeCleanedLNsProcessed = 0;
116     int nClusterLNsProcessed = 0;
117     int nPendingLNsLocked = 0;
118     int nEntriesRead = 0;
119     long nRepeatIteratorReads = 0;
120
121     /*
122      * Configuration parameters are non-private for use by FileProcessor,
123      * UtilizationTracker.
124      */

125     long lockTimeout;
126     int readBufferSize;
127     int lookAheadCacheSize;
128     int nDeadlockRetries;
129     boolean expunge;
130     boolean clusterResident;
131     boolean clusterAll;
132     int maxBatchFiles;
133     Level JavaDoc detailedTraceLevel;
134     long cleanerBytesInterval;
135     boolean trackDetail;
136     boolean fetchObsoleteSize;
137
138     /**
139      * All files that are to-be-cleaning or being-cleaned. Used to perform
140      * proactive migration. Is read-only after assignment, so no
141      * synchronization is needed.
142      */

143     Set JavaDoc mustBeCleanedFiles = Collections.EMPTY_SET;
144
145     /**
146      * All files that are below the minUtilization threshold. Used to perform
147      * clustering migration. Is read-only after assignment, so no
148      * synchronization is needed.
149      */

150     Set JavaDoc lowUtilizationFiles = Collections.EMPTY_SET;
151
152     private String JavaDoc name;
153     private EnvironmentImpl env;
154     private UtilizationProfile profile;
155     private UtilizationTracker tracker;
156     private FileSelector fileSelector;
157     private FileProcessor[] threads;
158
159     /*
160      * Log file deletion must check for the presence of read/only processes
161      * and ongoing backups.
162      */

163     private Object JavaDoc deleteFileLock;
164     private boolean deleteProhibited; // protected by deleteFileLock
165

166     public Cleaner(EnvironmentImpl env, String JavaDoc name)
167         throws DatabaseException {
168
169         this.env = env;
170         this.name = name;
171         tracker = new UtilizationTracker(env, this);
172         profile = new UtilizationProfile(env, tracker);
173         fileSelector = new FileSelector();
174         threads = new FileProcessor[0];
175         deleteFileLock = new Object JavaDoc();
176
177         /*
178          * The trackDetail property is immutable because of the complexity (if
179          * it were mutable) in determining whether to update the memory budget
180          * and perform eviction.
181          */

182         trackDetail = env.getConfigManager().getBoolean
183             (EnvironmentParams.CLEANER_TRACK_DETAIL);
184
185         /* Initialize mutable properties and register for notifications. */
186         envConfigUpdate(env.getConfigManager());
187         env.addConfigObserver(this);
188     }
189
190     /**
191      * Process notifications of mutable property changes.
192      */

193     public void envConfigUpdate(DbConfigManager cm)
194         throws DatabaseException {
195
196         lockTimeout = PropUtil.microsToMillis(cm.getLong
197                 (EnvironmentParams.CLEANER_LOCK_TIMEOUT));
198
199         readBufferSize = cm.getInt(EnvironmentParams.CLEANER_READ_SIZE);
200         if (readBufferSize <= 0) {
201             readBufferSize = cm.getInt
202                 (EnvironmentParams.LOG_ITERATOR_READ_SIZE);
203         }
204
205         lookAheadCacheSize = cm.getInt
206             (EnvironmentParams.CLEANER_LOOK_AHEAD_CACHE_SIZE);
207
208         nDeadlockRetries = cm.getInt(EnvironmentParams.CLEANER_DEADLOCK_RETRY);
209
210     expunge = cm.getBoolean(EnvironmentParams.CLEANER_REMOVE);
211
212     clusterResident = cm.getBoolean(EnvironmentParams.CLEANER_CLUSTER);
213
214     clusterAll = cm.getBoolean(EnvironmentParams.CLEANER_CLUSTER_ALL);
215
216         maxBatchFiles = cm.getInt(EnvironmentParams.CLEANER_MAX_BATCH_FILES);
217
218         detailedTraceLevel = Tracer.parseLevel
219             (env, EnvironmentParams.JE_LOGGING_LEVEL_CLEANER);
220
221         if (clusterResident && clusterAll) {
222             throw new IllegalArgumentException JavaDoc
223                 ("Both " + EnvironmentParams.CLEANER_CLUSTER +
224                  " and " + EnvironmentParams.CLEANER_CLUSTER_ALL +
225                  " may not be set to true.");
226         }
227
228         int nThreads = cm.getInt(EnvironmentParams.CLEANER_THREADS);
229         assert nThreads > 0;
230
231         if (nThreads != threads.length) {
232
233             /* Shutdown threads when reducing their number. */
234             for (int i = nThreads; i < threads.length; i += 1) {
235                 if (threads[i] != null) {
236                     threads[i].shutdown();
237                     threads[i] = null;
238                 }
239             }
240
241             /* Copy existing threads that are still used. */
242             FileProcessor[] newThreads = new FileProcessor[nThreads];
243             for (int i = 0; i < nThreads && i < threads.length; i += 1) {
244                 newThreads[i] = threads[i];
245             }
246
247             /* Don't lose track of new threads if an exception occurs. */
248             threads = newThreads;
249
250             /* Start new threads when increasing their number. */
251             for (int i = 0; i < nThreads; i += 1) {
252                 if (threads[i] == null) {
253                     threads[i] = new FileProcessor
254                         (name + '-' + (i + 1),
255                          env, this, profile, fileSelector);
256                 }
257             }
258         }
259
260         cleanerBytesInterval = cm.getLong
261             (EnvironmentParams.CLEANER_BYTES_INTERVAL);
262         if (cleanerBytesInterval == 0) {
263             cleanerBytesInterval = cm.getLong
264                 (EnvironmentParams.LOG_FILE_MAX) / 4;
265         }
266
267         fetchObsoleteSize = cm.getBoolean
268             (EnvironmentParams.CLEANER_FETCH_OBSOLETE_SIZE);
269     }
270
271     public UtilizationTracker getUtilizationTracker() {
272         return tracker;
273     }
274
275     public UtilizationProfile getUtilizationProfile() {
276         return profile;
277     }
278
279     public boolean getFetchObsoleteSize() {
280         return fetchObsoleteSize;
281     }
282
283     /*
284      * Delegate the run/pause/wakeup/shutdown DaemonRunner operations. We
285      * always check for null to account for the possibility of exceptions
286      * during thread creation. Cleaner daemon can't ever be run if No Locking
287      * mode is enabled.
288      */

289
290     public void runOrPause(boolean run) {
291     if (!env.isNoLocking()) {
292         for (int i = 0; i < threads.length; i += 1) {
293                 FileProcessor processor = threads[i];
294         if (processor != null) {
295
296                     /*
297                      * When the cleaner is set to run, we need to wake up the
298                      * thread immediately since there may be a backlog of files
299                      * to clean. But we must not block here if a file is
300                      * currently being processing. Therefore we force a wakeup
301                      * by adding a work item. This functionality may
302                      * eventually be moved to DaemonThread since it applies to
303                      * other deamons. [#15158]
304                      */

305                     if (run) {
306                         processor.addSentinalWorkObject();
307                     }
308             processor.runOrPause(run);
309         }
310         }
311     }
312     }
313
314     public void wakeup() {
315         for (int i = 0; i < threads.length; i += 1) {
316             if (threads[i] != null) {
317                 threads[i].wakeup();
318             }
319         }
320     }
321
322     public void requestShutdown() {
323         for (int i = 0; i < threads.length; i += 1) {
324             if (threads[i] != null) {
325                 threads[i].requestShutdown();
326             }
327         }
328     }
329
330     public void shutdown() {
331         for (int i = 0; i < threads.length; i += 1) {
332             if (threads[i] != null) {
333                 threads[i].shutdown();
334                 threads[i].clearEnv();
335                 threads[i] = null;
336             }
337         }
338     }
339     
340     public int getNWakeupRequests() {
341         int count = 0;
342         for (int i = 0; i < threads.length; i += 1) {
343             if (threads[i] != null) {
344                 count += threads[i].getNWakeupRequests();
345             }
346         }
347         return count;
348     }
349
350     private boolean areThreadsRunning() {
351         for (int i = 0; i < threads.length; i += 1) {
352             if (threads[i] != null) {
353                 return threads[i].isRunning();
354             }
355         }
356         return false;
357     }
358
359     /**
360      * Cleans selected files and returns the number of files cleaned. This
361      * method is not invoked by a deamon thread, it is programatically.
362      *
363      * @param cleanMultipleFiles is true to clean until we're under budget,
364      * or false to clean at most one file.
365      *
366      * @param forceCleaning is true to clean even if we're not under the
367      * utilization threshold.
368      *
369      * @return the number of files cleaned, not including files cleaned
370      * unsuccessfully.
371      */

372     public int doClean(boolean cleanMultipleFiles, boolean forceCleaning)
373         throws DatabaseException {
374
375         FileProcessor processor = new FileProcessor
376             ("", env, this, profile, fileSelector);
377         return processor.doClean
378             (false /*invokedFromDaemon*/, cleanMultipleFiles, forceCleaning);
379     }
380
381     /**
382      * Load stats.
383      */

384     public void loadStats(StatsConfig config, EnvironmentStats stat)
385         throws DatabaseException {
386
387         stat.setCleanerBacklog(nBacklogFiles);
388         stat.setNCleanerRuns(nCleanerRuns);
389         stat.setNCleanerDeletions(nCleanerDeletions);
390         stat.setNINsObsolete(nINsObsolete);
391         stat.setNINsCleaned(nINsCleaned);
392         stat.setNINsDead(nINsDead);
393         stat.setNINsMigrated(nINsMigrated);
394         stat.setNLNsObsolete(nLNsObsolete);
395         stat.setNLNsCleaned(nLNsCleaned);
396         stat.setNLNsDead(nLNsDead);
397         stat.setNLNsLocked(nLNsLocked);
398         stat.setNLNsMigrated(nLNsMigrated);
399         stat.setNLNsMarked(nLNsMarked);
400         stat.setNLNQueueHits(nLNQueueHits);
401         stat.setNPendingLNsProcessed(nPendingLNsProcessed);
402         stat.setNMarkedLNsProcessed(nMarkedLNsProcessed);
403         stat.setNToBeCleanedLNsProcessed(nToBeCleanedLNsProcessed);
404         stat.setNClusterLNsProcessed(nClusterLNsProcessed);
405         stat.setNPendingLNsLocked(nPendingLNsLocked);
406         stat.setNCleanerEntriesRead(nEntriesRead);
407         stat.setNRepeatIteratorReads(nRepeatIteratorReads);
408         
409         if (config.getClear()) {
410             nCleanerRuns = 0;
411             nCleanerDeletions = 0;
412             nINsObsolete = 0;
413             nINsCleaned = 0;
414             nINsDead = 0;
415             nINsMigrated = 0;
416             nLNsObsolete = 0;
417             nLNsCleaned = 0;
418             nLNsDead = 0;
419             nLNsLocked = 0;
420             nLNsMigrated = 0;
421             nLNsMarked = 0;
422             nLNQueueHits = 0;
423             nPendingLNsProcessed = 0;
424             nMarkedLNsProcessed = 0;
425             nToBeCleanedLNsProcessed = 0;
426             nClusterLNsProcessed = 0;
427             nPendingLNsLocked = 0;
428             nEntriesRead = 0;
429             nRepeatIteratorReads = 0;
430         }
431     }
432
433     /**
434      * Deletes all files that are safe-to-delete, if there are no read/only
435      * processes and concurrent backups.
436      *
437      * Deletion is coordinated by the synchronization variable deleteFileLock
438      * AND by the deleteProhibited state variable. The reason that two
439      * different mechanisms are use is because file deletion must be prevented
440      * both inter and intra-process. File locks must be used for inter-process,
441      * and the state bit for intra-process.
442      *
443      * To guard against read/only processes, the would-be deleter tries to get
444      * an exclusive lock on the environment. This will not be possible if a
445      * read/only process exists.
446      *
447      * To guard against backup mode, the would-be deleter checks the
448      * deleteProhibited state. Backup and file deletion can only be carried out
449      * by a read/write process, so both activities are working in the same
450      * process. Note that file locks are not supported intra-process. The
451      * deleteProhibited state is used rather than a simple synchronization on
452      * deleteFileLock because the start/endBackup is controlled by the
453      * application, and the copying of log files can take an arbitrarily long
454      * time. Using synchronization on deleteFileLock would make it possible to
455      * lock out a cleaner thread for an unacceptable amount of time.
456      */

457     void deleteSafeToDeleteFiles()
458         throws DatabaseException {
459
460         /*
461          * Synchronized to prevent multiple threads from requesting the same
462          * file lock.
463          */

464         synchronized (deleteFileLock) {
465             if (deleteProhibited) {
466                 return; /* deletion disabled. */
467             }
468
469             Set JavaDoc safeFiles = fileSelector.copySafeToDeleteFiles();
470             if (safeFiles == null) {
471                 return; /* Nothing to do. */
472             }
473
474             /*
475              * Fail loudly if the environment is invalid. A
476              * RunRecoveryException must have occurred.
477              */

478             env.checkIfInvalid();
479
480             /*
481              * Fail silently if the environment is not open.
482              */

483             if (env.mayNotWrite()) {
484                 return;
485             }
486
487             /*
488              * If we can't get an exclusive lock, then there are reader
489              * processes and we can't delete any cleaned files.
490              */

491             if (!env.getFileManager().lockEnvironment(false, true)) {
492                 Tracer.trace
493                     (Level.SEVERE, env, "Cleaner has " + safeFiles.size() +
494                      " files not deleted because of read-only processes.");
495                 return;
496             }
497
498             try {
499                 for (Iterator JavaDoc i = safeFiles.iterator(); i.hasNext();) {
500                     Long JavaDoc fileNum = (Long JavaDoc) i.next();
501                     long fileNumValue = fileNum.longValue();
502                     boolean deleted = false;
503                     try {
504                         if (expunge) {
505                             env.getFileManager().deleteFile(fileNumValue);
506                         } else {
507                             env.getFileManager().renameFile
508                                 (fileNumValue, FileManager.DEL_SUFFIX);
509                         }
510                         deleted = true;
511                     } catch (DatabaseException e) {
512                         traceFileNotDeleted(e, fileNumValue);
513                     } catch (IOException JavaDoc e) {
514                         traceFileNotDeleted(e, fileNumValue);
515                     }
516
517                     /*
518                      * If the log file was not deleted, leave it in the
519                      * safe-to-delete set (and the UP) so that we will retry
520                      * the deletion later. If the log file was deleted, trace
521                      * the deletion, delete the file from the UP and from the
522                      * safe-to-delete set.
523                      *
524                      * We do not retry if an error occurs deleting the UP
525                      * database entries below. Retrying is intended only to
526                      * solve a problem on Windows where deleting a log file
527                      * isn't always possible immediately after closing it.
528                      */

529                     if (deleted) {
530                         Tracer.trace
531                             (Level.SEVERE, env,
532                              "Cleaner deleted file 0x" +
533                              Long.toHexString(fileNumValue));
534
535                         /*
536                          * Remove the file from the profile before removing
537                          * it from the safe-to-delete set. If we remove in the
538                          * reverse order, it may be selected for cleaning.
539                          * Always delete the file from the safe-to-delete set
540                          * (in a finally block) so that we don't attempt to
541                          * delete the file again.
542                          */

543                         try {
544                             profile.removeFile(fileNum);
545                         } finally {
546                             fileSelector.removeDeletedFile(fileNum);
547                         }
548                     }
549                     nCleanerDeletions++;
550                 }
551             } finally {
552                 env.getFileManager().releaseExclusiveLock();
553             }
554         }
555     }
556
557     public void setDeleteProhibited() {
558
559         synchronized (deleteFileLock) {
560             deleteProhibited = true;
561         }
562     }
563
564     public void clearDeleteProhibited() {
565         synchronized (deleteFileLock) {
566             deleteProhibited = false;
567         }
568     }
569
570     private void traceFileNotDeleted(Exception JavaDoc e, long fileNum) {
571         Tracer.trace
572             (env, "Cleaner", "deleteSafeToDeleteFiles",
573              "Log file 0x" + Long.toHexString(fileNum) + " could not be " +
574              (expunge ? "deleted" : "renamed") +
575              ". This operation will be retried at the next checkpoint.",
576              e);
577     }
578
579     /**
580      * Returns a copy of the cleaned and processed files at the time a
581      * checkpoint starts.
582      *
583      * <p>If non-null is returned, the checkpoint should flush an extra level,
584      * and addCheckpointedFiles() should be called when the checkpoint is
585      * complete.</p>
586      */

587     public CheckpointStartCleanerState getFilesAtCheckpointStart()
588         throws DatabaseException {
589
590         /* Pending LNs can prevent file deletion. */
591         processPending();
592
593         return fileSelector.getFilesAtCheckpointStart();
594     }
595
596     /**
597      * When a checkpoint is complete, update the files that were returned at
598      * the beginning of the checkpoint.
599      */

600     public void updateFilesAtCheckpointEnd(CheckpointStartCleanerState info)
601         throws DatabaseException {
602
603         fileSelector.updateFilesAtCheckpointEnd(info);
604         deleteSafeToDeleteFiles();
605     }
606
607     /**
608      * Update the lowUtilizationFiles and mustBeCleanedFiles fields with new
609      * read-only collections, and update the backlog file count.
610      */

611     public void updateReadOnlyFileCollections() {
612         mustBeCleanedFiles = fileSelector.getMustBeCleanedFiles();
613         lowUtilizationFiles = fileSelector.getLowUtilizationFiles();
614         nBacklogFiles = fileSelector.getBacklog();
615     }
616
617     /**
618      * If any LNs are pending, process them. This method should be called
619      * often enough to prevent the pending LN set from growing too large.
620      */

621     void processPending()
622         throws DatabaseException {
623
624         DbTree dbMapTree = env.getDbMapTree();
625
626         LNInfo[] pendingLNs = fileSelector.getPendingLNs();
627         if (pendingLNs != null) {
628             TreeLocation location = new TreeLocation();
629
630             for (int i = 0; i < pendingLNs.length; i += 1) {
631                 LNInfo info = pendingLNs[i];
632
633                 DatabaseId dbId = info.getDbId();
634                 DatabaseImpl db = dbMapTree.getDb(dbId, lockTimeout);
635
636                 byte[] key = info.getKey();
637                 byte[] dupKey = info.getDupKey();
638                 LN ln = info.getLN();
639
640                 /* Evict before processing each entry. */
641                 if (DO_CRITICAL_EVICTION) {
642                     env.getEvictor().doCriticalEviction(true); // backgroundIO
643
}
644
645                 processPendingLN
646                     (ln, db, key, dupKey, location);
647
648                 /* Sleep if background read/write limit was exceeded. */
649                 env.sleepAfterBackgroundIO();
650             }
651         }
652
653         DatabaseId[] pendingDBs = fileSelector.getPendingDBs();
654         if (pendingDBs != null) {
655             for (int i = 0; i < pendingDBs.length; i += 1) {
656                 DatabaseId dbId = pendingDBs[i];
657                 DatabaseImpl db = dbMapTree.getDb(dbId, lockTimeout);
658                 if (db == null || db.isDeleteFinished()) {
659                     fileSelector.removePendingDB(dbId);
660                 }
661             }
662         }
663     }
664
665     /**
666      * Processes a pending LN, getting the lock first to ensure that the
667      * overhead of retries is mimimal.
668      */

669     private void processPendingLN(LN ln,
670                                   DatabaseImpl db,
671                                   byte[] key,
672                                   byte[] dupKey,
673                                   TreeLocation location)
674         throws DatabaseException {
675
676         boolean parentFound = false; // We found the parent BIN.
677
boolean processedHere = true; // The LN was cleaned here.
678
boolean lockDenied = false; // The LN lock was denied.
679
boolean obsolete = false; // The LN is no longer in use.
680
boolean completed = false; // This method completed.
681

682         BasicLocker locker = null;
683         BIN bin = null;
684         DIN parentDIN = null;
685         try {
686             nPendingLNsProcessed++;
687
688             /*
689              * If the DB is gone, this LN is obsolete. If delete cleanup is in
690              * progress, put the DB into the DB pending set; this LN will be
691              * declared deleted after the delete cleanup is finished.
692              */

693             if (db == null || db.isDeleted()) {
694                 addPendingDB(db);
695                 nLNsDead++;
696                 obsolete = true;
697                 completed = true;
698                 return;
699             }
700
701             Tree tree = db.getTree();
702             assert tree != null;
703
704             /* Get a non-blocking lock on the original node ID. */
705
706         locker = new BasicLocker(env);
707             LockResult lockRet = locker.nonBlockingLock
708                 (ln.getNodeId(), LockType.READ, db);
709             if (lockRet.getLockGrant() == LockGrantType.DENIED) {
710                 /* Try again later. */
711                 nPendingLNsLocked++;
712                 lockDenied = true;
713                 completed = true;
714                 return;
715             }
716
717             /*
718          * Search down to the bottom most level for the parent of this LN.
719              *
720              * We pass searchDupTree=true to search the dup tree by nodeID if
721              * necessary. This handles the case where dupKey is null because
722              * the pending entry was a deleted single-duplicate in a BIN.
723          */

724             parentFound = tree.getParentBINForChildLN
725                 (location, key, dupKey, ln,
726                  false, // splitsAllowed
727
true, // findDeletedEntries
728
true, // searchDupTree
729
UPDATE_GENERATION);
730             bin = location.bin;
731             int index = location.index;
732
733             if (!parentFound) {
734                 nLNsDead++;
735                 obsolete = true;
736                 completed = true;
737         return;
738             }
739
740             if (ln.containsDuplicates()) {
741                 /* Migrate a DupCountLN. */
742                 parentDIN = (DIN) bin.fetchTarget(index);
743                 parentDIN.latch(UPDATE_GENERATION);
744                 ChildReference dclRef = parentDIN.getDupCountLNRef();
745                 processedHere = false;
746                 migrateDupCountLN
747                     (db, dclRef.getLsn(), parentDIN, dclRef,
748                      true, // wasCleaned
749
true, // isPending
750
ln.getNodeId(), // lockedPendingNodeId
751
CLEAN_PENDING_LN);
752             } else {
753                 /* Migrate a plain LN. */
754                 processedHere = false;
755                 migrateLN
756                     (db, bin.getLsn(index), bin, index,
757                      true, // wasCleaned
758
true, // isPending
759
ln.getNodeId(), // lockedPendingNodeId
760
true, // backgroundIO
761
CLEAN_PENDING_LN);
762             }
763             completed = true;
764     } catch (DatabaseException DBE) {
765         DBE.printStackTrace();
766         Tracer.trace(env, "com.sleepycat.je.cleaner.Cleaner", "processLN",
767              "Exception thrown: ", DBE);
768         throw DBE;
769         } finally {
770             if (parentDIN != null) {
771                 parentDIN.releaseLatchIfOwner();
772             }
773
774             if (bin != null) {
775                 bin.releaseLatchIfOwner();
776             }
777
778             if (locker != null) {
779                 locker.operationEnd();
780             }
781
782             /*
783              * If migrateLN was not called above, remove the pending LN and
784              * perform tracing in this method.
785              */

786             if (processedHere) {
787                 if (completed && !lockDenied) {
788                     fileSelector.removePendingLN(ln.getNodeId());
789                 }
790                 trace(detailedTraceLevel, CLEAN_PENDING_LN, ln, DbLsn.NULL_LSN,
791                       completed, obsolete, false /*migrated*/);
792             }
793         }
794     }
795
796     /**
797      * Returns whether the given BIN entry may be stripped by the evictor.
798      * True is always returned if the BIN is not dirty. False is returned if
799      * the BIN is dirty and the entry will be migrated soon.
800      */

801     public boolean isEvictable(BIN bin, int index) {
802
803         if (bin.getDirty()) {
804
805             if (bin.getMigrate(index)) {
806                 return false;
807             }
808
809             long lsn = bin.getLsn(index);
810             if (lsn == DbLsn.NULL_LSN) {
811             
812                 /*
813                  * LN is resident but never logged, no cleaning restrictions
814                  * apply.
815                  */

816             return true;
817             }
818             
819             boolean isResident = (bin.getTarget(index) != null);
820             Long JavaDoc fileNum = new Long JavaDoc(DbLsn.getFileNumber(lsn));
821
822             if ((PROACTIVE_MIGRATION || isResident) &&
823                 mustBeCleanedFiles.contains(fileNum)) {
824                 return false;
825             }
826
827             if ((clusterAll || (clusterResident && isResident)) &&
828                 lowUtilizationFiles.contains(fileNum)) {
829                 return false;
830             }
831         }
832
833         return true;
834     }
835
836     /**
837      * This method should be called just before logging a BIN. LNs will be
838      * migrated if the MIGRATE flag is set, or if they are in a file to be
839      * cleaned, or if the LNs qualify according to the rules for cluster and
840      * clusterAll.
841      *
842      * <p>On return this method guarantees that no MIGRATE flag will be set on
843      * any child entry. If this method is *not* called before logging a BIN,
844      * then the addPendingLNs method must be called.</p>
845      *
846      * @param bin is the latched BIN. The latch will not be released by this
847      * method.
848      *
849      * @param proactiveMigration perform proactive migration if needed; this is
850      * false during a split, to reduce the delay in the user operation.
851      */

852     public void lazyMigrateLNs(final BIN bin,
853                                boolean proactiveMigration,
854                                boolean backgroundIO)
855         throws DatabaseException {
856
857         DatabaseImpl db = bin.getDatabase();
858
859         boolean isBinInDupDb = db.getSortedDuplicates() &&
860                                !bin.containsDuplicates();
861
862         /*
863          * For non-resident LNs, sort them by LSN before migrating them.
864          * Fetching in LSN order reduces physical disk I/O.
865          */

866         Integer JavaDoc[] sortedIndices = null;
867         int nSortedIndices = 0;
868         int nEntries = bin.getNEntries();
869
870         for (int index = 0; index < nEntries; index += 1) {
871
872             boolean migrateFlag = bin.getMigrate(index);
873             boolean isResident = (bin.getTarget(index) != null);
874             long childLsn = bin.getLsn(index);
875
876             if (childLsn != DbLsn.NULL_LSN) {
877             /* LSN could be NULL_LSN if deferred-write mode */
878             
879                 if (shouldMigrateLN
880                     (migrateFlag, isResident, proactiveMigration, isBinInDupDb,
881                      childLsn)) {
882
883                      if (isResident) {
884                          migrateLN
885                          (db, childLsn, bin, index,
886                           migrateFlag, // wasCleaned
887
false, // isPending
888
0, // lockedPendingNodeId
889
backgroundIO,
890                           CLEAN_MIGRATE_LN);
891                      } else {
892                          if (sortedIndices == null) {
893                              sortedIndices = new Integer JavaDoc[nEntries];
894                          }
895                          sortedIndices[nSortedIndices++] = new Integer JavaDoc(index);
896                      }
897                 }
898             }
899         }
900
901         if (sortedIndices != null) {
902             Arrays.sort(sortedIndices, 0, nSortedIndices, new Comparator JavaDoc() {
903                 public int compare(Object JavaDoc o1, Object JavaDoc o2) {
904                     int i1 = ((Integer JavaDoc) o1).intValue();
905                     int i2 = ((Integer JavaDoc) o2).intValue();
906                     return DbLsn.compareTo(bin.getLsn(i1), bin.getLsn(i2));
907                 }
908             });
909             for (int i = 0; i < nSortedIndices; i += 1) {
910                 int index = sortedIndices[i].intValue();
911                 long childLsn = bin.getLsn(index);
912                 boolean migrateFlag = bin.getMigrate(index);
913                 migrateLN
914                     (db, childLsn, bin, index,
915                      migrateFlag, // wasCleaned
916
false, // isPending
917
0, // lockedPendingNodeId
918
backgroundIO,
919                      CLEAN_MIGRATE_LN);
920             }
921         }
922     }
923
924     /**
925      * This method should be called just before logging a root DIN. The
926      * DupCountLN will be migrated if the MIGRATE flag is set, or if it is in a
927      * file to be cleaned, or if the LN qualifies according to the rules for
928      * cluster and clusterAll.
929      *
930      * <p>On return this method guarantees that the MIGRATE flag will not be
931      * set on the child entry. If this method is *not* called before logging a
932      * root DIN, then the addPendingDupCountLN method must be called.</p>
933      *
934      * @param din is the latched DIN. The latch will not be released by this
935      * method.
936      *
937      * @param dclRef is the reference to the DupCountLN.
938      *
939      * @param proactiveMigration perform proactive migration if needed; this is
940      * false during a split, to reduce the delay in the user operation.
941      */

942     public void lazyMigrateDupCountLN(DIN din,
943                                       ChildReference dclRef,
944                                       boolean proactiveMigration)
945         throws DatabaseException {
946
947         DatabaseImpl db = din.getDatabase();
948
949         boolean migrateFlag = dclRef.getMigrate();
950         boolean isResident = (dclRef.getTarget() != null);
951         boolean isBinInDupDb = false;
952         long childLsn = dclRef.getLsn();
953
954         if (shouldMigrateLN
955             (migrateFlag, isResident, proactiveMigration, isBinInDupDb,
956              childLsn)) {
957
958             migrateDupCountLN
959                 (db, childLsn, din, dclRef,
960                  migrateFlag, // wasCleaned
961
false, // isPending
962
0, // lockedPendingNodeId
963
CLEAN_MIGRATE_LN);
964         }
965     }
966
967     /**
968      * Returns whether an LN entry should be migrated. Updates stats.
969      *
970      * @param migrateFlag is whether the MIGRATE flag is set on the entry.
971      *
972      * @param isResident is whether the LN is currently resident.
973      *
974      * @param proactiveMigration perform proactive migration if needed; this is
975      * false during a split, to reduce the delay in the user operation.
976      *
977      * @param isBinInDupDb is whether this is a BIN entry in a database with
978      * duplicates enabled.
979      *
980      * @param childLsn is the LSN of the LN.
981      *
982      * @return whether to migrate the LN.
983      */

984     private boolean shouldMigrateLN(boolean migrateFlag,
985                                     boolean isResident,
986                                     boolean proactiveMigration,
987                                     boolean isBinInDupDb,
988                                     long childLsn) {
989         boolean doMigration = false;
990
991         if (migrateFlag) {
992
993             /*
994              * Always try to migrate if the MIGRATE flag is set, since the LN
995              * has been processed. If we did not migrate it, we would have to
996              * add it to pending LN set.
997              */

998             doMigration = true;
999             nMarkedLNsProcessed++;
1000
1001        } else if (!proactiveMigration || isBinInDupDb || env.isClosing()) {
1002
1003            /*
1004             * Do nothing if proactiveMigration is false, since all further
1005             * migration is optional.
1006             *
1007             * Do nothing if this is a BIN in a duplicate database. We
1008             * must not fetch DINs, since this BIN may be about to be
1009             * evicted. Fetching a DIN would add it as an orphan to the
1010             * INList, plus an IN with non-LN children is not evictable.
1011             *
1012             * Do nothing if the environment is shutting down and the
1013             * MIGRATE flag is not set. Proactive migration during
1014             * shutdown is counterproductive -- it prevents a short final
1015             * checkpoint, and it does not allow more files to be deleted.
1016             */

1017
1018        } else {
1019
1020            Long JavaDoc fileNum = new Long JavaDoc(DbLsn.getFileNumber(childLsn));
1021
1022            if ((PROACTIVE_MIGRATION || isResident) &&
1023                mustBeCleanedFiles.contains(fileNum)) {
1024
1025                /* Migrate because it will be cleaned soon. */
1026                doMigration = true;
1027                nToBeCleanedLNsProcessed++;
1028
1029            } else if ((clusterAll || (clusterResident && isResident)) &&
1030                lowUtilizationFiles.contains(fileNum)) {
1031
1032                /* Migrate for clustering. */
1033                doMigration = true;
1034                nClusterLNsProcessed++;
1035            }
1036        }
1037
1038        return doMigration;
1039    }
1040
1041    /**
1042     * Migrate an LN in the given BIN entry, if it is not obsolete. The BIN is
1043     * latched on entry to this method and is left latched when it returns.
1044     */

1045    private void migrateLN(DatabaseImpl db,
1046                           long lsn,
1047                           BIN bin,
1048                           int index,
1049                           boolean wasCleaned,
1050                           boolean isPending,
1051                           long lockedPendingNodeId,
1052                           boolean backgroundIO,
1053                           String JavaDoc cleanAction)
1054        throws DatabaseException {
1055
1056        /* Status variables are used to generate debug tracing info. */
1057        boolean obsolete = false; // The LN is no longer in use.
1058
boolean migrated = false; // The LN was in use and is migrated.
1059
boolean lockDenied = false; // The LN lock was denied.
1060
boolean completed = false; // This method completed.
1061
boolean clearTarget = false; // Node was non-resident when called.
1062

1063        /*
1064         * If wasCleaned is false we don't count statistics unless we migrate
1065         * the LN. This avoids double counting.
1066         */

1067        BasicLocker locker = null;
1068        LN ln = null;
1069
1070        try {
1071            if (lsn == DbLsn.NULL_LSN) {
1072                /* This node was never written, no need to migrate. */
1073                completed = true;
1074                return;
1075            }
1076
1077            /*
1078             * Fetch the node, if necessary. If it was not resident and it is
1079             * an evictable LN, we will clear it after we migrate it.
1080             */

1081        if (!bin.isEntryKnownDeleted(index)) {
1082                ln = (LN) bin.getTarget(index);
1083                if (ln == null) {
1084                    /* If fetchTarget returns null, a deleted LN was cleaned.*/
1085                    ln = (LN) bin.fetchTarget(index);
1086                    clearTarget = !db.getId().equals(DbTree.ID_DB_ID);
1087                }
1088            }
1089
1090        /* Don't migrate knownDeleted or deleted cleaned LNs. */
1091            if (ln == null) {
1092                if (wasCleaned) {
1093                    nLNsDead++;
1094                }
1095                obsolete = true;
1096                completed = true;
1097                return;
1098        }
1099
1100            /*
1101             * Get a non-blocking read lock on the LN. A pending node is
1102             * already locked, but that node ID may be different than the
1103             * current LN's node if a slot is reused. We must lock the current
1104             * node to guard against aborts.
1105             */

1106            if (lockedPendingNodeId != ln.getNodeId()) {
1107                locker = new BasicLocker(env);
1108                LockResult lockRet = locker.nonBlockingLock
1109                    (ln.getNodeId(), LockType.READ, db);
1110                if (lockRet.getLockGrant() == LockGrantType.DENIED) {
1111
1112                    /*
1113                     * LN is currently locked by another Locker, so we can't
1114                     * assume anything about the value of the LSN in the bin.
1115                     */

1116                    if (wasCleaned) {
1117                        nLNsLocked++;
1118                    }
1119                    lockDenied = true;
1120                    completed = true;
1121                    return;
1122                }
1123            }
1124
1125        /* Don't migrate deleted LNs. */
1126            if (ln.isDeleted()) {
1127                bin.setKnownDeletedLeaveTarget(index);
1128                if (wasCleaned) {
1129                    nLNsDead++;
1130                }
1131                obsolete = true;
1132                completed = true;
1133                return;
1134            }
1135
1136            /*
1137             * Once we have a lock, check whether the current LSN needs to be
1138             * migrated. There is no need to migrate it if the LSN no longer
1139             * qualifies for cleaning. The LSN could have been changed by an
1140             * update or delete after we set the MIGRATE flag.
1141             *
1142             * Note that we do not perform this optimization if the MIGRATE
1143             * flag is not set, i.e, for clustering and proactive migration of
1144             * resident LNs. For these cases, we checked the conditions for
1145             * migration immediately before calling this method. Although the
1146             * condition could change after locking, the window is small and
1147             * a second check is not worthwhile.
1148             */

1149            if (bin.getMigrate(index)) {
1150                Long JavaDoc fileNum = new Long JavaDoc(DbLsn.getFileNumber(lsn));
1151                if (!fileSelector.isFileCleaningInProgress(fileNum)) {
1152                    obsolete = true;
1153                    completed = true;
1154                    if (wasCleaned) {
1155                        nLNsDead++;
1156                    }
1157                    return;
1158                }
1159            }
1160
1161            /* Migrate the LN. */
1162            byte[] key = getLNMainKey(bin, index);
1163            long newLNLsn = ln.log
1164                (env, db.getId(), key, lsn, ln.getTotalLastLoggedSize(key),
1165                 locker, backgroundIO);
1166            bin.updateEntry(index, newLNLsn);
1167            nLNsMigrated++;
1168            migrated = true;
1169            completed = true;
1170            return;
1171        } finally {
1172            if (isPending) {
1173                if (completed && !lockDenied) {
1174                    fileSelector.removePendingLN(lockedPendingNodeId);
1175                }
1176            } else {
1177
1178                /*
1179                 * If a to-be-migrated LN was not processed successfully, we
1180                 * must guarantee that the file will not be deleted and that we
1181                 * will retry the LN later. The retry information must be
1182                 * complete or we may delete a file later without processing
1183                 * all of its LNs.
1184                 */

1185                if (bin.getMigrate(index) && (!completed || lockDenied)) {
1186
1187                    byte[] key = getLNMainKey(bin, index);
1188                    byte[] dupKey = getLNDupKey(bin, index, ln);
1189                    fileSelector.addPendingLN(ln, db.getId(), key, dupKey);
1190
1191                    /* Wake up the cleaner thread to process pending LNs. */
1192                    if (!areThreadsRunning()) {
1193                        env.getUtilizationTracker().activateCleaner();
1194                    }
1195
1196                    /*
1197                     * If we need to retry, don't clear the target since we
1198                     * would only have to fetch it again soon.
1199                     */

1200                    clearTarget = false;
1201                }
1202            }
1203
1204            /*
1205             * Always clear the migrate flag. If the LN could not be locked
1206             * and the migrate flag was set, the LN will have been added to the
1207             * pending LN set above.
1208             */

1209            bin.setMigrate(index, false);
1210
1211            /*
1212             * If the node was originally non-resident, clear it now so that we
1213             * don't create more work for the evictor and reduce the cache
1214             * memory available to the application.
1215             */

1216            if (clearTarget) {
1217                bin.updateEntry(index, null);
1218            }
1219
1220            if (locker != null) {
1221                locker.operationEnd();
1222            }
1223
1224            trace(detailedTraceLevel, cleanAction, ln, lsn,
1225                  completed, obsolete, migrated);
1226        }
1227    }
1228
1229    /**
1230     * Migrate the DupCountLN for the given DIN. The DIN is latched on entry
1231     * to this method and is left latched when it returns.
1232     */

1233    private void migrateDupCountLN(DatabaseImpl db,
1234                                   long lsn,
1235                                   DIN parentDIN,
1236                                   ChildReference dclRef,
1237                                   boolean wasCleaned,
1238                                   boolean isPending,
1239                                   long lockedPendingNodeId,
1240                                   String JavaDoc cleanAction)
1241        throws DatabaseException {
1242
1243        /* Status variables are used to generate debug tracing info. */
1244        boolean obsolete = false; // The LN is no longer in use.
1245
boolean migrated = false; // The LN was in use and is migrated.
1246
boolean lockDenied = false; // The LN lock was denied.
1247
boolean completed = false; // This method completed.
1248
boolean clearTarget = false; // Node was non-resident when called.
1249

1250        /*
1251         * If wasCleaned is false we don't count statistics unless we migrate
1252         * the LN. This avoids double counting.
1253         */

1254        BasicLocker locker = null;
1255        LN ln = null;
1256
1257        try {
1258            if (lsn == DbLsn.NULL_LSN) {
1259                /* This node was never written, no need to migrate. */
1260                completed = true;
1261                return;
1262            }
1263
1264            /*
1265             * Fetch the node, if necessary. If it was not resident and it is
1266             * an evictable LN, we will clear it after we migrate it.
1267             */

1268        ln = (LN) dclRef.getTarget();
1269            if (ln == null) {
1270                ln = (LN) dclRef.fetchTarget(db, parentDIN);
1271                assert ln != null;
1272                clearTarget = !db.getId().equals(DbTree.ID_DB_ID);
1273            }
1274
1275            /*
1276             * Get a non-blocking read lock on the LN, if this is not an
1277             * already locked pending node.
1278             */

1279            if (lockedPendingNodeId != ln.getNodeId()) {
1280                locker = new BasicLocker(env);
1281                LockResult lockRet = locker.nonBlockingLock
1282                    (ln.getNodeId(), LockType.READ, db);
1283                if (lockRet.getLockGrant() == LockGrantType.DENIED) {
1284
1285                    /*
1286                     * LN is currently locked by another Locker, so we can't
1287                     * assume anything about the value of the LSN in the bin.
1288                     */

1289                    if (wasCleaned) {
1290                        nLNsLocked++;
1291                    }
1292                    lockDenied = true;
1293                    completed = true;
1294                    return;
1295                }
1296            }
1297
1298            /*
1299             * Once we have a lock, check whether the current LSN needs to be
1300             * migrated. There is no need to migrate it if the LSN no longer
1301             * qualifies for cleaning.
1302             */

1303            Long JavaDoc fileNum = new Long JavaDoc(DbLsn.getFileNumber(lsn));
1304            if (!fileSelector.isFileCleaningInProgress(fileNum)) {
1305                obsolete = true;
1306                completed = true;
1307                if (wasCleaned) {
1308                    nLNsDead++;
1309                }
1310                return;
1311            }
1312
1313            /* Migrate the LN. */
1314            byte[] key = parentDIN.getDupKey();
1315            long newLNLsn = ln.log
1316                (env, db.getId(), key, lsn, ln.getTotalLastLoggedSize(key),
1317                 locker,
1318                 false); // backgroundIO
1319
parentDIN.updateDupCountLNRef(newLNLsn);
1320            nLNsMigrated++;
1321            migrated = true;
1322            completed = true;
1323            return;
1324        } finally {
1325            if (isPending) {
1326                if (completed && !lockDenied) {
1327                    fileSelector.removePendingLN(lockedPendingNodeId);
1328                }
1329            } else {
1330
1331                /*
1332                 * If a to-be-migrated LN was not processed successfully, we
1333                 * must guarantee that the file will not be deleted and that we
1334                 * will retry the LN later. The retry information must be
1335                 * complete or we may delete a file later without processing
1336                 * all of its LNs.
1337                 */

1338                if (dclRef.getMigrate() && (!completed || lockDenied)) {
1339
1340                    byte[] key = parentDIN.getDupKey();
1341                    byte[] dupKey = null;
1342                    fileSelector.addPendingLN(ln, db.getId(), key, dupKey);
1343
1344                    /* Wake up the cleaner thread to process pending LNs. */
1345                    if (!areThreadsRunning()) {
1346                        env.getUtilizationTracker().activateCleaner();
1347                    }
1348
1349                    /*
1350                     * If we need to retry, don't clear the target since we
1351                     * would only have to fetch it again soon.
1352                     */

1353                    clearTarget = false;
1354                }
1355            }
1356
1357            /*
1358             * Always clear the migrate flag. If the LN could not be locked
1359             * and the migrate flag was set, the LN will have been added to the
1360             * pending LN set above.
1361             */

1362            dclRef.setMigrate(false);
1363
1364            /*
1365             * If the node was originally non-resident, clear it now so that we
1366             * don't create more work for the evictor and reduce the cache
1367             * memory available to the application.
1368             */

1369            if (clearTarget) {
1370                parentDIN.updateDupCountLN(null);
1371            }
1372
1373            if (locker != null) {
1374                locker.operationEnd();
1375            }
1376
1377            trace(detailedTraceLevel, cleanAction, ln, lsn,
1378                  completed, obsolete, migrated);
1379        }
1380    }
1381
1382    /**
1383     * Returns the main key for a given BIN entry.
1384     */

1385    private byte[] getLNMainKey(BIN bin, int index)
1386        throws DatabaseException {
1387
1388        if (bin.containsDuplicates()) {
1389            return bin.getDupKey();
1390        } else {
1391            return bin.getKey(index);
1392        }
1393    }
1394
1395    /**
1396     * Returns the duplicate key for a given BIN entry.
1397     */

1398    private byte[] getLNDupKey(BIN bin, int index, LN ln)
1399        throws DatabaseException {
1400
1401        DatabaseImpl db = bin.getDatabase();
1402
1403        if (!db.getSortedDuplicates() || ln.containsDuplicates()) {
1404
1405            /*
1406             * The dup key is not needed for a non-duplicate DB or for a
1407             * DupCountLN.
1408             */

1409            return null;
1410
1411        } else if (bin.containsDuplicates()) {
1412
1413            /* The DBIN entry key is the dup key. */
1414            return bin.getKey(index);
1415
1416        } else {
1417
1418            /*
1419             * The data is the dup key if the LN is not deleted. If the LN is
1420             * deleted, this method will return null and we will do a node ID
1421             * search later when processing the pending LN.
1422             */

1423            return ln.getData();
1424        }
1425    }
1426
1427    /**
1428     * Adds the DB ID to the pending DB set if it is being deleted but deletion
1429     * is not yet complete.
1430     */

1431    void addPendingDB(DatabaseImpl db) {
1432        if (db != null && db.isDeleted() && !db.isDeleteFinished()) {
1433            DatabaseId id = db.getId();
1434            if (fileSelector.addPendingDB(id)) {
1435                Tracer.trace
1436                    (detailedTraceLevel, env, "CleanAddPendingDB " + id);
1437            }
1438        }
1439    }
1440
1441    /**
1442     * Send trace messages to the java.util.logger. Don't rely on the logger
1443     * alone to conditionalize whether we send this message, we don't even want
1444     * to construct the message if the level is not enabled.
1445     */

1446    void trace(Level JavaDoc level,
1447               String JavaDoc action,
1448               Node node,
1449               long logLsn,
1450               boolean completed,
1451               boolean obsolete,
1452               boolean dirtiedMigrated) {
1453
1454        Logger JavaDoc logger = env.getLogger();
1455        if (logger.isLoggable(level)) {
1456            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1457            sb.append(action);
1458            if (node != null) {
1459                sb.append(" node=");
1460                sb.append(node.getNodeId());
1461            }
1462            sb.append(" logLsn=");
1463            sb.append(DbLsn.getNoFormatString(logLsn));
1464            sb.append(" complete=").append(completed);
1465            sb.append(" obsolete=").append(obsolete);
1466            sb.append(" dirtiedOrMigrated=").append(dirtiedMigrated);
1467
1468            logger.log(level, sb.toString());
1469        }
1470    }
1471}
1472
Popular Tags