View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements. See the NOTICE file distributed with this
6    * work for additional information regarding copyright ownership. The ASF
7    * licenses this file to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.master.handler;
20  
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.util.Map;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  import org.apache.hadoop.hbase.HRegionInfo;
29  import org.apache.hadoop.hbase.RegionLoad;
30  import org.apache.hadoop.hbase.ServerLoad;
31  import org.apache.hadoop.hbase.ServerName;
32  import org.apache.hadoop.hbase.exceptions.RegionOpeningException;
33  import org.apache.hadoop.hbase.executor.EventHandler;
34  import org.apache.hadoop.hbase.executor.EventType;
35  import org.apache.hadoop.hbase.master.CatalogJanitor;
36  import org.apache.hadoop.hbase.master.MasterServices;
37  import org.apache.hadoop.hbase.master.RegionPlan;
38  import org.apache.hadoop.hbase.master.RegionStates;
39  import org.apache.hadoop.hbase.master.ServerManager;
40  import org.apache.hadoop.hbase.security.User;
41  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
42  
43  /**
44   * Handles MERGE regions request on master: move the regions together(on the
45   * same regionserver) and send MERGE RPC to regionserver.
46   *
47   * NOTE:The real merge is executed on the regionserver
48   *
49   */
50  @InterfaceAudience.Private
51  public class DispatchMergingRegionHandler extends EventHandler {
52    private static final Log LOG = LogFactory.getLog(DispatchMergingRegionHandler.class);
53    private final MasterServices masterServices;
54    private final CatalogJanitor catalogJanitor;
55    private HRegionInfo region_a;
56    private HRegionInfo region_b;
57    private final boolean forcible;
58    private final int timeout;
59    private final User user;
60  
61    public DispatchMergingRegionHandler(final MasterServices services,
62        final CatalogJanitor catalogJanitor, final HRegionInfo region_a,
63        final HRegionInfo region_b, final boolean forcible, final User user) {
64      super(services, EventType.C_M_MERGE_REGION);
65      this.masterServices = services;
66      this.catalogJanitor = catalogJanitor;
67      this.region_a = region_a;
68      this.region_b = region_b;
69      this.forcible = forcible;
70      this.user = user;
71      this.timeout = server.getConfiguration().getInt(
72          "hbase.master.regionmerge.timeout", 120 * 1000);
73    }
74  
75    @Override
76    public void process() throws IOException {
77      boolean regionAHasMergeQualifier = !catalogJanitor.cleanMergeQualifier(region_a);
78      if (regionAHasMergeQualifier
79          || !catalogJanitor.cleanMergeQualifier(region_b)) {
80        LOG.info("Skip merging regions " + region_a.getRegionNameAsString()
81            + ", " + region_b.getRegionNameAsString() + ", because region "
82            + (regionAHasMergeQualifier ? region_a.getEncodedName() : region_b
83                .getEncodedName()) + " has merge qualifier");
84        return;
85      }
86  
87      RegionStates regionStates = masterServices.getAssignmentManager()
88          .getRegionStates();
89      ServerName region_a_location = regionStates.getRegionServerOfRegion(region_a);
90      ServerName region_b_location = regionStates.getRegionServerOfRegion(region_b);
91      if (region_a_location == null || region_b_location == null) {
92        LOG.info("Skip merging regions " + region_a.getRegionNameAsString()
93            + ", " + region_b.getRegionNameAsString() + ", because region "
94            + (region_a_location == null ? region_a.getEncodedName() : region_b
95                .getEncodedName()) + " is not online now");
96        return;
97      }
98      long startTime = EnvironmentEdgeManager.currentTime();
99      boolean onSameRS = region_a_location.equals(region_b_location);
100 
101     // Make sure regions are on the same regionserver before send merge
102     // regions request to regionserver
103     if (!onSameRS) {
104       // Move region_b to region a's location, switch region_a and region_b if
105       // region_a's load lower than region_b's, so we will always move lower
106       // load region
107       RegionLoad loadOfRegionA = getRegionLoad(region_a_location, region_a);
108       RegionLoad loadOfRegionB = getRegionLoad(region_b_location, region_b);
109       if (loadOfRegionA != null && loadOfRegionB != null
110           && loadOfRegionA.getRequestsCount() < loadOfRegionB
111               .getRequestsCount()) {
112         // switch region_a and region_b
113         HRegionInfo tmpRegion = this.region_a;
114         this.region_a = this.region_b;
115         this.region_b = tmpRegion;
116         ServerName tmpLocation = region_a_location;
117         region_a_location = region_b_location;
118         region_b_location = tmpLocation;
119       }
120 
121       RegionPlan regionPlan = new RegionPlan(region_b, region_b_location,
122           region_a_location);
123       LOG.info("Moving regions to same server for merge: " + regionPlan.toString());
124       masterServices.getAssignmentManager().balance(regionPlan);
125       while (!masterServices.isStopped()) {
126         try {
127           Thread.sleep(20);
128           // Make sure check RIT first, then get region location, otherwise
129           // we would make a wrong result if region is online between getting
130           // region location and checking RIT
131           boolean isRIT = regionStates.isRegionInTransition(region_b);
132           region_b_location = masterServices.getAssignmentManager()
133               .getRegionStates().getRegionServerOfRegion(region_b);
134           onSameRS = region_a_location.equals(region_b_location);
135           if (onSameRS || !isRIT) {
136             // Regions are on the same RS, or region_b is not in
137             // RegionInTransition any more
138             break;
139           }
140           if ((EnvironmentEdgeManager.currentTime() - startTime) > timeout) break;
141         } catch (InterruptedException e) {
142           InterruptedIOException iioe = new InterruptedIOException();
143           iioe.initCause(e);
144           throw iioe;
145         }
146       }
147     }
148 
149     if (onSameRS) {
150       startTime = EnvironmentEdgeManager.currentTime();
151       while (!masterServices.isStopped()) {
152         try {
153           masterServices.getServerManager().sendRegionsMerge(region_a_location,
154               region_a, region_b, forcible, user);
155           LOG.info("Sent merge to server " + region_a_location + " for region " +
156             region_a.getEncodedName() + "," + region_b.getEncodedName() + ", focible=" + forcible);
157           break;
158         } catch (RegionOpeningException roe) {
159           if ((EnvironmentEdgeManager.currentTime() - startTime) > timeout) {
160             LOG.warn("Failed sending merge to " + region_a_location + " after " + timeout + "ms",
161               roe);
162             break;
163           }
164           // Do a retry since region should be online on RS immediately
165         } catch (IOException ie) {
166           LOG.warn("Failed sending merge to " + region_a_location + " for region " +
167             region_a.getEncodedName() + "," + region_b.getEncodedName() + ", focible=" + forcible,
168             ie);
169           break;
170         }
171       }
172     } else {
173       LOG.info("Cancel merging regions " + region_a.getRegionNameAsString()
174           + ", " + region_b.getRegionNameAsString()
175           + ", because can't move them together after "
176           + (EnvironmentEdgeManager.currentTime() - startTime) + "ms");
177     }
178   }
179 
180   private RegionLoad getRegionLoad(ServerName sn, HRegionInfo hri) {
181     ServerManager serverManager =  masterServices.getServerManager();
182     ServerLoad load = serverManager.getLoad(sn);
183     if (load != null) {
184       Map<byte[], RegionLoad> regionsLoad = load.getRegionsLoad();
185       if (regionsLoad != null) {
186         return regionsLoad.get(hri.getRegionName());
187       }
188     }
189     return null;
190   }
191 }