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