1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.master;
19
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
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.RegionTransition;
35 import org.apache.hadoop.hbase.Server;
36 import org.apache.hadoop.hbase.ServerLoad;
37 import org.apache.hadoop.hbase.ServerName;
38 import org.apache.hadoop.hbase.catalog.MetaReader;
39 import org.apache.hadoop.hbase.master.RegionState.State;
40 import org.apache.hadoop.hbase.util.Bytes;
41 import org.apache.hadoop.hbase.util.Pair;
42 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
43 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
44 import org.apache.zookeeper.KeeperException;
45
46 import com.google.common.base.Preconditions;
47
48
49
50
51
52
53
54 @InterfaceAudience.Private
55 public class RegionStates {
56 private static final Log LOG = LogFactory.getLog(RegionStates.class);
57
58
59
60
61 final HashMap<String, RegionState> regionsInTransition;
62
63
64
65
66
67 private final Map<String, RegionState> regionStates;
68
69
70
71
72
73 private final Map<ServerName, Set<HRegionInfo>> serverHoldings;
74
75
76
77
78
79 private final TreeMap<HRegionInfo, ServerName> regionAssignments;
80
81 private final ServerManager serverManager;
82 private final Server server;
83
84 RegionStates(final Server master, final ServerManager serverManager) {
85 regionStates = new HashMap<String, RegionState>();
86 regionsInTransition = new HashMap<String, RegionState>();
87 serverHoldings = new HashMap<ServerName, Set<HRegionInfo>>();
88 regionAssignments = new TreeMap<HRegionInfo, ServerName>();
89 this.serverManager = serverManager;
90 this.server = master;
91 }
92
93
94
95
96 @SuppressWarnings("unchecked")
97 public synchronized Map<HRegionInfo, ServerName> getRegionAssignments() {
98 return (Map<HRegionInfo, ServerName>)regionAssignments.clone();
99 }
100
101 public synchronized ServerName getRegionServerOfRegion(HRegionInfo hri) {
102 return regionAssignments.get(hri);
103 }
104
105
106
107
108 @SuppressWarnings("unchecked")
109 public synchronized Map<String, RegionState> getRegionsInTransition() {
110 return (Map<String, RegionState>)regionsInTransition.clone();
111 }
112
113
114
115
116 public synchronized boolean isRegionInTransition(final HRegionInfo hri) {
117 return regionsInTransition.containsKey(hri.getEncodedName());
118 }
119
120
121
122
123 public synchronized boolean isRegionInTransition(final String regionName) {
124 return regionsInTransition.containsKey(regionName);
125 }
126
127
128
129
130 public synchronized boolean isRegionsInTransition() {
131 return !regionsInTransition.isEmpty();
132 }
133
134
135
136
137 public synchronized boolean isRegionAssigned(final HRegionInfo hri) {
138 return regionAssignments.containsKey(hri);
139 }
140
141
142
143
144 public synchronized boolean isRegionInState(
145 final HRegionInfo hri, final State state) {
146 RegionState regionState = getRegionState(hri);
147 State s = regionState != null ? regionState.getState() : null;
148 return s == state;
149 }
150
151
152
153
154 public synchronized void waitForUpdate(
155 final long timeout) throws InterruptedException {
156 this.wait(timeout);
157 }
158
159
160
161
162 public synchronized RegionState
163 getRegionTransitionState(final HRegionInfo hri) {
164 return regionsInTransition.get(hri.getEncodedName());
165 }
166
167
168
169
170 public synchronized RegionState
171 getRegionTransitionState(final String regionName) {
172 return regionsInTransition.get(regionName);
173 }
174
175
176
177
178
179 public synchronized void createRegionStates(
180 final List<HRegionInfo> hris) {
181 for (HRegionInfo hri: hris) {
182 createRegionState(hri);
183 }
184 }
185
186
187
188
189
190
191 public synchronized RegionState createRegionState(final HRegionInfo hri) {
192 String regionName = hri.getEncodedName();
193 RegionState regionState = regionStates.get(regionName);
194 if (regionState != null) {
195 LOG.warn("Tried to create a state of a region already in RegionStates, " +
196 "used existing state: " + regionState + ", ignored new state: state=OFFLINE, server=null");
197 } else {
198 regionState = new RegionState(hri, State.OFFLINE);
199 regionStates.put(regionName, regionState);
200 }
201 return regionState;
202 }
203
204
205
206
207 public synchronized RegionState updateRegionState(
208 final HRegionInfo hri, final State state) {
209 RegionState regionState = regionStates.get(hri.getEncodedName());
210 ServerName serverName = (regionState == null || state == State.CLOSED
211 || state == State.OFFLINE) ? null : regionState.getServerName();
212 return updateRegionState(hri, state, serverName);
213 }
214
215
216
217
218
219
220
221 public synchronized RegionState updateRegionState(
222 final RegionTransition transition, final State state) {
223 byte [] regionName = transition.getRegionName();
224 HRegionInfo regionInfo = getRegionInfo(regionName);
225 if (regionInfo == null) {
226 String prettyRegionName = HRegionInfo.prettyPrint(
227 HRegionInfo.encodeRegionName(regionName));
228 LOG.warn("Failed to find region " + prettyRegionName
229 + " in updating its state to " + state
230 + " based on region transition " + transition);
231 return null;
232 }
233 return updateRegionState(regionInfo, state,
234 transition.getServerName());
235 }
236
237
238
239
240 public synchronized RegionState updateRegionState(
241 final HRegionInfo hri, final State state, final ServerName serverName) {
242 ServerName newServerName = serverName;
243 if (serverName != null &&
244 (state == State.CLOSED || state == State.OFFLINE)) {
245 LOG.warn("Closed region " + hri.getShortNameToLog() + " still on "
246 + serverName + "? Ignored, reset it to null");
247 newServerName = null;
248 }
249
250 if (state == State.FAILED_CLOSE || state == State.FAILED_OPEN) {
251 LOG.warn("Failed to transition " + hri.getShortNameToLog()
252 + " on " + serverName + ", set to " + state);
253 }
254
255 String regionName = hri.getEncodedName();
256 RegionState regionState = new RegionState(
257 hri, state, System.currentTimeMillis(), newServerName);
258 RegionState oldState = regionStates.put(regionName, regionState);
259 if (oldState == null || oldState.getState() != regionState.getState()) {
260 LOG.info("Transitioned from " + oldState + " to " + regionState);
261 }
262 if (newServerName != null || (
263 state != State.PENDING_CLOSE && state != State.CLOSING)) {
264 regionsInTransition.put(regionName, regionState);
265 }
266
267
268 this.notifyAll();
269 return regionState;
270 }
271
272
273
274
275
276
277 public synchronized void regionOnline(
278 final HRegionInfo hri, final ServerName serverName) {
279 String regionName = hri.getEncodedName();
280 RegionState oldState = regionStates.get(regionName);
281 if (oldState == null) {
282 LOG.warn("Online a region not in RegionStates: " + hri.getShortNameToLog());
283 } else {
284 State state = oldState.getState();
285 ServerName sn = oldState.getServerName();
286 if (state != State.OPEN || sn == null || !sn.equals(serverName)) {
287 LOG.debug("Online a region " + hri.getShortNameToLog() + " with current state=" + state +
288 ", expected state=OPEN" + ", assigned to server: " + sn + " expected " + serverName);
289 }
290 }
291 updateRegionState(hri, State.OPEN, serverName);
292 regionsInTransition.remove(regionName);
293
294 ServerName oldServerName = regionAssignments.put(hri, serverName);
295 if (!serverName.equals(oldServerName)) {
296 LOG.info("Onlined " + hri.getShortNameToLog() + " on " + serverName);
297 Set<HRegionInfo> regions = serverHoldings.get(serverName);
298 if (regions == null) {
299 regions = new HashSet<HRegionInfo>();
300 serverHoldings.put(serverName, regions);
301 }
302 regions.add(hri);
303 if (oldServerName != null) {
304 LOG.info("Offlined " + hri.getShortNameToLog() + " from " + oldServerName);
305 serverHoldings.get(oldServerName).remove(hri);
306 }
307 }
308 }
309
310
311
312
313 public void regionOffline(final HRegionInfo hri) {
314 regionOffline(hri, null);
315 }
316
317
318
319
320
321
322 public synchronized void regionOffline(
323 final HRegionInfo hri, final State expectedState) {
324 Preconditions.checkArgument(expectedState == null
325 || expectedState == State.OFFLINE || expectedState == State.SPLIT
326 || expectedState == State.MERGED, "Offlined region should be in state"
327 + " OFFLINE/SPLIT/MERGED instead of " + expectedState);
328 String regionName = hri.getEncodedName();
329 RegionState oldState = regionStates.get(regionName);
330 if (oldState == null) {
331 LOG.warn("Offline a region not in RegionStates: " + hri.getShortNameToLog());
332 } else if (LOG.isDebugEnabled()) {
333 State state = oldState.getState();
334 ServerName sn = oldState.getServerName();
335 if (state != State.OFFLINE
336 && state != State.SPLITTING && state != State.MERGING) {
337 LOG.debug("Offline a region " + hri.getShortNameToLog() + " with current state="
338 + state + ", expected state=OFFLINE/SPLITTING/MERGING");
339 }
340 if (sn != null && state == State.OFFLINE) {
341 LOG.debug("Offline a region " + hri.getShortNameToLog()
342 + " with current state=OFFLINE, assigned to server: "
343 + sn + ", expected null");
344 }
345 }
346 State newState = expectedState;
347 if (newState == null) newState = State.OFFLINE;
348 updateRegionState(hri, newState);
349 regionsInTransition.remove(regionName);
350
351 ServerName oldServerName = regionAssignments.remove(hri);
352 if (oldServerName != null) {
353 LOG.info("Offlined " + hri.getShortNameToLog() + " from " + oldServerName);
354 serverHoldings.get(oldServerName).remove(hri);
355 }
356 }
357
358
359
360
361 public synchronized List<HRegionInfo> serverOffline(
362 final ZooKeeperWatcher watcher, final ServerName sn) {
363
364
365 List<HRegionInfo> rits = new ArrayList<HRegionInfo>();
366 Set<HRegionInfo> assignedRegions = serverHoldings.remove(sn);
367 if (assignedRegions == null) {
368 assignedRegions = new HashSet<HRegionInfo>();
369 }
370
371 for (HRegionInfo region : assignedRegions) {
372 regionAssignments.remove(region);
373 }
374
375 for (RegionState state : regionsInTransition.values()) {
376 HRegionInfo hri = state.getRegion();
377 if (assignedRegions.contains(hri)) {
378
379
380
381 LOG.info("Transitioning " + state + " will be handled by SSH for " + sn);
382 if (state.isSplitting() || state.isMerging()) {
383 LOG.info("Offline splitting/merging region " + state);
384 try {
385
386 ZKAssign.deleteNodeFailSilent(watcher, hri);
387 regionOffline(hri);
388 } catch (KeeperException ke) {
389 server.abort("Unexpected ZK exception deleting node " + hri, ke);
390 }
391 }
392 } else if (sn.equals(state.getServerName())) {
393
394
395
396
397 if (state.isPendingOpen() || state.isOpening()) {
398 LOG.info("Found opening region " + state + " to be reassigned by SSH for " + sn);
399 rits.add(hri);
400 } else {
401 LOG.warn("THIS SHOULD NOT HAPPEN: unexpected state "
402 + state + " of region in transition on server " + sn);
403 }
404 }
405 }
406 assignedRegions.clear();
407 this.notifyAll();
408 return rits;
409 }
410
411
412
413
414
415
416
417
418
419
420
421 public synchronized List<HRegionInfo> getRegionsOfTable(TableName tableName) {
422 List<HRegionInfo> tableRegions = new ArrayList<HRegionInfo>();
423
424
425 HRegionInfo boundary = new HRegionInfo(tableName, null, null, false, 0L);
426 for (HRegionInfo hri: regionAssignments.tailMap(boundary).keySet()) {
427 if(!hri.getTableName().equals(tableName)) break;
428 tableRegions.add(hri);
429 }
430 return tableRegions;
431 }
432
433
434
435
436
437
438
439
440 public synchronized void waitOnRegionToClearRegionsInTransition(
441 final HRegionInfo hri) throws InterruptedException {
442 if (!isRegionInTransition(hri)) return;
443
444 while(!server.isStopped() && isRegionInTransition(hri)) {
445 RegionState rs = getRegionState(hri);
446 LOG.info("Waiting on " + rs + " to clear regions-in-transition");
447 waitForUpdate(100);
448 }
449
450 if (server.isStopped()) {
451 LOG.info("Giving up wait on region in " +
452 "transition because stoppable.isStopped is set");
453 }
454 }
455
456
457
458
459
460
461
462 public synchronized void waitForAssignment(
463 final HRegionInfo hri) throws InterruptedException {
464 if (!isRegionAssigned(hri)) return;
465
466 while(!server.isStopped() && !isRegionAssigned(hri)) {
467 RegionState rs = getRegionState(hri);
468 LOG.info("Waiting on " + rs + " to be assigned");
469 waitForUpdate(100);
470 }
471
472 if (server.isStopped()) {
473 LOG.info("Giving up wait on region " +
474 "assignment because stoppable.isStopped is set");
475 }
476 }
477
478
479
480
481
482
483
484 protected synchronized double getAverageLoad() {
485 int numServers = 0, totalLoad = 0;
486 for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
487 Set<HRegionInfo> regions = e.getValue();
488 ServerName serverName = e.getKey();
489 int regionCount = regions.size();
490 if (regionCount > 0 || serverManager.isServerOnline(serverName)) {
491 totalLoad += regionCount;
492 numServers++;
493 }
494 }
495 return numServers == 0 ? 0.0 :
496 (double)totalLoad / (double)numServers;
497 }
498
499
500
501
502
503
504
505
506
507 protected Map<TableName, Map<ServerName, List<HRegionInfo>>>
508 getAssignmentsByTable() {
509 Map<TableName, Map<ServerName, List<HRegionInfo>>> result =
510 new HashMap<TableName, Map<ServerName,List<HRegionInfo>>>();
511 synchronized (this) {
512 if (!server.getConfiguration().getBoolean("hbase.master.loadbalance.bytable", false)) {
513 Map<ServerName, List<HRegionInfo>> svrToRegions =
514 new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
515 for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
516 svrToRegions.put(e.getKey(), new ArrayList<HRegionInfo>(e.getValue()));
517 }
518 result.put(TableName.valueOf("ensemble"), svrToRegions);
519 } else {
520 for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
521 for (HRegionInfo hri: e.getValue()) {
522 if (hri.isMetaRegion()) continue;
523 TableName tablename = hri.getTableName();
524 Map<ServerName, List<HRegionInfo>> svrToRegions = result.get(tablename);
525 if (svrToRegions == null) {
526 svrToRegions = new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
527 result.put(tablename, svrToRegions);
528 }
529 List<HRegionInfo> regions = svrToRegions.get(e.getKey());
530 if (regions == null) {
531 regions = new ArrayList<HRegionInfo>();
532 svrToRegions.put(e.getKey(), regions);
533 }
534 regions.add(hri);
535 }
536 }
537 }
538 }
539
540 Map<ServerName, ServerLoad>
541 onlineSvrs = serverManager.getOnlineServers();
542
543 for (Map<ServerName, List<HRegionInfo>> map: result.values()) {
544 for (ServerName svr: onlineSvrs.keySet()) {
545 if (!map.containsKey(svr)) {
546 map.put(svr, new ArrayList<HRegionInfo>());
547 }
548 }
549 }
550 return result;
551 }
552
553 protected synchronized RegionState getRegionState(final HRegionInfo hri) {
554 return regionStates.get(hri.getEncodedName());
555 }
556
557 protected synchronized RegionState getRegionState(final String regionName) {
558 return regionStates.get(regionName);
559 }
560
561
562
563
564
565
566 protected HRegionInfo getRegionInfo(final byte [] regionName) {
567 String encodedName = HRegionInfo.encodeRegionName(regionName);
568 RegionState regionState = regionStates.get(encodedName);
569 if (regionState != null) {
570 return regionState.getRegion();
571 }
572
573 try {
574 Pair<HRegionInfo, ServerName> p =
575 MetaReader.getRegion(server.getCatalogTracker(), regionName);
576 HRegionInfo hri = p == null ? null : p.getFirst();
577 if (hri != null) {
578 createRegionState(hri);
579 }
580 return hri;
581 } catch (IOException e) {
582 server.abort("Aborting because error occoured while reading " +
583 Bytes.toStringBinary(regionName) + " from .META.", e);
584 return null;
585 }
586 }
587 }