View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  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,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
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   * Runs periodically to determine if the WAL should be rolled.
43   *
44   * NOTE: This class extends Thread rather than Chore because the sleep time
45   * can be interrupted when there is something to do, rather than the Chore
46   * sleep time which is invariant.
47   *
48   * TODO: change to a pool of threads
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    // Period to roll log.
62    private final long rollPeriod;
63    private final int threadWakeFrequency;
64    // The interval to check low replication on hlog's pipeline
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            // TODO logs will contend with each other here, replace with e.g. DelayedQueue
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    /** @param server */
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     // Wake up if we are waiting on rollLog. For tests.
114     synchronized (rollLog) {
115       this.rollLog.notify();
116     }
117     super.interrupt();
118   }
119 
120   /**
121    * we need to check low replication in period, see HBASE-18132
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       boolean rollLogLocal;
147       synchronized (rollLog) {
148         rollLogLocal = rollLog.get();
149         rollLog.set(false);
150       }
151       if (!rollLogLocal) {
152         boolean periodic = false;
153         for (RollController controller : wals.values()) {
154           if (controller.needsPeriodicRoll(now)) {
155             periodic = true;
156             break;
157           }
158         }
159         if (!periodic) {
160           synchronized (rollLog) {
161             try {
162               rollLog.wait(this.threadWakeFrequency);
163             } catch (InterruptedException e) {
164               // Fall through
165               LOG.info("LogRoller interrupted ", e);
166             }
167           }
168           continue;
169         }
170       }
171       rollLock.lock(); // FindBugs UL_UNRELEASED_LOCK_EXCEPTION_PATH
172       try {
173         for (Entry<WAL, RollController> entry : wals.entrySet()) {
174           final WAL wal = entry.getKey();
175           RollController controller = entry.getValue();
176           if (controller.isRollRequested()) {
177             // WAL roll requested, fall through
178             LOG.debug("WAL " + wal + " roll requested");
179           } else if (controller.needsPeriodicRoll(now)) {
180             // Time for periodic roll, fall through
181             LOG.debug("WAL " + wal + " roll period " + this.rollPeriod + "ms elapsed");
182           } else {
183             continue;
184           }
185           // Force the roll if the logroll.period is elapsed or if a roll was requested.
186           // The returned value is an array of actual region names.
187           final byte [][] regionsToFlush = controller.rollWal(now);
188           if (regionsToFlush != null) {
189             for (byte [] r: regionsToFlush) scheduleFlush(r);
190           }
191         }
192       } catch (FailedLogCloseException e) {
193         server.abort("Failed log close in log roller", e);
194       } catch (java.net.ConnectException e) {
195         server.abort("Failed log close in log roller", e);
196       } catch (IOException ex) {
197         LOG.fatal("Aborting", ex);
198         // Abort if we get here.  We probably won't recover an IOE. HBASE-1132
199         server.abort("IOE in log roller",
200           RemoteExceptionHandler.checkIOException(ex));
201       } catch (Exception ex) {
202         final String msg = "Failed rolling WAL; aborting to recover edits!";
203         LOG.error(msg, ex);
204         server.abort(msg, ex);
205       } finally {
206         rollLock.unlock();
207       }
208     }
209     LOG.info("LogRoller exiting.");
210   }
211 
212   /**
213    * @param encodedRegionName Encoded name of region to flush.
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         // force flushing all stores to clean old logs
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    * For testing only
236    * @return true if all WAL roll finished
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    * Independently control the roll of each wal. When use multiwal,
250    * can avoid all wal roll together. see HBASE-24665 for detail
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       // reset the flag in front to avoid missing roll request before we return from rollWriter.
270       this.rollRequest.set(false);
271       byte[][] regionsToFlush = wal.rollWriter(true);
272       return regionsToFlush;
273     }
274 
275     public boolean isRollRequested() {
276       return rollRequest.get();
277     }
278 
279     public boolean needsPeriodicRoll(long now) {
280       return (now - this.lastRollTime) > rollPeriod;
281     }
282 
283     public boolean needsRoll(long now) {
284       return isRollRequested() || needsPeriodicRoll(now);
285     }
286   }
287 }