1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.cleaner;
9 
10 import static com.sleepycat.je.cleaner.CleanerStatDefinition.CLEANER_BACKLOG;
11 import static com.sleepycat.je.cleaner.CleanerStatDefinition.CLEANER_FILE_DELETION_BACKLOG;
12 import static com.sleepycat.je.cleaner.CleanerStatDefinition.CLEANER_PENDING_LN_QUEUE_SIZE;
13 
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.LinkedList;
19 import java.util.Map;
20 import java.util.NavigableSet;
21 import java.util.Set;
22 import java.util.SortedMap;
23 import java.util.TreeMap;
24 import java.util.TreeSet;
25 
26 import com.sleepycat.je.dbi.DatabaseId;
27 import com.sleepycat.je.dbi.MemoryBudget;
28 import com.sleepycat.je.tree.LN;
29 import com.sleepycat.je.utilint.IntStat;
30 import com.sleepycat.je.utilint.StatGroup;
31 import com.sleepycat.je.utilint.VLSN;
32 
33 /**
34  * Keeps track of the status of files for which cleaning is in progres.
35  */
36 public class FileSelector {
37 
38     /**
39      * Each file for which cleaning is in progress has one of the following
40      * status values.  Files numbers migrate from one status to another, in
41      * the order declared below.
42      */
43     private enum FileStatus {
44 
45         /**
46          * A file's status is initially TO_BE_CLEANED when it is selected as
47          * part of a batch of files that, when deleted, will bring total
48          * utilization down to the minimum configured value.  All files with
49          * this status will be cleaned in lowest-cost-to-clean order.  For two
50          * files of equal cost to clean, the lower numbered (oldest) files is
51          * selected; this is why the fileInfoMap is sorted by key (file
52          * number).
53          */
54         TO_BE_CLEANED,
55 
56         /**
57          * When a TO_BE_CLEANED file is selected for processing by
58          * FileProcessor, it is moved to the BEING_CLEANED status.  This
59          * distinction is used to prevent a file from being processed by more
60          * than one thread.
61          */
62         BEING_CLEANED,
63 
64         /**
65          * A file is moved to the CLEANED status when all its log entries have
66          * been read and processed.  However, entries needing migration will be
67          * marked with the BIN entry MIGRATE flag, entries that could not be
68          * locked will be in the pending LN set, and the DBs that were pending
69          * deletion will be in the pending DB set.
70          */
71         CLEANED,
72 
73         /**
74          * A file is moved to the CHECKPOINTED status at the end of a
75          * checkpoint if it was CLEANED at the beginning of the checkpoint.
76          * Because all dirty BINs are flushed during the checkpoints, no files
77          * in this set will have entries with the MIGRATE flag set.  However,
78          * some entries may be in the pending LN set and some DBs may be in the
79          * pending DB set.
80          */
81         CHECKPOINTED,
82 
83         /**
84          * A file is moved from the CHECKPOINTED status to the FULLY_PROCESSED
85          * status when the pending LN/DB sets become empty.  Since a pending LN
86          * was not locked successfully, we don't know its original file.  But
87          * we do know that when no pending LNs are present for any file, all
88          * log entries in CHECKPOINTED files are either obsolete or have been
89          * migrated.  Note, however, that the parent BINs of the migrated
90          * entries may not have been logged yet.
91          *
92          * No special handling is required to coordinate syncing of deferred
93          * write databases for pending, deferred write LNs, because
94          * non-temporary deferred write DBs are always synced during
95          * checkpoints, and temporary deferred write DBs are not recovered.
96          * Note that although DW databases are non-txnal, their LNs may be
97          * pended because of lock collisions.
98          */
99         FULLY_PROCESSED,
100 
101         /**
102          * A file is moved to the SAFE_TO_DELETE status at the end of a
103          * checkpoint if it was FULLY_PROCESSED at the beginning of the
104          * checkpoint.  All parent BINs of migrated entries have now been
105          * logged.
106          */
107         SAFE_TO_DELETE;
108     }
109 
110     /**
111      * Information about a file being cleaned.
112      */
113     private static class FileInfo {
114         FileStatus status;
115         Set<DatabaseId> dbIds;
116         VLSN firstVlsn = VLSN.NULL_VLSN;
117         VLSN lastVlsn = VLSN.NULL_VLSN;
118 
119         @Override
toString()120         public String toString() {
121             return "status = " + status +
122                    " dbIds = " + dbIds +
123                    " firstVlsn = " + firstVlsn +
124                    " lastVlsn = " + lastVlsn;
125         }
126     }
127 
128     /**
129      * Information about files being cleaned, keyed by file number.  The map is
130      * sorted by file number to clean older files before newer files.
131      */
132     private SortedMap<Long, FileInfo> fileInfoMap;
133 
134     /**
135      * Pending LN info, keyed by original LSN.  These are LNs that could not be
136      * locked, either during processing or during migration.
137      */
138     private Map<Long, LNInfo> pendingLNs;
139 
140     /**
141      * For processed entries with DBs that are pending deletion, we consider
142      * them to be obsolete but we store their DatabaseIds in a set.  Until the
143      * DB deletion is complete, we can't delete the log files containing those
144      * entries.
145      */
146     private Set<DatabaseId> pendingDBs;
147 
148     /**
149      * If during a checkpoint there are no pending LNs or DBs added, we can
150      * move CLEANED files to SAFE_TO_DELETE files at the end of the checkpoint.
151      * This is an optimization that allows deleting files more quickly when
152      * possible. In particular this impacts the checkpoint during environment
153      * close, since no user operations are active during that checkpoint; this
154      * optimization allows us to delete all cleaned files after the final
155      * checkpoint.
156      */
157     private boolean anyPendingDuringCheckpoint;
158 
159     /* Used to prevent repeated probing of the same files. */
160     private final LinkedList<Long> excludeFilesForCorrection;
161     private final static int MAX_EXCLUDE_FILES_FOR_CORRECTION = 5;
162 
FileSelector()163     FileSelector() {
164         fileInfoMap = new TreeMap<Long, FileInfo>();
165         pendingLNs = new HashMap<Long, LNInfo>();
166         pendingDBs = new HashSet<DatabaseId>();
167         excludeFilesForCorrection = new LinkedList<Long>();
168     }
169 
170     /**
171      * Returns the best file that qualifies for cleaning, or null if no file
172      * qualifies.
173      *
174      * @param forceCleaning is true to always select a file, even if its
175      * utilization is above the minimum utilization threshold.
176      *
177      * @param maxBatchFiles is the maximum number of files to be selected at
178      * one time, or zero if there is no limit.
179      *
180      * @return the next file to be cleaned, or null if no file needs cleaning.
181      */
182     synchronized Long
selectFileForCleaning(UtilizationCalculator calculator, SortedMap<Long, FileSummary> fileSummaryMap, boolean forceCleaning, int maxBatchFiles)183         selectFileForCleaning(UtilizationCalculator calculator,
184                               SortedMap<Long, FileSummary> fileSummaryMap,
185                               boolean forceCleaning,
186                               int maxBatchFiles) {
187 
188         /*
189          * Don't accumulate a backlog until the corrected average size is
190          * established.  Otherwise, the files in the backlog may not need
191          * cleaning.
192          */
193         if (!calculator.isCorrectionEstablished()) {
194             maxBatchFiles = 1;
195         }
196 
197         /*
198          * Move files to TO_BE_CLEANED status until we reach the theoretical
199          * minimum utilization threshold or the maximum batch size is reached.
200          */
201         int toBeCleaned = getNumberOfFiles(FileStatus.TO_BE_CLEANED);
202         while ((maxBatchFiles == 0) ||
203                (toBeCleaned < maxBatchFiles)) {
204 
205             final Long fileNum = calculator.getBestFile(
206                 fileSummaryMap, forceCleaning, toBeCleaned > 0 /*isBacklog*/,
207                 false /*isProbe*/, Collections.EMPTY_SET);
208 
209             if (fileNum == null) {
210                 /* Reached theoretical minimum utilization threshold. */
211                 break;
212             }
213 
214             setStatus(fileNum, FileStatus.TO_BE_CLEANED);
215             toBeCleaned += 1;
216         }
217 
218         /*
219          * Select the cheapest file to clean from a copy of the TO_BE_CLEANED
220          * files.  Then move the file to the BEING_CLEANED status.
221          */
222         final Long fileNum = calculator.getCheapestFileToClean
223             (fileSummaryMap, getFiles(FileStatus.TO_BE_CLEANED));
224         if (fileNum == null) {
225             return null;
226         }
227         setStatus(fileNum, FileStatus.BEING_CLEANED);
228         return fileNum;
229     }
230 
231     /**
232      * Returns the best file for correcting utilization, or null if correction
233      * is not needed.  Should only be called when selectFileForCleaning returns
234      * null.
235      */
236     synchronized Long
selectFileForCorrection(UtilizationCalculator calculator, SortedMap<Long, FileSummary> fileSummaryMap)237         selectFileForCorrection(UtilizationCalculator calculator,
238                                 SortedMap<Long, FileSummary> fileSummaryMap) {
239 
240         if (!calculator.shouldPerformProbe(fileSummaryMap.lastKey())) {
241             return null;
242         }
243 
244         final Long fileNum = calculator.getBestFile(
245             fileSummaryMap, false /*forceCleaning*/, false /*isBacklog*/,
246             true /*isProbe*/, new HashSet<Long>(excludeFilesForCorrection));
247 
248         /* Rotate excluded files to include newly selected file. */
249         if (fileNum != null) {
250             excludeFilesForCorrection.addLast(fileNum);
251             while (excludeFilesForCorrection.size() >
252                    MAX_EXCLUDE_FILES_FOR_CORRECTION) {
253                 excludeFilesForCorrection.removeFirst();
254             }
255         }
256 
257         return fileNum;
258     }
259 
260     /**
261      * Returns the number of files having the given status.
262      */
getNumberOfFiles(FileStatus status)263     private synchronized int getNumberOfFiles(FileStatus status) {
264         int count = 0;
265         for (FileInfo info : fileInfoMap.values()) {
266             if (info.status == status) {
267                 count += 1;
268             }
269         }
270         return count;
271     }
272 
273     /**
274      * Returns a sorted set of files having the given status.
275      */
getFiles(FileStatus status)276     private synchronized NavigableSet<Long> getFiles(FileStatus status) {
277         final NavigableSet<Long> set = new TreeSet<Long>();
278         for (Map.Entry<Long, FileInfo> entry : fileInfoMap.entrySet()) {
279             if (entry.getValue().status == status) {
280                 set.add(entry.getKey());
281             }
282         }
283         return set;
284     }
285 
286     /**
287      * Moves a file to a given status, adding the file to the fileInfoMap if
288      * necessary.
289      *
290      * This method must be called while synchronized.
291      */
setStatus(Long fileNum, FileStatus newStatus)292     private FileInfo setStatus(Long fileNum, FileStatus newStatus) {
293         FileInfo info = fileInfoMap.get(fileNum);
294         if (info == null) {
295             info = new FileInfo();
296             fileInfoMap.put(fileNum, info);
297         }
298         info.status = newStatus;
299         return info;
300     }
301 
302     /**
303      * Moves a collection of files to a given status, adding the files to the
304      * fileInfoMap if necessary.
305      *
306      * This method must be called while synchronized.
307      */
setStatus(Collection<Long> files, FileStatus newStatus)308     private void setStatus(Collection<Long> files, FileStatus newStatus) {
309         for (Long fileNum : files) {
310             setStatus(fileNum, newStatus);
311         }
312     }
313 
314     /**
315      * Moves all files with oldStatus to newStatus.
316      *
317      * This method must be called while synchronized.
318      */
setStatus(FileStatus oldStatus, FileStatus newStatus)319     private void setStatus(FileStatus oldStatus, FileStatus newStatus) {
320         for (FileInfo info : fileInfoMap.values()) {
321             if (info.status == oldStatus) {
322                 info.status = newStatus;
323             }
324         }
325     }
326 
327     /**
328      * Asserts that a file has a given status.  Should only be called under an
329      * assertion to avoid the overhead of the method call and synchronization.
330      * Always returns true to enable calling it under an assertion.
331      *
332      * This method must be called while synchronized.
333      */
checkStatus(Long fileNum, FileStatus expectStatus)334     private boolean checkStatus(Long fileNum, FileStatus expectStatus) {
335         final FileInfo info = fileInfoMap.get(fileNum);
336         assert info != null : "Expected " + expectStatus + " but was missing";
337         assert info.status == expectStatus :
338             "Expected " + expectStatus + " but was " + info.status;
339         return true;
340     }
341 
342     /**
343      * Calls checkStatus(Long, FileStatus) for a collection of files.
344      *
345      * This method must be called while synchronized.
346      */
checkStatus(final Collection<Long> files, final FileStatus expectStatus)347     private boolean checkStatus(final Collection<Long> files,
348                                 final FileStatus expectStatus) {
349         for (Long fileNum : files) {
350             checkStatus(fileNum, expectStatus);
351         }
352         return true;
353     }
354 
355     /**
356      * Returns whether the file is in any stage of the cleaning process.
357      */
isFileCleaningInProgress(Long fileNum)358     synchronized boolean isFileCleaningInProgress(Long fileNum) {
359         return fileInfoMap.containsKey(fileNum);
360     }
361 
isFileCleaningInProgress(Collection<Long> files)362     private boolean isFileCleaningInProgress(Collection<Long> files) {
363         for (Long file : files) {
364             if (isFileCleaningInProgress(file)) {
365                 return true;
366             }
367         }
368         return false;
369     }
370 
371     /**
372      * Removes all references to a file.
373      */
removeAllFileReferences(Long fileNum, MemoryBudget budget)374     synchronized void removeAllFileReferences(Long fileNum,
375                                               MemoryBudget budget) {
376         FileInfo info = fileInfoMap.get(fileNum);
377         if (info != null) {
378             adjustMemoryBudget(budget, info.dbIds, null /*newDatabases*/);
379             fileInfoMap.remove(fileNum);
380         }
381     }
382 
383     /**
384      * When file cleaning is aborted, move the file back from BEING_CLEANED to
385      * TO_BE_CLEANED.
386      */
putBackFileForCleaning(Long fileNum)387     synchronized void putBackFileForCleaning(Long fileNum) {
388         assert checkStatus(fileNum, FileStatus.BEING_CLEANED);
389         setStatus(fileNum, FileStatus.TO_BE_CLEANED);
390     }
391 
392     /**
393      * For unit testing.
394      */
injectFileForCleaning(Long fileNum)395     public synchronized void injectFileForCleaning(Long fileNum) {
396         if (!isFileCleaningInProgress(fileNum)) {
397             setStatus(fileNum, FileStatus.TO_BE_CLEANED);
398         }
399     }
400 
401     /**
402      * When cleaning is complete, move the file from the BEING_CLEANED to
403      * CLEANED.
404      */
addCleanedFile(Long fileNum, Set<DatabaseId> databases, VLSN firstVlsn, VLSN lastVlsn, MemoryBudget budget)405     synchronized void addCleanedFile(Long fileNum,
406                                      Set<DatabaseId> databases,
407                                      VLSN firstVlsn,
408                                      VLSN lastVlsn,
409                                      MemoryBudget budget) {
410         assert checkStatus(fileNum, FileStatus.BEING_CLEANED);
411         FileInfo info = setStatus(fileNum, FileStatus.CLEANED);
412         adjustMemoryBudget(budget, info.dbIds, databases);
413         info.dbIds = databases;
414         info.firstVlsn = firstVlsn;
415         info.lastVlsn = lastVlsn;
416     }
417 
418     /**
419      * Returns a read-only copy of TO_BE_CLEANED files that can be accessed
420      * without synchronization.
421      */
getToBeCleanedFiles()422     synchronized Set<Long> getToBeCleanedFiles() {
423         return getFiles(FileStatus.TO_BE_CLEANED);
424     }
425 
426     /**
427      * Returns the number of files waiting TO_BE_CLEANED.
428      */
getBacklog()429     int getBacklog() {
430         return getNumberOfFiles(FileStatus.TO_BE_CLEANED);
431     }
432 
433     /**
434      * Returns a copy of the CLEANED and FULLY_PROCESSED files at the time a
435      * checkpoint starts.
436      */
getFilesAtCheckpointStart()437     synchronized CheckpointStartCleanerState getFilesAtCheckpointStart() {
438 
439         anyPendingDuringCheckpoint = !pendingLNs.isEmpty() ||
440             !pendingDBs.isEmpty();
441 
442         CheckpointStartCleanerState info = new CheckpointStartCleanerState
443             (getFiles(FileStatus.CLEANED),
444              getFiles(FileStatus.FULLY_PROCESSED));
445         return info;
446     }
447 
448     /**
449      * When a checkpoint is complete, move the previously CLEANED and
450      * FULLY_PROCESSED files to the CHECKPOINTED and SAFE_TO_DELETE status.
451      */
452     synchronized void
updateFilesAtCheckpointEnd(CheckpointStartCleanerState info)453         updateFilesAtCheckpointEnd(CheckpointStartCleanerState info) {
454 
455         if (!info.isEmpty()) {
456 
457             Set<Long> previouslyCleanedFiles = info.getCleanedFiles();
458             if (previouslyCleanedFiles != null) {
459                 assert checkStatus(previouslyCleanedFiles, FileStatus.CLEANED);
460                 setStatus(previouslyCleanedFiles, anyPendingDuringCheckpoint ?
461                                                   FileStatus.CHECKPOINTED :
462                                                   FileStatus.SAFE_TO_DELETE);
463             }
464 
465             Set<Long> previouslyProcessedFiles = info.getFullyProcessedFiles();
466             if (previouslyProcessedFiles != null) {
467                 assert checkStatus(previouslyProcessedFiles,
468                                    FileStatus.FULLY_PROCESSED);
469                 setStatus(previouslyProcessedFiles, FileStatus.SAFE_TO_DELETE);
470             }
471 
472             updateProcessedFiles();
473         }
474     }
475 
476     /**
477      * Adds the given LN info to the pending LN set.
478      */
addPendingLN(long originalLsn, LN ln, DatabaseId dbId, byte[] key)479     synchronized boolean addPendingLN(long originalLsn,
480                                       LN ln,
481                                       DatabaseId dbId,
482                                       byte[] key) {
483         assert ln != null;
484 
485         boolean added = pendingLNs.put
486             (Long.valueOf(originalLsn),
487              new LNInfo(ln, dbId, key)) != null;
488 
489         anyPendingDuringCheckpoint = true;
490         return added;
491     }
492 
493     /**
494      * Returns a map of LNInfo for LNs that could not be migrated in a prior
495      * cleaning attempt, or null if no LNs are pending.
496      */
getPendingLNs()497     synchronized Map<Long, LNInfo> getPendingLNs() {
498 
499         if (pendingLNs.size() > 0) {
500             return new HashMap<Long, LNInfo>(pendingLNs);
501         } else {
502             return null;
503         }
504     }
505 
506     /**
507      * Removes the LN for the given LSN from the pending LN set.
508      */
removePendingLN(long originalLsn)509     synchronized void removePendingLN(long originalLsn) {
510 
511         pendingLNs.remove(originalLsn);
512         updateProcessedFiles();
513     }
514 
515     /**
516      * Returns number of LNs pending.
517      */
getPendingLNQueueSize()518     private synchronized int getPendingLNQueueSize() {
519         return pendingLNs.size();
520     }
521 
522     /**
523      * Adds the given DatabaseId to the pending DB set.
524      */
addPendingDB(DatabaseId dbId)525     synchronized boolean addPendingDB(DatabaseId dbId) {
526 
527         boolean added = pendingDBs.add(dbId);
528 
529         anyPendingDuringCheckpoint = true;
530         return added;
531     }
532 
533     /**
534      * Returns an array of DatabaseIds for DBs that were pending deletion in a
535      * prior cleaning attempt, or null if no DBs are pending.
536      */
getPendingDBs()537     synchronized DatabaseId[] getPendingDBs() {
538 
539         if (pendingDBs.size() > 0) {
540             DatabaseId[] dbs = new DatabaseId[pendingDBs.size()];
541             pendingDBs.toArray(dbs);
542             return dbs;
543         } else {
544             return null;
545         }
546     }
547 
548     /**
549      * Removes the DatabaseId from the pending DB set.
550      */
removePendingDB(DatabaseId dbId)551     synchronized void removePendingDB(DatabaseId dbId) {
552 
553         pendingDBs.remove(dbId);
554         updateProcessedFiles();
555     }
556 
557     /**
558      * Returns a copy of the SAFE_TO_DELETE files, or null if there are none.
559      */
copySafeToDeleteFiles()560     synchronized NavigableSet<Long> copySafeToDeleteFiles() {
561         final NavigableSet<Long> set = getFiles(FileStatus.SAFE_TO_DELETE);
562         return (set.size() > 0) ? set : null;
563     }
564 
565     /**
566      * Returns a copy of the databases for a collection of cleaned files, or
567      * null if there are none.
568      */
569     synchronized Set<DatabaseId>
getCleanedDatabases(Collection<Long> fileNums)570         getCleanedDatabases(Collection<Long> fileNums) {
571 
572         HashSet<DatabaseId> set = null;
573 
574         for (Long fileNum : fileNums) {
575             FileInfo info = fileInfoMap.get(fileNum);
576             if (info != null) {
577                 if (set == null) {
578                     set = new HashSet<DatabaseId>(info.dbIds);
579                 } else {
580                     set.addAll(info.dbIds);
581                 }
582             }
583         }
584         return set;
585     }
586 
587     /**
588      * Returns the first VLSN for a cleaned file, which will be VLSN.NULL_VLSN
589      * if the file contained no VLSNs, and null if the information is not
590      * found.
591      */
getFirstVLSN(final Long fileNum)592     public synchronized VLSN getFirstVLSN(final Long fileNum) {
593         final FileInfo info = fileInfoMap.get(fileNum);
594         return (info != null) ? info.firstVlsn : null;
595     }
596 
597     /**
598      * Returns the last VLSN for a cleaned file.
599      */
getLastVLSN(Long fileNum)600     synchronized VLSN getLastVLSN(Long fileNum) {
601         FileInfo info = fileInfoMap.get(fileNum);
602         return (info != null) ? info.lastVlsn : null;
603     }
604 
605     /**
606      * Removes file information after the log file itself has finally been
607      * deleted.
608      */
removeDeletedFile(Long fileNum, MemoryBudget budget)609     synchronized void removeDeletedFile(Long fileNum, MemoryBudget budget) {
610         assert checkStatus(fileNum, FileStatus.SAFE_TO_DELETE);
611         FileInfo info = fileInfoMap.remove(fileNum);
612         if (info != null) {
613             adjustMemoryBudget(budget, info.dbIds, null /*newDatabases*/);
614         }
615     }
616 
617     /**
618      * Update memory budgets when the environment is closed and will never be
619      * accessed again.
620      */
close(MemoryBudget budget)621     synchronized void close(MemoryBudget budget) {
622         for (FileInfo info : fileInfoMap.values()) {
623             adjustMemoryBudget(budget, info.dbIds, null /*newDatabases*/);
624         }
625     }
626 
627     /**
628      * If there are no pending LNs or DBs outstanding, move the CHECKPOINTED
629      * files to FULLY_PROCESSED.  The check for pending LNs/DBs and the copying
630      * of the CHECKPOINTED files must be done atomically in a synchronized
631      * block.  All methods that call this method are synchronized.
632      */
updateProcessedFiles()633     private void updateProcessedFiles() {
634         if (pendingLNs.isEmpty() && pendingDBs.isEmpty()) {
635             setStatus(FileStatus.CHECKPOINTED, FileStatus.FULLY_PROCESSED);
636         }
637     }
638 
639     /**
640      * Adjust the memory budget when an entry is added to or removed from the
641      * cleanedFilesDatabases map.
642      */
adjustMemoryBudget(MemoryBudget budget, Set<DatabaseId> oldDatabases, Set<DatabaseId> newDatabases)643     private void adjustMemoryBudget(MemoryBudget budget,
644                                     Set<DatabaseId> oldDatabases,
645                                     Set<DatabaseId> newDatabases) {
646         long adjustMem = 0;
647         if (oldDatabases != null) {
648             adjustMem -= getCleanedFilesDatabaseEntrySize(oldDatabases);
649         }
650         if (newDatabases != null) {
651             adjustMem += getCleanedFilesDatabaseEntrySize(newDatabases);
652         }
653         budget.updateAdminMemoryUsage(adjustMem);
654     }
655 
656     /**
657      * Returns the size of a HashMap entry that contains the given set of
658      * DatabaseIds.  We don't count the DatabaseId size because it is likely
659      * that it is also stored (and budgeted) in the DatabaseImpl.
660      */
getCleanedFilesDatabaseEntrySize(Set<DatabaseId> databases)661     private long getCleanedFilesDatabaseEntrySize(Set<DatabaseId> databases) {
662         return MemoryBudget.HASHMAP_ENTRY_OVERHEAD +
663                MemoryBudget.HASHSET_OVERHEAD +
664                (databases.size() * MemoryBudget.HASHSET_ENTRY_OVERHEAD);
665     }
666 
667     /**
668      * Loads file selection stats.
669      */
loadStats()670     synchronized StatGroup loadStats() {
671         StatGroup stats = new StatGroup(CleanerStatDefinition.FS_GROUP_NAME,
672                                         CleanerStatDefinition.FS_GROUP_DESC);
673         new IntStat(stats, CLEANER_BACKLOG, getBacklog());
674         new IntStat(stats, CLEANER_FILE_DELETION_BACKLOG,
675                     getNumberOfFiles(FileStatus.SAFE_TO_DELETE));
676         new IntStat(stats, CLEANER_PENDING_LN_QUEUE_SIZE,
677                     getPendingLNQueueSize());
678 
679         return stats;
680     }
681 
682     /**
683      * Holds copy of all checkpoint-dependent cleaner state.
684      */
685     public static class CheckpointStartCleanerState {
686 
687         /* A snapshot of the cleaner collections at the checkpoint start. */
688         private Set<Long> cleanedFiles;
689         private Set<Long> fullyProcessedFiles;
690 
CheckpointStartCleanerState(Set<Long> cleanedFiles, Set<Long> fullyProcessedFiles)691         private CheckpointStartCleanerState(Set<Long> cleanedFiles,
692                                             Set<Long> fullyProcessedFiles) {
693 
694             /*
695              * Save snapshots of the collections of various files at the
696              * beginning of the checkpoint.
697              */
698             this.cleanedFiles = cleanedFiles;
699             this.fullyProcessedFiles = fullyProcessedFiles;
700         }
701 
isEmpty()702         public boolean isEmpty() {
703             return ((cleanedFiles.size() == 0) &&
704                     (fullyProcessedFiles.size() == 0));
705         }
706 
getCleanedFiles()707         public Set<Long> getCleanedFiles() {
708             return cleanedFiles;
709         }
710 
getFullyProcessedFiles()711         public Set<Long> getFullyProcessedFiles() {
712             return fullyProcessedFiles;
713         }
714     }
715 
716     @Override
toString()717     public synchronized String toString() {
718         return "files = " + fileInfoMap +
719                " pendingLNs = " + pendingLNs +
720                " pendingDBs = " + pendingDBs +
721                " anyPendingDuringCheckpoint = " + anyPendingDuringCheckpoint;
722     }
723 }
724