View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import java.io.IOException;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.locks.ReentrantLock;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.hbase.RemoteExceptionHandler;
30  import org.apache.hadoop.util.StringUtils;
31  
32  /**
33   * Compact region on request and then run split if appropriate
34   */
35  public class CompactSplitThread extends Thread implements CompactionRequestor {
36    static final Log LOG = LogFactory.getLog(CompactSplitThread.class);
37    private final long frequency;
38    private final ReentrantLock lock = new ReentrantLock();
39  
40    private final HRegionServer server;
41    private final Configuration conf;
42  
43    private final PriorityCompactionQueue compactionQueue =
44      new PriorityCompactionQueue();
45  
46    /* The default priority for user-specified compaction requests.
47     * The user gets top priority unless we have blocking compactions. (Pri <= 0)
48     */
49    public static final int PRIORITY_USER = 1;
50  
51    /**
52     * Splitting should not take place if the total number of regions exceed this.
53     * This is not a hard limit to the number of regions but it is a guideline to
54     * stop splitting after number of online regions is greater than this.
55     */
56    private int regionSplitLimit;
57  
58    /** @param server */
59    public CompactSplitThread(HRegionServer server) {
60      super();
61      this.server = server;
62      this.conf = server.getConfiguration();
63      this.regionSplitLimit = conf.getInt("hbase.regionserver.regionSplitLimit",
64          Integer.MAX_VALUE);
65      this.frequency =
66        conf.getLong("hbase.regionserver.thread.splitcompactcheckfrequency",
67        20 * 1000);
68    }
69  
70    @Override
71    public void run() {
72      while (!this.server.isStopped()) {
73        HRegion r = null;
74        try {
75          r = compactionQueue.poll(this.frequency, TimeUnit.MILLISECONDS);
76          if (r != null) {
77            lock.lock();
78            try {
79              if(!this.server.isStopped()) {
80                // Don't interrupt us while we are working
81                byte [] midKey = r.compactStores();
82                if (r.getLastCompactInfo() != null) {  // compaction aborted?
83                  this.server.getMetrics().addCompaction(r.getLastCompactInfo());
84                }
85                if (shouldSplitRegion() && midKey != null &&
86                    !this.server.isStopped()) {
87                  split(r, midKey);
88                }
89              }
90            } finally {
91              lock.unlock();
92            }
93          }
94        } catch (InterruptedException ex) {
95          continue;
96        } catch (IOException ex) {
97          LOG.error("Compaction/Split failed for region " +
98              r.getRegionNameAsString(),
99            RemoteExceptionHandler.checkIOException(ex));
100         if (!server.checkFileSystem()) {
101           break;
102         }
103       } catch (Exception ex) {
104         LOG.error("Compaction failed" +
105             (r != null ? (" for region " + r.getRegionNameAsString()) : ""),
106             ex);
107         if (!server.checkFileSystem()) {
108           break;
109         }
110       }
111     }
112     compactionQueue.clear();
113     LOG.info(getName() + " exiting");
114   }
115 
116   public synchronized void requestCompaction(final HRegion r,
117       final String why) {
118     requestCompaction(r, false, why, r.getCompactPriority());
119   }
120 
121   public synchronized void requestCompaction(final HRegion r,
122       final String why, int p) {
123     requestCompaction(r, false, why, p);
124   }
125 
126   /**
127    * @param r HRegion store belongs to
128    * @param force Whether next compaction should be major
129    * @param why Why compaction requested -- used in debug messages
130    */
131   public synchronized void requestCompaction(final HRegion r,
132       final boolean force, final String why, int priority) {
133     if (this.server.isStopped()) {
134       return;
135     }
136     // tell the region to major-compact (and don't downgrade it)
137     if (force) {
138       r.setForceMajorCompaction(force);
139     }
140     if (compactionQueue.add(r, priority) && LOG.isDebugEnabled()) {
141       LOG.debug("Compaction " + (force? "(major) ": "") +
142         "requested for " + r.getRegionNameAsString() +
143         (why != null && !why.isEmpty()? " because " + why: "") +
144         "; priority=" + priority + ", compaction queue size=" + compactionQueue.size());
145     }
146   }
147 
148   private void split(final HRegion parent, final byte [] midKey)
149   throws IOException {
150     final long startTime = System.currentTimeMillis();
151     SplitTransaction st = new SplitTransaction(parent, midKey);
152     // If prepare does not return true, for some reason -- logged inside in
153     // the prepare call -- we are not ready to split just now.  Just return.
154     if (!st.prepare()) return;
155     try {
156       st.execute(this.server, this.server);
157     } catch (IOException ioe) {
158       try {
159         LOG.info("Running rollback of failed split of " +
160           parent.getRegionNameAsString() + "; " + ioe.getMessage());
161         st.rollback(this.server, this.server);
162         LOG.info("Successful rollback of failed split of " +
163           parent.getRegionNameAsString());
164       } catch (Exception ee) {
165         // If failed rollback, kill this server to avoid having a hole in table.
166         LOG.info("Failed rollback of failed split of " +
167           parent.getRegionNameAsString() + " -- aborting server", ee);
168         this.server.abort("Failed split");
169       }
170       return;
171     }
172 
173     // Now tell the master about the new regions.  If we fail here, its OK.
174     // Basescanner will do fix up.  And reporting split to master is going away.
175     // TODO: Verify this still holds in new master rewrite.
176     this.server.reportSplit(parent.getRegionInfo(), st.getFirstDaughter(),
177       st.getSecondDaughter());
178     LOG.info("Region split, META updated, and report to master. Parent=" +
179       parent.getRegionInfo().getRegionNameAsString() + ", new regions: " +
180       st.getFirstDaughter().getRegionNameAsString() + ", " +
181       st.getSecondDaughter().getRegionNameAsString() + ". Split took " +
182       StringUtils.formatTimeDiff(System.currentTimeMillis(), startTime));
183   }
184 
185   /**
186    * Only interrupt once it's done with a run through the work loop.
187    */
188   void interruptIfNecessary() {
189     if (lock.tryLock()) {
190       try {
191         this.interrupt();
192       } finally {
193         lock.unlock();
194       }
195     }
196   }
197 
198   /**
199    * Returns the current size of the queue containing regions that are
200    * processed.
201    *
202    * @return The current size of the regions queue.
203    */
204   public int getCompactionQueueSize() {
205     return compactionQueue.size();
206   }
207 
208   private boolean shouldSplitRegion() {
209     return (regionSplitLimit > server.getNumberOfOnlineRegions());
210   }
211 
212   /**
213    * @return the regionSplitLimit
214    */
215   public int getRegionSplitLimit() {
216     return this.regionSplitLimit;
217   }
218 }