View Javadoc

1   /*
2    * Copyright (c) 2004-2005, University Health Network.  All rights reserved. Distributed under the BSD 
3    * license (see http://opensource.org/licenses/bsd-license.php).
4    *  
5    * Created on 4-Jan-2005
6    */
7   package ca.uhn.cache.internal.impl;
8   
9   import org.apache.commons.logging.Log;
10  import org.apache.commons.logging.LogFactory;
11  
12  import ca.uhn.cache.internal.ICacheCleaner;
13  import ca.uhn.cache.internal.IChunk;
14  import ca.uhn.cache.internal.IChunkIterator;
15  import ca.uhn.cache.internal.IChunkPurger;
16  import ca.uhn.cache.internal.IChunkStore;
17  import ca.uhn.cache.internal.IQueryResultStore;
18  import ca.uhn.cache.internal.exception.QueryResultStoreException;
19  
20  /***
21   * Default implementation of ICacheCleaner.  
22   * 
23   * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
24   * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:54:03 $ by $Author: bryan_tripp $
25   */
26  public class CacheCleaner implements ICacheCleaner {
27  
28      private static final Log ourLog = LogFactory.getLog( CacheCleaner.class );
29  
30      private IChunkStore myChunkStore;
31      private IChunkPurger myPurger;
32      private int myTargetSize;
33      private IQueryResultStore myItemStore;
34      private float myAgeDecrement;
35      
36      /***
37       * @param theChunkStore chunk store cleaned by this cleaner 
38       * @param thePurger a specialized interface to the same store   
39       * @param theTargetSize as in getTargetSize()
40       * @param theItemStore query result store that corresponds to the chunk store  
41       * @param theAgeDecrement unused chunks are evicted in blocks according to the age of last usage, 
42       *      and this param is the size of each block as a proportion of greatest age in the cache 
43       *      (.01 = 1%, i.e. chunks divided into 100 blocks)  
44       */
45      public CacheCleaner(IChunkStore theChunkStore, IChunkPurger thePurger, int theTargetSize, 
46              IQueryResultStore theItemStore, float theAgeDecrement) {
47          myChunkStore = theChunkStore;
48          myPurger = thePurger;
49          myTargetSize = theTargetSize;
50          myItemStore = theItemStore;
51          
52          if (theAgeDecrement > 1 || theAgeDecrement < 0) {
53              throw new IllegalArgumentException("theAgeDecrement must be between 0 and 1");
54          }
55          myAgeDecrement = theAgeDecrement;
56      }
57  
58      /*** 
59       * @see ca.uhn.cache.internal.ICacheCleaner#getTargetSize()
60       */
61      public long getTargetSize() {
62          return myTargetSize;
63      }
64  
65      /*** 
66       * @see ca.uhn.cache.internal.ICacheCleaner#evictStaleChunks()
67       */
68      public void evictStaleChunks() {
69          IChunkIterator stale = myPurger.getStaleChunks(); 
70          evict(stale);
71      }
72      
73      /*** 
74       * @see ca.uhn.cache.internal.ICacheCleaner#evictUnusedChunks()
75       */
76      public void evictUnusedChunks() {
77          long now = System.currentTimeMillis();
78          long cutoffAge = now - myPurger.getLowestVogueness();
79          
80          int decrement = Math.round( ((float) cutoffAge) * myAgeDecrement);  
81  
82          //clean up blocks of ages until cache small enough
83          while (myPurger.getNumCachedItems() > getTargetSize()) {
84              cutoffAge = cutoffAge - decrement;         
85              IChunkIterator unused = myPurger.getUnusedChunks(now - cutoffAge);
86              evict(unused);
87          }
88      }
89      
90      private void evict(IChunkIterator theIterator) {
91          IChunk currentChunk = null;
92          try {
93              while (theIterator.hasNext()) {
94                  currentChunk = theIterator.next();
95                  evict(currentChunk);
96              }
97          } catch (QueryResultStoreException e) {
98              ourLog.error("Error evicting chunk " + currentChunk, e);
99              theIterator.close();
100         }        
101     }
102     
103     //order is important: prevents chunk store from claiming data that aren't in item store, 
104     //in the event of a crash
105     private void evict(IChunk theChunk) throws QueryResultStoreException {
106         myChunkStore.remove(theChunk.getBoundaries());
107         myItemStore.delete(theChunk.getBoundaries());
108     }
109     
110 }