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