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 org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.hadoop.conf.Configuration;
25  import org.apache.hadoop.hbase.HConstants;
26  import org.apache.hadoop.hbase.HRegionInfo;
27  import org.apache.hadoop.hbase.RemoteExceptionHandler;
28  import org.apache.hadoop.hbase.client.HTable;
29  import org.apache.hadoop.hbase.client.Put;
30  import org.apache.hadoop.hbase.util.Writables;
31  import org.apache.hadoop.util.StringUtils;
32  
33  import java.io.IOException;
34  import java.util.HashSet;
35  import java.util.concurrent.BlockingQueue;
36  import java.util.concurrent.LinkedBlockingQueue;
37  import java.util.concurrent.TimeUnit;
38  import java.util.concurrent.locks.ReentrantLock;
39  
40  /**
41   * Compact region on request and then run split if appropriate
42   */
43  class CompactSplitThread extends Thread {
44    static final Log LOG = LogFactory.getLog(CompactSplitThread.class);
45  
46    private HTable root = null;
47    private HTable meta = null;
48    private final long frequency;
49    private final ReentrantLock lock = new ReentrantLock();
50  
51    private final HRegionServer server;
52    private final Configuration conf;
53  
54    private final BlockingQueue<HRegion> compactionQueue =
55      new LinkedBlockingQueue<HRegion>();
56  
57    private final HashSet<HRegion> regionsInQueue = new HashSet<HRegion>();
58  
59    /** @param server */
60    public CompactSplitThread(HRegionServer server) {
61      super();
62      this.server = server;
63      this.conf = server.conf;
64      this.frequency =
65        conf.getLong("hbase.regionserver.thread.splitcompactcheckfrequency",
66        20 * 1000);
67    }
68  
69    @Override
70    public void run() {
71      int count = 0;
72      while (!this.server.isStopRequested()) {
73        HRegion r = null;
74        try {
75          r = compactionQueue.poll(this.frequency, TimeUnit.MILLISECONDS);
76          if (r != null && !this.server.isStopRequested()) {
77            synchronized (regionsInQueue) {
78              regionsInQueue.remove(r);
79            }
80            lock.lock();
81            try {
82              // Don't interrupt us while we are working
83              byte [] midKey = r.compactStores();
84              if (midKey != null && !this.server.isStopRequested()) {
85                split(r, midKey);
86              }
87            } finally {
88              lock.unlock();
89            }
90          }
91        } catch (InterruptedException ex) {
92          continue;
93        } catch (IOException ex) {
94          LOG.error("Compaction/Split failed for region " +
95              r.getRegionNameAsString(),
96            RemoteExceptionHandler.checkIOException(ex));
97          if (!server.checkFileSystem()) {
98            break;
99          }
100       } catch (Exception ex) {
101         LOG.error("Compaction failed" +
102             (r != null ? (" for region " + r.getRegionNameAsString()) : ""),
103             ex);
104         if (!server.checkFileSystem()) {
105           break;
106         }
107       }
108     }
109     regionsInQueue.clear();
110     compactionQueue.clear();
111     LOG.info(getName() + " exiting");
112   }
113 
114   /**
115    * @param r HRegion store belongs to
116    * @param why Why compaction requested -- used in debug messages
117    */
118   public synchronized void compactionRequested(final HRegion r,
119       final String why) {
120     compactionRequested(r, false, why);
121   }
122 
123   /**
124    * @param r HRegion store belongs to
125    * @param force Whether next compaction should be major
126    * @param why Why compaction requested -- used in debug messages
127    */
128   public synchronized void compactionRequested(final HRegion r,
129       final boolean force, final String why) {
130     if (this.server.stopRequested.get()) {
131       return;
132     }
133     r.setForceMajorCompaction(force);
134     if (LOG.isDebugEnabled()) {
135       LOG.debug("Compaction " + (force? "(major) ": "") +
136         "requested for region " + r.getRegionNameAsString() +
137         (why != null && !why.isEmpty()? " because: " + why: ""));
138     }
139     synchronized (regionsInQueue) {
140       if (!regionsInQueue.contains(r)) {
141         compactionQueue.add(r);
142         regionsInQueue.add(r);
143       }
144     }
145   }
146 
147   private void split(final HRegion region, final byte [] midKey)
148   throws IOException {
149     final HRegionInfo oldRegionInfo = region.getRegionInfo();
150     final long startTime = System.currentTimeMillis();
151     final HRegion[] newRegions = region.splitRegion(midKey);
152     if (newRegions == null) {
153       // Didn't need to be split
154       return;
155     }
156 
157     // When a region is split, the META table needs to updated if we're
158     // splitting a 'normal' region, and the ROOT table needs to be
159     // updated if we are splitting a META region.
160     HTable t = null;
161     if (region.getRegionInfo().isMetaTable()) {
162       // We need to update the root region
163       if (this.root == null) {
164         this.root = new HTable(conf, HConstants.ROOT_TABLE_NAME);
165       }
166       t = root;
167     } else {
168       // For normal regions we need to update the meta region
169       if (meta == null) {
170         meta = new HTable(conf, HConstants.META_TABLE_NAME);
171       }
172       t = meta;
173     }
174 
175     // Mark old region as offline and split in META.
176     // NOTE: there is no need for retry logic here. HTable does it for us.
177     oldRegionInfo.setOffline(true);
178     oldRegionInfo.setSplit(true);
179     // Inform the HRegionServer that the parent HRegion is no-longer online.
180     this.server.removeFromOnlineRegions(oldRegionInfo);
181 
182     Put put = new Put(oldRegionInfo.getRegionName());
183     put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
184       Writables.getBytes(oldRegionInfo));
185     put.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
186         HConstants.EMPTY_BYTE_ARRAY);
187     put.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
188         HConstants.EMPTY_BYTE_ARRAY);
189     put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER,
190       Writables.getBytes(newRegions[0].getRegionInfo()));
191     put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER,
192       Writables.getBytes(newRegions[1].getRegionInfo()));
193     t.put(put);
194 
195     // If we crash here, then the daughters will not be added and we'll have
196     // and offlined parent but no daughters to take up the slack.  hbase-2244
197     // adds fixup to the metascanners.
198 
199     // Add new regions to META
200     for (int i = 0; i < newRegions.length; i++) {
201       put = new Put(newRegions[i].getRegionName());
202       put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
203           Writables.getBytes(newRegions[i].getRegionInfo()));
204       t.put(put);
205     }
206 
207     // If we crash here, the master will not know of the new daughters and they
208     // will not be assigned.  The metascanner when it runs will notice and take
209     // care of assigning the new daughters.
210 
211     // Now tell the master about the new regions
212     server.reportSplit(oldRegionInfo, newRegions[0].getRegionInfo(),
213       newRegions[1].getRegionInfo());
214 
215     LOG.info("region split, META updated, and report to master all" +
216       " successful. Old region=" + oldRegionInfo.toString() +
217       ", new regions: " + newRegions[0].toString() + ", " +
218       newRegions[1].toString() + ". Split took " +
219       StringUtils.formatTimeDiff(System.currentTimeMillis(), startTime));
220   }
221 
222   /**
223    * Only interrupt once it's done with a run through the work loop.
224    */
225   void interruptIfNecessary() {
226     if (lock.tryLock()) {
227       this.interrupt();
228     }
229   }
230 
231   /**
232    * Returns the current size of the queue containing regions that are
233    * processed.
234    *
235    * @return The current size of the regions queue.
236    */
237   public int getCompactionQueueSize() {
238     return compactionQueue.size();
239   }
240 }