View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.mavibot.btree;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.TreeSet;
29  
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  
34  /**
35   * A class used for reclaiming the copied pages.
36   *
37   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
38   */
39  public class PageReclaimer
40  {
41      /** the record manager */
42      private RecordManager rm;
43  
44      /** The LoggerFactory used by this class */
45      protected static final Logger LOG = LoggerFactory.getLogger( PageReclaimer.class );
46  
47      /** a flag to detect the running state */
48      private boolean running = false;
49      
50      /**
51       * Creates a new instance of PageReclaimer.
52       *
53       * @param rm the record manager
54       */
55      public PageReclaimer( RecordManager rm )
56      {
57          this.rm = rm;
58      }    
59  
60      
61      /**
62       * relcaims the copied pages
63       */
64      /* no qualifier */ void reclaim()
65      {
66          //System.out.println( "reclaiming pages" );
67          try
68          {
69              if ( running )
70              {
71                  return;
72              }
73              
74              running = true;
75              
76              Set<String> managed = rm.getManagedTrees();
77  
78              for ( String name : managed )
79              {
80                  PersistedBTree tree = ( PersistedBTree ) rm.getManagedTree( name );
81  
82                  long latestRev = tree.getRevision();
83                  
84                  Set<Long> inUseRevisions = new TreeSet<Long>();
85                  
86                  // the tree might have been removed
87                  if ( tree != null )
88                  {
89                      Iterator<ReadTransaction> txnItr = tree.getReadTransactions().iterator();
90                      while ( txnItr.hasNext() )
91                      {
92                          inUseRevisions.add( txnItr.next().getRevision() );
93                      }
94                  }
95  
96                  List<RevisionOffset> copiedRevisions = getRevisions( name );
97  
98                  // the revision last removed from copiedPage BTree
99                  long lastRemovedRev = -1;
100 
101                 List<Long> freeList = new ArrayList<Long>();
102                 
103                 for ( RevisionOffset ro : copiedRevisions )
104                 {
105                     long rv = ro.getRevision();
106                     if ( inUseRevisions.contains( rv ) )
107                     {
108                         //System.out.println( "Revision " + rv + " of BTree " + name + " is in use, not reclaiming pages" );
109                         break;
110                     }
111 
112                     long[] offsets = ro.getOffsets();
113 
114                     //System.out.println( "Reclaiming " + Arrays.toString( offsets ) + "( " + offsets.length + " ) pages of the revision " + rv + " of BTree " + name );
115 
116                     for( long l : offsets )
117                     {
118                         freeList.add( l );
119                     }
120 
121                     RevisionName key = new RevisionName( rv, name );
122                     
123                     //System.out.println( "delete cpb key " + key );
124                     rm.copiedPageBtree.delete( key );
125                     lastRemovedRev = rv;
126                 }
127 
128                 // no new txn is needed for the operations on BoB
129                 // and also no need to traverse BoB if the tree is a sub-btree
130                 if ( ( lastRemovedRev != -1 ) && !tree.isAllowDuplicates() )
131                 {
132                     // we SHOULD NOT delete the latest revision from BoB
133                     NameRevision nr = new NameRevision( name, latestRev );
134                     TupleCursor<NameRevision, Long> cursor = rm.btreeOfBtrees.browseFrom( nr );
135                     
136                     List<Long> btreeHeaderOffsets = new ArrayList<Long>();
137                     
138                     while ( cursor.hasPrev() )
139                     {
140                         Tuple<NameRevision, Long> t = cursor.prev();
141                         //System.out.println( "deleting BoB rev " + t.getKey()  + " latest rev " + latestRev );
142                         rm.btreeOfBtrees.delete( t.getKey() );
143                         btreeHeaderOffsets.add( t.value );
144                     }
145 
146                     cursor.close();
147                     
148                     for( Long l : btreeHeaderOffsets )
149                     {
150                         // the offset may have already been present while
151                         // clearing CPB so skip it here, otherwise it will result in OOM
152                         // due to the attempt to free and already freed page
153                         if(freeList.contains( l ))
154                         {
155                             //System.out.println( "bob duplicate offset " + l );
156                             continue;
157                         }
158 
159                         freeList.add( l );
160                     }
161                 }
162                 
163                 for( Long offset : freeList )
164                 {
165                     PageIO[] pageIos = rm.readPageIOs( offset, -1L );
166                     
167                     for ( PageIO pageIo : pageIos )
168                     {
169                         rm.free( pageIo );
170                     }
171                 }
172 
173             }
174 
175             running = false;
176         }
177         catch ( Exception e )
178         {
179         	running = false;
180         	rm.rollback();
181         	LOG.warn( "Errors while reclaiming", e );
182         	throw new RuntimeException( e );
183         }
184     }
185 
186 
187     /**
188      * gets a list of all the copied pages of a given B-Tree.
189      * 
190      * @param name the name of the B-Tree
191      * @return list of RevisionOffset
192      * @throws Exception
193      */
194     private List<RevisionOffset> getRevisions( String name ) throws Exception
195     {
196         TupleCursor<RevisionName, long[]> cursor = rm.copiedPageBtree.browse();
197 
198         List<RevisionOffset> lst = new ArrayList<RevisionOffset>();
199 
200         while ( cursor.hasNext() )
201         {
202             Tuple<RevisionName, long[]> t = cursor.next();
203             RevisionName rn = t.getKey();
204             if ( name.equals( rn.getName() ) )
205             {
206                 //System.out.println( t.getValue() );
207                 lst.add( new RevisionOffset( rn.getRevision(), t.getValue() ) );
208             }
209         }
210 
211         cursor.close();
212         
213         return lst;
214     }
215 }