1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.master.handler;
20
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.NavigableMap;
27 import java.util.TreeMap;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.hbase.CoordinatedStateException;
32 import org.apache.hadoop.hbase.HRegionInfo;
33 import org.apache.hadoop.hbase.HRegionLocation;
34 import org.apache.hadoop.hbase.HTableDescriptor;
35 import org.apache.hadoop.hbase.InvalidFamilyOperationException;
36 import org.apache.hadoop.hbase.MetaTableAccessor;
37 import org.apache.hadoop.hbase.Server;
38 import org.apache.hadoop.hbase.ServerName;
39 import org.apache.hadoop.hbase.TableName;
40 import org.apache.hadoop.hbase.TableNotDisabledException;
41 import org.apache.hadoop.hbase.client.Connection;
42 import org.apache.hadoop.hbase.client.RegionLocator;
43 import org.apache.hadoop.hbase.executor.EventHandler;
44 import org.apache.hadoop.hbase.executor.EventType;
45 import org.apache.hadoop.hbase.master.BulkReOpen;
46 import org.apache.hadoop.hbase.master.MasterServices;
47 import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
48 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
49 import org.apache.hadoop.hbase.util.Bytes;
50 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
51 import org.apache.hadoop.hbase.classification.InterfaceAudience;
52
53 import com.google.common.collect.Lists;
54 import com.google.common.collect.Maps;
55
56
57
58
59
60
61
62
63 @InterfaceAudience.Private
64 public abstract class TableEventHandler extends EventHandler {
65 private static final Log LOG = LogFactory.getLog(TableEventHandler.class);
66 protected final MasterServices masterServices;
67 protected final TableName tableName;
68 protected TableLock tableLock;
69 private boolean isPrepareCalled = false;
70
71 public TableEventHandler(EventType eventType, TableName tableName, Server server,
72 MasterServices masterServices) {
73 super(server, eventType);
74 this.masterServices = masterServices;
75 this.tableName = tableName;
76 }
77
78 @Override
79 public TableEventHandler prepare() throws IOException {
80
81 this.tableLock = masterServices.getTableLockManager()
82 .writeLock(tableName, eventType.toString());
83 this.tableLock.acquire();
84 boolean success = false;
85 try {
86 try {
87 this.masterServices.checkTableModifiable(tableName);
88 } catch (TableNotDisabledException ex) {
89 if (isOnlineSchemaChangeAllowed()
90 && eventType.isOnlineSchemaChangeSupported()) {
91 LOG.debug("Ignoring table not disabled exception " +
92 "for supporting online schema changes.");
93 } else {
94 throw ex;
95 }
96 }
97 prepareWithTableLock();
98 success = true;
99 } finally {
100 if (!success ) {
101 releaseTableLock();
102 }
103 }
104 this.isPrepareCalled = true;
105 return this;
106 }
107
108
109
110
111 protected void prepareWithTableLock() throws IOException {
112 }
113
114 private boolean isOnlineSchemaChangeAllowed() {
115 return this.server.getConfiguration().getBoolean(
116 "hbase.online.schema.update.enable", false);
117 }
118
119 @Override
120 public void process() {
121 if (!isPrepareCalled) {
122
123
124 throw new RuntimeException("Implementation should have called prepare() first");
125 }
126 try {
127 LOG.info("Handling table operation " + eventType + " on table " +
128 tableName);
129
130 List<HRegionInfo> hris;
131 if (TableName.META_TABLE_NAME.equals(tableName)) {
132 hris = new MetaTableLocator().getMetaRegions(server.getZooKeeper());
133 } else {
134 hris = MetaTableAccessor.getTableRegions(server.getZooKeeper(),
135 server.getConnection(), tableName);
136 }
137 handleTableOperation(hris);
138 if (eventType.isOnlineSchemaChangeSupported() && this.masterServices.
139 getAssignmentManager().getTableStateManager().isTableState(
140 tableName, ZooKeeperProtos.Table.State.ENABLED)) {
141 if (reOpenAllRegions(hris)) {
142 LOG.info("Completed table operation " + eventType + " on table " +
143 tableName);
144 } else {
145 LOG.warn("Error on reopening the regions");
146 }
147 }
148 completed(null);
149 } catch (IOException e) {
150 LOG.error("Error manipulating table " + tableName, e);
151 completed(e);
152 } catch (CoordinatedStateException e) {
153 LOG.error("Error manipulating table " + tableName, e);
154 completed(e);
155 } finally {
156 releaseTableLock();
157 }
158 }
159
160 protected void releaseTableLock() {
161 if (this.tableLock != null) {
162 try {
163 this.tableLock.release();
164 } catch (IOException ex) {
165 LOG.warn("Could not release the table lock", ex);
166 }
167 }
168 }
169
170
171
172
173
174 protected void completed(final Throwable exception) {
175 }
176
177 public boolean reOpenAllRegions(List<HRegionInfo> regions) throws IOException {
178 boolean done = false;
179 LOG.info("Bucketing regions by region server...");
180 List<HRegionLocation> regionLocations = null;
181 Connection connection = this.masterServices.getConnection();
182 try (RegionLocator locator = connection.getRegionLocator(tableName)) {
183 regionLocations = locator.getAllRegionLocations();
184 }
185
186 NavigableMap<HRegionInfo, ServerName> hri2Sn = new TreeMap<HRegionInfo, ServerName>();
187 for (HRegionLocation location: regionLocations) {
188 hri2Sn.put(location.getRegionInfo(), location.getServerName());
189 }
190 TreeMap<ServerName, List<HRegionInfo>> serverToRegions = Maps.newTreeMap();
191 List<HRegionInfo> reRegions = new ArrayList<HRegionInfo>();
192 for (HRegionInfo hri : regions) {
193 ServerName sn = hri2Sn.get(hri);
194
195
196 if (null == sn) {
197 LOG.info("Skip " + hri);
198 continue;
199 }
200 if (!serverToRegions.containsKey(sn)) {
201 LinkedList<HRegionInfo> hriList = Lists.newLinkedList();
202 serverToRegions.put(sn, hriList);
203 }
204 reRegions.add(hri);
205 serverToRegions.get(sn).add(hri);
206 }
207
208 LOG.info("Reopening " + reRegions.size() + " regions on "
209 + serverToRegions.size() + " region servers.");
210 this.masterServices.getAssignmentManager().setRegionsToReopen(reRegions);
211 BulkReOpen bulkReopen = new BulkReOpen(this.server, serverToRegions,
212 this.masterServices.getAssignmentManager());
213 while (true) {
214 try {
215 if (bulkReopen.bulkReOpen()) {
216 done = true;
217 break;
218 } else {
219 LOG.warn("Timeout before reopening all regions");
220 }
221 } catch (InterruptedException e) {
222 LOG.warn("Reopen was interrupted");
223
224 Thread.currentThread().interrupt();
225 break;
226 }
227 }
228 return done;
229 }
230
231
232
233
234
235
236
237
238
239 public HTableDescriptor getTableDescriptor()
240 throws FileNotFoundException, IOException {
241 HTableDescriptor htd =
242 this.masterServices.getTableDescriptors().get(tableName);
243 if (htd == null) {
244 throw new IOException("HTableDescriptor missing for " + tableName);
245 }
246 return htd;
247 }
248
249 byte [] hasColumnFamily(final HTableDescriptor htd, final byte [] cf)
250 throws InvalidFamilyOperationException {
251 if (!htd.hasFamily(cf)) {
252 throw new InvalidFamilyOperationException("Column family '" +
253 Bytes.toString(cf) + "' does not exist");
254 }
255 return cf;
256 }
257
258 protected abstract void handleTableOperation(List<HRegionInfo> regions)
259 throws IOException, CoordinatedStateException;
260 }