1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.replication.regionserver;
20
21 import java.io.Closeable;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.util.Iterator;
25 import java.util.NoSuchElementException;
26 import java.util.concurrent.PriorityBlockingQueue;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileStatus;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.classification.InterfaceAudience;
36 import org.apache.hadoop.hbase.classification.InterfaceStability;
37 import org.apache.hadoop.hbase.regionserver.wal.ProtobufLogReader;
38 import org.apache.hadoop.hbase.util.CancelableProgressable;
39 import org.apache.hadoop.hbase.util.FSUtils;
40 import org.apache.hadoop.hbase.util.LeaseNotRecoveredException;
41 import org.apache.hadoop.hbase.wal.WAL.Entry;
42 import org.apache.hadoop.hbase.wal.WAL.Reader;
43 import org.apache.hadoop.hbase.wal.WALFactory;
44 import org.apache.hadoop.ipc.RemoteException;
45
46
47
48
49
50
51 @InterfaceAudience.Private
52 @InterfaceStability.Evolving
53 public class WALEntryStream implements Iterator<Entry>, Closeable, Iterable<Entry> {
54 private static final Log LOG = LogFactory.getLog(WALEntryStream.class);
55
56 private Reader reader;
57 private Path currentPath;
58
59 private Entry currentEntry;
60
61 private long currentPosition = 0;
62 private final ReplicationSourceLogQueue logQueue;
63 private final String walGroupId;
64 private FileSystem fs;
65 private Configuration conf;
66 private MetricsSource metrics;
67
68
69
70
71
72
73
74
75
76 public WALEntryStream(ReplicationSourceLogQueue logQueue, FileSystem fs, Configuration conf,
77 MetricsSource metrics, String walGroupId) {
78 this(logQueue, fs, conf, 0, metrics, walGroupId);
79 }
80
81
82
83
84
85
86
87
88
89
90 public WALEntryStream(ReplicationSourceLogQueue logQueue, FileSystem fs, Configuration conf,
91 long startPosition, MetricsSource metrics, String walGroupId) {
92 this.logQueue = logQueue;
93 this.fs = fs;
94 this.conf = conf;
95 this.currentPosition = startPosition;
96 this.metrics = metrics;
97 this.walGroupId = walGroupId;
98 }
99
100
101
102
103
104 @Override
105 public boolean hasNext() {
106 if (currentEntry == null) {
107 try {
108 tryAdvanceEntry();
109 } catch (Exception e) {
110 throw new WALEntryStreamRuntimeException(e);
111 }
112 }
113 return currentEntry != null;
114 }
115
116
117
118
119
120
121 @Override
122 public Entry next() {
123 if (!hasNext()) {
124 throw new NoSuchElementException();
125 }
126 Entry save = currentEntry;
127 currentEntry = null;
128 return save;
129 }
130
131
132
133
134 @Override
135 public void remove() {
136 throw new UnsupportedOperationException();
137 }
138
139
140
141
142 @Override
143 public void close() throws IOException {
144 closeReader();
145 }
146
147
148
149
150 @Override
151 public Iterator<Entry> iterator() {
152 return this;
153 }
154
155
156
157
158 public long getPosition() {
159 return currentPosition;
160 }
161
162
163
164
165 public Path getCurrentPath() {
166 return currentPath;
167 }
168
169 private String getCurrentPathStat() {
170 StringBuilder sb = new StringBuilder();
171 if (currentPath != null) {
172 sb.append("currently replicating from: ").append(currentPath).append(" at position: ")
173 .append(currentPosition).append("\n");
174 } else {
175 sb.append("no replication ongoing, waiting for new log");
176 }
177 return sb.toString();
178 }
179
180
181
182
183
184
185 public void reset() throws IOException {
186 if (reader != null && currentPath != null) {
187 resetReader();
188 }
189 }
190
191 private void setPosition(long position) {
192 currentPosition = position;
193 }
194
195 private void setCurrentPath(Path path) {
196 this.currentPath = path;
197 }
198
199 private void tryAdvanceEntry() throws IOException {
200 if (checkReader()) {
201 readNextEntryAndSetPosition();
202 if (currentEntry == null) {
203 if (logQueue.getQueue(walGroupId).size() > 1) {
204
205
206
207 resetReader();
208 readNextEntryAndSetPosition();
209 if (currentEntry == null) {
210 if (checkAllBytesParsed()) {
211 dequeueCurrentLog();
212 if (openNextLog()) {
213 readNextEntryAndSetPosition();
214 }
215 }
216 }
217 }
218 }
219 }
220
221 }
222
223
224 private boolean checkAllBytesParsed() throws IOException {
225
226 final long trailerSize = currentTrailerSize();
227 FileStatus stat = null;
228 try {
229 stat = fs.getFileStatus(this.currentPath);
230 } catch (IOException exception) {
231 LOG.warn("Couldn't get file length information about log " + this.currentPath + ", it "
232 + (trailerSize < 0 ? "was not" : "was") + " closed cleanly " + getCurrentPathStat());
233 metrics.incrUnknownFileLengthForClosedWAL();
234 }
235 if (stat != null) {
236 if (trailerSize < 0) {
237 if (currentPosition < stat.getLen()) {
238 final long skippedBytes = stat.getLen() - currentPosition;
239 if (LOG.isDebugEnabled()) {
240 LOG.debug("Reached the end of WAL file '" + currentPath
241 + "'. It was not closed cleanly, so we did not parse " + skippedBytes
242 + " bytes of data. This is normally ok.");
243 }
244 metrics.incrUncleanlyClosedWALs();
245 metrics.incrBytesSkippedInUncleanlyClosedWALs(skippedBytes);
246 }
247 } else if (currentPosition + trailerSize < stat.getLen()) {
248 LOG.warn("Processing end of WAL file '" + currentPath + "'. At position " + currentPosition
249 + ", which is too far away from reported file length " + stat.getLen()
250 + ". Restarting WAL reading (see HBASE-15983 for details). " + getCurrentPathStat());
251 setPosition(0);
252 resetReader();
253 metrics.incrRestartedWALReading();
254 metrics.incrRepeatedFileBytes(currentPosition);
255 return false;
256 }
257 }
258 if (LOG.isTraceEnabled()) {
259 LOG.trace("Reached the end of log " + this.currentPath + ", and the length of the file is "
260 + (stat == null ? "N/A" : stat.getLen()));
261 }
262 metrics.incrCompletedWAL();
263 return true;
264 }
265
266 private void dequeueCurrentLog() throws IOException {
267 if (LOG.isDebugEnabled()) {
268 LOG.debug("Reached the end of log " + currentPath);
269 }
270 closeReader();
271 logQueue.remove(walGroupId);
272 setCurrentPath(null);
273 setPosition(0);
274 }
275
276 private void readNextEntryAndSetPosition() throws IOException {
277 Entry readEntry = reader.next();
278 long readerPos = reader.getPosition();
279 if (readEntry != null) {
280 metrics.incrLogEditsRead();
281 metrics.incrLogReadInBytes(readerPos - currentPosition);
282 }
283 currentEntry = readEntry;
284 setPosition(readerPos);
285 }
286
287 private void closeReader() throws IOException {
288 if (reader != null) {
289 reader.close();
290 reader = null;
291 }
292 }
293
294
295 private boolean checkReader() throws IOException {
296 if (reader == null) {
297 return openNextLog();
298 }
299 return true;
300 }
301
302
303 private boolean openNextLog() throws IOException {
304 PriorityBlockingQueue<Path> queue = logQueue.getQueue(walGroupId);
305 Path nextPath = queue.peek();
306 if (nextPath != null) {
307 openReader(nextPath);
308 if (reader != null) {
309 return true;
310 }
311 }
312 return false;
313 }
314
315 Path getArchivedLog(Path path) throws IOException {
316 Path rootDir = FSUtils.getRootDir(conf);
317 Path oldLogDir = new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME);
318 Path archivedLogLocation = new Path(oldLogDir, path.getName());
319 if (fs.exists(archivedLogLocation)) {
320 LOG.info("Log " + path + " was moved to " + archivedLogLocation);
321 return archivedLogLocation;
322 } else {
323 LOG.error("Couldn't locate log: " + path);
324 return path;
325 }
326 }
327
328 private void handleFileNotFound(Path path, FileNotFoundException fnfe) throws IOException {
329
330 Path archivedLog = getArchivedLog(path);
331 if (!path.equals(archivedLog)) {
332 openReader(archivedLog);
333 } else {
334 throw fnfe;
335 }
336 }
337 private void openReader(Path path) throws IOException {
338 try {
339
340
341 if (reader == null || !getCurrentPath().equals(path)) {
342 closeReader();
343 reader = WALFactory.createReader(fs, path, conf);
344 seek();
345 setCurrentPath(path);
346 } else {
347 resetReader();
348 }
349 } catch (FileNotFoundException fnfe) {
350 handleFileNotFound(path, fnfe);
351 } catch (RemoteException re) {
352 IOException ioe = re.unwrapRemoteException(FileNotFoundException.class);
353 if (!(ioe instanceof FileNotFoundException)) {
354 throw ioe;
355 }
356 handleFileNotFound(path, (FileNotFoundException)ioe);
357 } catch (LeaseNotRecoveredException lnre) {
358
359 LOG.warn("Try to recover the WAL lease " + path, lnre);
360 recoverLease(conf, path);
361 reader = null;
362 } catch (NullPointerException npe) {
363
364
365
366 LOG.warn("Got NPE opening reader, will retry.");
367 reader = null;
368 }
369 }
370
371
372 private void recoverLease(final Configuration conf, final Path path) {
373 try {
374 final FileSystem dfs = FSUtils.getCurrentFileSystem(conf);
375 FSUtils fsUtils = FSUtils.getInstance(dfs, conf);
376 fsUtils.recoverFileLease(dfs, path, conf, new CancelableProgressable() {
377 @Override
378 public boolean progress() {
379 LOG.debug("recover WAL lease: " + path);
380 return true;
381 }
382 });
383 } catch (IOException e) {
384 LOG.warn("unable to recover lease for WAL: " + path, e);
385 }
386 }
387
388 private void resetReader() throws IOException {
389 try {
390 reader.reset();
391 seek();
392 } catch (FileNotFoundException fnfe) {
393
394 Path archivedLog = getArchivedLog(currentPath);
395 if (!currentPath.equals(archivedLog)) {
396 openReader(archivedLog);
397 } else {
398 throw fnfe;
399 }
400 } catch (NullPointerException npe) {
401 throw new IOException("NPE resetting reader, likely HDFS-4380", npe);
402 }
403 }
404
405 private void seek() throws IOException {
406 if (currentPosition != 0) {
407 reader.seek(currentPosition);
408 }
409 }
410
411 private long currentTrailerSize() {
412 long size = -1L;
413 if (reader instanceof ProtobufLogReader) {
414 final ProtobufLogReader pblr = (ProtobufLogReader) reader;
415 size = pblr.trailerSize();
416 }
417 return size;
418 }
419
420 @InterfaceAudience.Private
421 public static class WALEntryStreamRuntimeException extends RuntimeException {
422 private static final long serialVersionUID = -6298201811259982568L;
423
424 public WALEntryStreamRuntimeException(Exception e) {
425 super(e);
426 }
427 }
428
429 }