1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import java.io.IOException;
22 import java.util.Map.Entry;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.atomic.AtomicBoolean;
25 import java.util.concurrent.locks.ReentrantLock;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.hbase.HConstants;
31 import org.apache.hadoop.hbase.RemoteExceptionHandler;
32 import org.apache.hadoop.hbase.Server;
33 import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
34 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
35 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36 import org.apache.hadoop.hbase.wal.WAL;
37 import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.HasThread;
40
41
42
43
44
45
46
47
48
49
50 @InterfaceAudience.Private
51 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
52 justification="Use of an atomic type both as monitor and condition variable is intended")
53 public class LogRoller extends HasThread {
54 private static final Log LOG = LogFactory.getLog(LogRoller.class);
55 private final ReentrantLock rollLock = new ReentrantLock();
56 private final AtomicBoolean rollLog = new AtomicBoolean(false);
57 private final ConcurrentHashMap<WAL, RollController> wals =
58 new ConcurrentHashMap<WAL, RollController>();
59 private final Server server;
60 protected final RegionServerServices services;
61
62 private final long rollPeriod;
63 private final int threadWakeFrequency;
64
65 private final long checkLowReplicationInterval;
66
67 public void addWAL(final WAL wal) {
68 if (null == wals.putIfAbsent(wal, new RollController(wal))) {
69 wal.registerWALActionsListener(new WALActionsListener.Base() {
70 @Override
71 public void logRollRequested(WALActionsListener.RollRequestReason reason) {
72 RollController controller = wals.get(wal);
73 if (controller == null) {
74 wals.putIfAbsent(wal, new RollController(wal));
75 controller = wals.get(wal);
76 }
77 controller.requestRoll();
78
79 synchronized(rollLog) {
80 rollLog.set(true);
81 rollLog.notifyAll();
82 }
83 }
84 });
85 }
86 }
87
88 public void requestRollAll() {
89 for (RollController controller : wals.values()) {
90 controller.requestRoll();
91 }
92 synchronized(rollLog) {
93 rollLog.set(true);
94 rollLog.notifyAll();
95 }
96 }
97
98
99 public LogRoller(final Server server, final RegionServerServices services) {
100 super("LogRoller");
101 this.server = server;
102 this.services = services;
103 this.rollPeriod = this.server.getConfiguration().
104 getLong("hbase.regionserver.logroll.period", 3600000);
105 this.threadWakeFrequency = this.server.getConfiguration().
106 getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000);
107 this.checkLowReplicationInterval = this.server.getConfiguration().getLong(
108 "hbase.regionserver.hlog.check.lowreplication.interval", 30 * 1000);
109 }
110
111 @Override
112 public void interrupt() {
113
114 synchronized (rollLog) {
115 this.rollLog.notify();
116 }
117 super.interrupt();
118 }
119
120
121
122
123 void checkLowReplication(long now) {
124 try {
125 for (Entry<WAL, RollController> entry : wals.entrySet()) {
126 WAL wal = entry.getKey();
127 boolean neeRollAlready = entry.getValue().needsRoll(now);
128 if(wal instanceof FSHLog && !neeRollAlready) {
129 FSHLog hlog = (FSHLog)wal;
130 if ((now - hlog.getLastTimeCheckLowReplication())
131 > this.checkLowReplicationInterval) {
132 hlog.checkLogRoll();
133 }
134 }
135 }
136 } catch (Throwable e) {
137 LOG.warn("Failed checking low replication", e);
138 }
139 }
140
141 @Override
142 public void run() {
143 while (!server.isStopped()) {
144 long now = EnvironmentEdgeManager.currentTime();
145 checkLowReplication(now);
146 if (!rollLog.get()) {
147 boolean periodic = false;
148 for (RollController controller : wals.values()) {
149 if (controller.needsPeriodicRoll(now)) {
150 periodic = true;
151 break;
152 }
153 }
154 if (!periodic) {
155 synchronized (rollLog) {
156 try {
157 if (!rollLog.get()) {
158 rollLog.wait(this.threadWakeFrequency);
159 }
160 } catch (InterruptedException e) {
161
162 }
163 }
164 continue;
165 }
166 }
167 rollLock.lock();
168 try {
169 for (Entry<WAL, RollController> entry : wals.entrySet()) {
170 final WAL wal = entry.getKey();
171 RollController controller = entry.getValue();
172 if (controller.isRollRequested()) {
173
174 LOG.debug("WAL " + wal + " roll requested");
175 } else if (controller.needsPeriodicRoll(now)) {
176
177 LOG.debug("WAL " + wal + " roll period " + this.rollPeriod + "ms elapsed");
178 } else {
179 continue;
180 }
181
182
183 final byte [][] regionsToFlush = controller.rollWal(now);
184 if (regionsToFlush != null) {
185 for (byte [] r: regionsToFlush) scheduleFlush(r);
186 }
187 }
188 } catch (FailedLogCloseException e) {
189 server.abort("Failed log close in log roller", e);
190 } catch (java.net.ConnectException e) {
191 server.abort("Failed log close in log roller", e);
192 } catch (IOException ex) {
193 LOG.fatal("Aborting", ex);
194
195 server.abort("IOE in log roller",
196 RemoteExceptionHandler.checkIOException(ex));
197 } catch (Exception ex) {
198 final String msg = "Failed rolling WAL; aborting to recover edits!";
199 LOG.error(msg, ex);
200 server.abort(msg, ex);
201 } finally {
202 try {
203 rollLog.set(false);
204 } finally {
205 rollLock.unlock();
206 }
207 }
208 }
209 LOG.info("LogRoller exiting.");
210 }
211
212
213
214
215 private void scheduleFlush(final byte [] encodedRegionName) {
216 boolean scheduled = false;
217 Region r = this.services.getFromOnlineRegions(Bytes.toString(encodedRegionName));
218 FlushRequester requester = null;
219 if (r != null) {
220 requester = this.services.getFlushRequester();
221 if (requester != null) {
222
223 requester.requestFlush(r, true);
224 scheduled = true;
225 }
226 }
227 if (!scheduled) {
228 LOG.warn("Failed to schedule flush of " +
229 Bytes.toString(encodedRegionName) + ", region=" + r + ", requester=" +
230 requester);
231 }
232 }
233
234
235
236
237
238 public boolean walRollFinished() {
239 long now = EnvironmentEdgeManager.currentTime();
240 for (RollController controller : wals.values()) {
241 if (controller.needsRoll(now)) {
242 return false;
243 }
244 }
245 return true;
246 }
247
248
249
250
251
252 protected class RollController {
253 private final WAL wal;
254 private final AtomicBoolean rollRequest;
255 private long lastRollTime;
256
257 RollController(WAL wal) {
258 this.wal = wal;
259 this.rollRequest = new AtomicBoolean(false);
260 this.lastRollTime = EnvironmentEdgeManager.currentTime();
261 }
262
263 public void requestRoll() {
264 this.rollRequest.set(true);
265 }
266
267 public byte[][] rollWal(long now) throws IOException {
268 this.lastRollTime = now;
269 byte[][] regionsToFlush = wal.rollWriter(true);
270 this.rollRequest.set(false);
271 return regionsToFlush;
272 }
273
274 public boolean isRollRequested() {
275 return rollRequest.get();
276 }
277
278 public boolean needsPeriodicRoll(long now) {
279 return (now - this.lastRollTime) > rollPeriod;
280 }
281
282 public boolean needsRoll(long now) {
283 return isRollRequested() || needsPeriodicRoll(now);
284 }
285 }
286 }