1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.regionserver.wal;
19
20 import static org.apache.hadoop.hbase.wal.DefaultWALProvider.WAL_FILE_NAME_DELIMITER;
21
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25 import java.io.OutputStream;
26 import java.lang.management.MemoryUsage;
27 import java.lang.reflect.InvocationTargetException;
28 import java.net.URLEncoder;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Comparator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.NavigableMap;
35 import java.util.Set;
36 import java.util.concurrent.BlockingQueue;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.ConcurrentSkipListMap;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.concurrent.CountDownLatch;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.ExecutorService;
43 import java.util.concurrent.Executors;
44 import java.util.concurrent.LinkedBlockingQueue;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.atomic.AtomicBoolean;
47 import java.util.concurrent.atomic.AtomicInteger;
48 import java.util.concurrent.atomic.AtomicLong;
49 import java.util.concurrent.locks.ReentrantLock;
50
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53 import org.apache.hadoop.conf.Configuration;
54 import org.apache.hadoop.fs.FSDataOutputStream;
55 import org.apache.hadoop.fs.FileStatus;
56 import org.apache.hadoop.fs.FileSystem;
57 import org.apache.hadoop.fs.Path;
58 import org.apache.hadoop.fs.PathFilter;
59 import org.apache.hadoop.hbase.Cell;
60 import org.apache.hadoop.hbase.CellUtil;
61 import org.apache.hadoop.hbase.HBaseConfiguration;
62 import org.apache.hadoop.hbase.HConstants;
63 import org.apache.hadoop.hbase.HRegionInfo;
64 import org.apache.hadoop.hbase.HTableDescriptor;
65 import org.apache.hadoop.hbase.classification.InterfaceAudience;
66 import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
67 import org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil;
68 import org.apache.hadoop.hbase.util.Bytes;
69 import org.apache.hadoop.hbase.util.ClassSize;
70 import org.apache.hadoop.hbase.util.DrainBarrier;
71 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
72 import org.apache.hadoop.hbase.util.FSUtils;
73 import org.apache.hadoop.hbase.util.HasThread;
74 import org.apache.hadoop.hbase.util.Threads;
75 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
76 import org.apache.hadoop.hbase.wal.WAL;
77 import org.apache.hadoop.hbase.wal.WALFactory;
78 import org.apache.hadoop.hbase.wal.WALKey;
79 import org.apache.hadoop.hbase.wal.WALPrettyPrinter;
80 import org.apache.hadoop.hbase.wal.WALProvider.Writer;
81 import org.apache.hadoop.hbase.wal.WALSplitter;
82 import org.apache.hadoop.hdfs.DFSOutputStream;
83 import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
84 import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
85 import org.apache.hadoop.util.StringUtils;
86 import org.apache.htrace.NullScope;
87 import org.apache.htrace.Span;
88 import org.apache.htrace.Trace;
89 import org.apache.htrace.TraceScope;
90
91 import com.google.common.annotations.VisibleForTesting;
92 import com.lmax.disruptor.BlockingWaitStrategy;
93 import com.lmax.disruptor.EventHandler;
94 import com.lmax.disruptor.ExceptionHandler;
95 import com.lmax.disruptor.LifecycleAware;
96 import com.lmax.disruptor.TimeoutException;
97 import com.lmax.disruptor.dsl.Disruptor;
98 import com.lmax.disruptor.dsl.ProducerType;
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 @InterfaceAudience.Private
128 public class FSHLog implements WAL {
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 private static final Log LOG = LogFactory.getLog(FSHLog.class);
164
165 private static final int DEFAULT_SLOW_SYNC_TIME_MS = 100;
166
167 private static final int DEFAULT_WAL_SYNC_TIMEOUT_MS = 5 * 60 * 1000;
168
169
170
171
172
173
174
175
176
177 private final Disruptor<RingBufferTruck> disruptor;
178
179
180
181
182 private final ExecutorService appendExecutor;
183
184
185
186
187
188
189
190 private final RingBufferEventHandler ringBufferEventHandler;
191
192
193
194
195
196
197 private final ThreadLocal<SyncFuture> cachedSyncFutures;
198
199
200
201
202
203 private volatile long highestUnsyncedSequence = -1;
204
205
206
207
208
209
210 private final AtomicLong highestSyncedSequence = new AtomicLong(0);
211
212
213
214
215 protected final FileSystem fs;
216
217
218
219
220 private final Path fullPathLogDir;
221
222
223
224
225 private final Path fullPathArchiveDir;
226
227
228
229
230 private final PathFilter ourFiles;
231
232
233
234
235 private final String logFilePrefix;
236
237
238
239
240 private final String logFileSuffix;
241
242
243
244
245 private final String prefixPathStr;
246
247 private final WALCoprocessorHost coprocessorHost;
248
249
250
251
252 protected final Configuration conf;
253
254
255 private final List<WALActionsListener> listeners =
256 new CopyOnWriteArrayList<WALActionsListener>();
257
258 @Override
259 public void registerWALActionsListener(final WALActionsListener listener) {
260 this.listeners.add(listener);
261 }
262
263 @Override
264 public boolean unregisterWALActionsListener(final WALActionsListener listener) {
265 return this.listeners.remove(listener);
266 }
267
268 @Override
269 public WALCoprocessorHost getCoprocessorHost() {
270 return coprocessorHost;
271 }
272
273
274
275
276 private FSDataOutputStream hdfs_out;
277
278
279
280
281 private final int minTolerableReplication;
282
283 private final int slowSyncNs;
284
285 private final long walSyncTimeout;
286
287
288
289
290
291 private final AtomicInteger consecutiveLogRolls = new AtomicInteger(0);
292
293 private final int lowReplicationRollLimit;
294
295
296
297
298 private volatile boolean lowReplicationRollEnabled = true;
299
300
301
302
303
304
305 private SequenceIdAccounting sequenceIdAccounting = new SequenceIdAccounting();
306
307
308
309
310 volatile Writer writer;
311
312
313 private final DrainBarrier closeBarrier = new DrainBarrier();
314
315
316
317
318
319
320
321
322 private final ReentrantLock rollWriterLock = new ReentrantLock(true);
323
324 private volatile boolean closed = false;
325 private final AtomicBoolean shutdown = new AtomicBoolean(false);
326
327
328 private final AtomicLong filenum = new AtomicLong(-1);
329
330
331 private final AtomicInteger numEntries = new AtomicInteger(0);
332
333
334 private final long logrollsize;
335
336
337
338
339 private AtomicLong totalLogSize = new AtomicLong(0);
340
341
342
343
344
345
346 private final int maxLogs;
347
348
349 private final int closeErrorsTolerated;
350
351 private final AtomicInteger closeErrorCount = new AtomicInteger();
352
353
354 private volatile long lastTimeCheckLowReplication = EnvironmentEdgeManager.currentTime();
355
356
357
358
359
360
361 final Comparator<Path> LOG_NAME_COMPARATOR = new Comparator<Path>() {
362 @Override
363 public int compare(Path o1, Path o2) {
364 long t1 = getFileNumFromFileName(o1);
365 long t2 = getFileNumFromFileName(o2);
366 if (t1 == t2) return 0;
367 return (t1 > t2) ? 1 : -1;
368 }
369 };
370
371
372
373
374
375 private NavigableMap<Path, Map<byte[], Long>> byWalRegionSequenceIds =
376 new ConcurrentSkipListMap<Path, Map<byte[], Long>>(LOG_NAME_COMPARATOR);
377
378
379
380
381
382 static class RingBufferExceptionHandler implements ExceptionHandler {
383 @Override
384 public void handleEventException(Throwable ex, long sequence, Object event) {
385 LOG.error("Sequence=" + sequence + ", event=" + event, ex);
386 throw new RuntimeException(ex);
387 }
388
389 @Override
390 public void handleOnStartException(Throwable ex) {
391 LOG.error(ex);
392 throw new RuntimeException(ex);
393 }
394
395 @Override
396 public void handleOnShutdownException(Throwable ex) {
397 LOG.error(ex);
398 throw new RuntimeException(ex);
399 }
400 }
401
402
403
404
405
406
407
408
409
410
411 public FSHLog(final FileSystem fs, final Path root, final String logDir, final Configuration conf)
412 throws IOException {
413 this(fs, root, logDir, HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
414 }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440 public FSHLog(final FileSystem fs, final Path rootDir, final String logDir,
441 final String archiveDir, final Configuration conf,
442 final List<WALActionsListener> listeners,
443 final boolean failIfWALExists, final String prefix, final String suffix)
444 throws IOException {
445 this.fs = fs;
446 this.fullPathLogDir = new Path(rootDir, logDir);
447 this.fullPathArchiveDir = new Path(rootDir, archiveDir);
448 this.conf = conf;
449
450 if (!fs.exists(fullPathLogDir) && !fs.mkdirs(fullPathLogDir)) {
451 throw new IOException("Unable to mkdir " + fullPathLogDir);
452 }
453
454 if (!fs.exists(this.fullPathArchiveDir)) {
455 if (!fs.mkdirs(this.fullPathArchiveDir)) {
456 throw new IOException("Unable to mkdir " + this.fullPathArchiveDir);
457 }
458 }
459
460
461 this.logFilePrefix =
462 prefix == null || prefix.isEmpty() ? "wal" : URLEncoder.encode(prefix, "UTF8");
463
464 if (suffix != null && !(suffix.isEmpty()) && !(suffix.startsWith(WAL_FILE_NAME_DELIMITER))) {
465 throw new IllegalArgumentException("WAL suffix must start with '" + WAL_FILE_NAME_DELIMITER +
466 "' but instead was '" + suffix + "'");
467 }
468
469
470 FSUtils.setStoragePolicy(fs, conf, this.fullPathLogDir, HConstants.WAL_STORAGE_POLICY,
471 HConstants.DEFAULT_WAL_STORAGE_POLICY);
472 this.logFileSuffix = (suffix == null) ? "" : URLEncoder.encode(suffix, "UTF8");
473 this.prefixPathStr = new Path(fullPathLogDir,
474 logFilePrefix + WAL_FILE_NAME_DELIMITER).toString();
475
476 this.ourFiles = new PathFilter() {
477 @Override
478 public boolean accept(final Path fileName) {
479
480 final String fileNameString = fileName.toString();
481 if (!fileNameString.startsWith(prefixPathStr)) {
482 return false;
483 }
484 if (logFileSuffix.isEmpty()) {
485
486 return org.apache.commons.lang.StringUtils.isNumeric(
487 fileNameString.substring(prefixPathStr.length()));
488 } else if (!fileNameString.endsWith(logFileSuffix)) {
489 return false;
490 }
491 return true;
492 }
493 };
494
495 if (failIfWALExists) {
496 final FileStatus[] walFiles = FSUtils.listStatus(fs, fullPathLogDir, ourFiles);
497 if (null != walFiles && 0 != walFiles.length) {
498 throw new IOException("Target WAL already exists within directory " + fullPathLogDir);
499 }
500 }
501
502
503 if (listeners != null) {
504 for (WALActionsListener i: listeners) {
505 registerWALActionsListener(i);
506 }
507 }
508 this.coprocessorHost = new WALCoprocessorHost(this, conf);
509
510
511
512 final long blocksize = this.conf.getLong("hbase.regionserver.hlog.blocksize",
513 FSUtils.getDefaultBlockSize(this.fs, this.fullPathLogDir));
514 this.logrollsize =
515 (long)(blocksize * conf.getFloat("hbase.regionserver.logroll.multiplier", 0.95f));
516
517 float memstoreRatio = conf.getFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_KEY,
518 conf.getFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_OLD_KEY,
519 HeapMemorySizeUtil.DEFAULT_MEMSTORE_SIZE));
520 boolean maxLogsDefined = conf.get("hbase.regionserver.maxlogs") != null;
521 if(maxLogsDefined){
522 LOG.warn("'hbase.regionserver.maxlogs' was deprecated.");
523 }
524 this.maxLogs = conf.getInt("hbase.regionserver.maxlogs",
525 Math.max(32, calculateMaxLogFiles(memstoreRatio, logrollsize)));
526 this.minTolerableReplication = conf.getInt("hbase.regionserver.hlog.tolerable.lowreplication",
527 FSUtils.getDefaultReplication(fs, this.fullPathLogDir));
528 this.lowReplicationRollLimit =
529 conf.getInt("hbase.regionserver.hlog.lowreplication.rolllimit", 5);
530 this.closeErrorsTolerated = conf.getInt("hbase.regionserver.logroll.errors.tolerated", 0);
531 int maxHandlersCount = conf.getInt(HConstants.REGION_SERVER_HANDLER_COUNT, 200);
532
533 LOG.info("WAL configuration: blocksize=" + StringUtils.byteDesc(blocksize) +
534 ", rollsize=" + StringUtils.byteDesc(this.logrollsize) +
535 ", prefix=" + this.logFilePrefix + ", suffix=" + logFileSuffix + ", logDir=" +
536 this.fullPathLogDir + ", archiveDir=" + this.fullPathArchiveDir);
537
538
539 rollWriter();
540
541 this.slowSyncNs =
542 1000000 * conf.getInt("hbase.regionserver.hlog.slowsync.ms",
543 DEFAULT_SLOW_SYNC_TIME_MS);
544 this.walSyncTimeout = conf.getLong("hbase.regionserver.hlog.sync.timeout",
545 DEFAULT_WAL_SYNC_TIMEOUT_MS);
546
547
548
549 String hostingThreadName = Thread.currentThread().getName();
550 this.appendExecutor = Executors.
551 newSingleThreadExecutor(Threads.getNamedThreadFactory(hostingThreadName + ".append"));
552
553
554
555
556 final int preallocatedEventCount =
557 this.conf.getInt("hbase.regionserver.wal.disruptor.event.count", 1024 * 16);
558
559
560 this.disruptor =
561 new Disruptor<RingBufferTruck>(RingBufferTruck.EVENT_FACTORY, preallocatedEventCount,
562 this.appendExecutor, ProducerType.MULTI, new BlockingWaitStrategy());
563
564
565 this.disruptor.getRingBuffer().next();
566 this.ringBufferEventHandler =
567 new RingBufferEventHandler(conf.getInt("hbase.regionserver.hlog.syncer.count", 5),
568 maxHandlersCount);
569 this.disruptor.handleExceptionsWith(new RingBufferExceptionHandler());
570 this.disruptor.handleEventsWith(new RingBufferEventHandler [] {this.ringBufferEventHandler});
571 this.cachedSyncFutures = new ThreadLocal<SyncFuture>() {
572 @Override
573 protected SyncFuture initialValue() {
574 return new SyncFuture();
575 }
576 };
577
578 this.disruptor.start();
579 }
580
581 private int calculateMaxLogFiles(float memstoreSizeRatio, long logRollSize) {
582 long max = -1L;
583 final MemoryUsage usage = HeapMemorySizeUtil.safeGetHeapMemoryUsage();
584 if (usage != null) {
585 max = usage.getMax();
586 }
587 int maxLogs = Math.round(max * memstoreSizeRatio * 2 / logRollSize);
588 return maxLogs;
589 }
590
591
592
593
594
595 protected FileStatus[] getFiles() throws IOException {
596 return FSUtils.listStatus(fs, fullPathLogDir, ourFiles);
597 }
598
599
600
601
602
603
604
605
606
607 @VisibleForTesting
608 OutputStream getOutputStream() {
609 FSDataOutputStream fsdos = this.hdfs_out;
610 if (fsdos == null) return null;
611 return fsdos.getWrappedStream();
612 }
613
614 @Override
615 public byte [][] rollWriter() throws FailedLogCloseException, IOException {
616 return rollWriter(false);
617 }
618
619
620
621
622
623 private Path getNewPath() throws IOException {
624 this.filenum.set(System.currentTimeMillis());
625 Path newPath = getCurrentFileName();
626 while (fs.exists(newPath)) {
627 this.filenum.incrementAndGet();
628 newPath = getCurrentFileName();
629 }
630 return newPath;
631 }
632
633 Path getOldPath() {
634 long currentFilenum = this.filenum.get();
635 Path oldPath = null;
636 if (currentFilenum > 0) {
637
638 oldPath = computeFilename(currentFilenum);
639 }
640 return oldPath;
641 }
642
643
644
645
646
647 private void tellListenersAboutPreLogRoll(final Path oldPath, final Path newPath)
648 throws IOException {
649 coprocessorHost.preWALRoll(oldPath, newPath);
650
651 if (!this.listeners.isEmpty()) {
652 for (WALActionsListener i : this.listeners) {
653 i.preLogRoll(oldPath, newPath);
654 }
655 }
656 }
657
658
659
660
661
662 private void tellListenersAboutPostLogRoll(final Path oldPath, final Path newPath)
663 throws IOException {
664 if (!this.listeners.isEmpty()) {
665 for (WALActionsListener i : this.listeners) {
666 i.postLogRoll(oldPath, newPath);
667 }
668 }
669
670 coprocessorHost.postWALRoll(oldPath, newPath);
671 }
672
673
674
675
676
677
678 private void preemptiveSync(final ProtobufLogWriter nextWriter) {
679 long startTimeNanos = System.nanoTime();
680 try {
681 nextWriter.sync();
682 postSync(System.nanoTime() - startTimeNanos, 0);
683 } catch (IOException e) {
684
685 LOG.warn("pre-sync failed but an optimization so keep going", e);
686 }
687 }
688
689 @Override
690 public byte [][] rollWriter(boolean force) throws FailedLogCloseException, IOException {
691 rollWriterLock.lock();
692 try {
693
694 if (!force && (this.writer != null && this.numEntries.get() <= 0)) return null;
695 byte [][] regionsToFlush = null;
696 if (this.closed) {
697 LOG.debug("WAL closed. Skipping rolling of writer");
698 return regionsToFlush;
699 }
700 if (!closeBarrier.beginOp()) {
701 LOG.debug("WAL closing. Skipping rolling of writer");
702 return regionsToFlush;
703 }
704 TraceScope scope = Trace.startSpan("FSHLog.rollWriter");
705 try {
706 Path oldPath = getOldPath();
707 Path newPath = getNewPath();
708
709 Writer nextWriter = this.createWriterInstance(newPath);
710 FSDataOutputStream nextHdfsOut = null;
711 if (nextWriter instanceof ProtobufLogWriter) {
712 nextHdfsOut = ((ProtobufLogWriter)nextWriter).getStream();
713
714
715 preemptiveSync((ProtobufLogWriter)nextWriter);
716 }
717 tellListenersAboutPreLogRoll(oldPath, newPath);
718
719 newPath = replaceWriter(oldPath, newPath, nextWriter, nextHdfsOut);
720 tellListenersAboutPostLogRoll(oldPath, newPath);
721
722 if (getNumRolledLogFiles() > 0) {
723 cleanOldLogs();
724 regionsToFlush = findRegionsToForceFlush();
725 }
726 } finally {
727 closeBarrier.endOp();
728 assert scope == NullScope.INSTANCE || !scope.isDetached();
729 scope.close();
730 }
731 return regionsToFlush;
732 } finally {
733 rollWriterLock.unlock();
734 }
735 }
736
737
738
739
740
741
742
743 protected Writer createWriterInstance(final Path path) throws IOException {
744 return DefaultWALProvider.createWriter(conf, fs, path, false);
745 }
746
747
748
749
750
751 private void cleanOldLogs() throws IOException {
752 List<Path> logsToArchive = null;
753
754
755 for (Map.Entry<Path, Map<byte[], Long>> e : this.byWalRegionSequenceIds.entrySet()) {
756 Path log = e.getKey();
757 Map<byte[], Long> sequenceNums = e.getValue();
758 if (this.sequenceIdAccounting.areAllLower(sequenceNums)) {
759 if (logsToArchive == null) logsToArchive = new ArrayList<Path>();
760 logsToArchive.add(log);
761 if (LOG.isTraceEnabled()) LOG.trace("WAL file ready for archiving " + log);
762 }
763 }
764 if (logsToArchive != null) {
765 for (Path p : logsToArchive) {
766 this.totalLogSize.addAndGet(-this.fs.getFileStatus(p).getLen());
767 archiveLogFile(p);
768 this.byWalRegionSequenceIds.remove(p);
769 }
770 }
771 }
772
773
774
775
776
777
778
779
780 byte[][] findRegionsToForceFlush() throws IOException {
781 byte [][] regions = null;
782 int logCount = getNumRolledLogFiles();
783 if (logCount > this.maxLogs && logCount > 0) {
784 Map.Entry<Path, Map<byte[], Long>> firstWALEntry =
785 this.byWalRegionSequenceIds.firstEntry();
786 regions = this.sequenceIdAccounting.findLower(firstWALEntry.getValue());
787 }
788 if (regions != null) {
789 StringBuilder sb = new StringBuilder();
790 for (int i = 0; i < regions.length; i++) {
791 if (i > 0) sb.append(", ");
792 sb.append(Bytes.toStringBinary(regions[i]));
793 }
794 LOG.info("Too many WALs; count=" + logCount + ", max=" + this.maxLogs +
795 "; forcing flush of " + regions.length + " regions(s): " + sb.toString());
796 }
797 return regions;
798 }
799
800
801
802
803
804 @VisibleForTesting
805 protected void afterCreatingZigZagLatch() {}
806
807
808
809
810 @VisibleForTesting
811 protected void beforeWaitOnSafePoint() {};
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831 Path replaceWriter(final Path oldPath, final Path newPath, Writer nextWriter,
832 final FSDataOutputStream nextHdfsOut)
833 throws IOException {
834
835
836
837
838
839 SyncFuture syncFuture = null;
840 SafePointZigZagLatch zigzagLatch = null;
841 long sequence = -1L;
842 if (this.ringBufferEventHandler != null) {
843
844
845
846
847
848
849
850
851 sequence = getSequenceOnRingBuffer();
852 zigzagLatch = this.ringBufferEventHandler.attainSafePoint();
853 }
854 afterCreatingZigZagLatch();
855 TraceScope scope = Trace.startSpan("FSHFile.replaceWriter");
856 try {
857
858
859
860
861
862 try {
863 if (zigzagLatch != null) {
864
865
866 assert sequence > 0L : "Failed to get sequence from ring buffer";
867 Trace.addTimelineAnnotation("awaiting safepoint");
868 syncFuture = zigzagLatch.waitSafePoint(publishSyncOnRingBuffer(sequence));
869 }
870 } catch (FailedSyncBeforeLogCloseException e) {
871
872 if (isUnflushedEntries()) throw e;
873 LOG.warn("Failed sync-before-close but no outstanding appends; closing WAL: " +
874 e.getMessage());
875 }
876
877
878
879 try {
880 if (this.writer != null) {
881 Trace.addTimelineAnnotation("closing writer");
882 this.writer.close();
883 Trace.addTimelineAnnotation("writer closed");
884 }
885 this.closeErrorCount.set(0);
886 } catch (IOException ioe) {
887 int errors = closeErrorCount.incrementAndGet();
888 if (!isUnflushedEntries() && (errors <= this.closeErrorsTolerated)) {
889 LOG.warn("Riding over failed WAL close of " + oldPath + ", cause=\"" +
890 ioe.getMessage() + "\", errors=" + errors +
891 "; THIS FILE WAS NOT CLOSED BUT ALL EDITS SYNCED SO SHOULD BE OK");
892 } else {
893 throw ioe;
894 }
895 }
896 this.writer = nextWriter;
897 this.hdfs_out = nextHdfsOut;
898 int oldNumEntries = this.numEntries.get();
899 this.numEntries.set(0);
900 final String newPathString = (null == newPath ? null : FSUtils.getPath(newPath));
901 if (oldPath != null) {
902 this.byWalRegionSequenceIds.put(oldPath, this.sequenceIdAccounting.resetHighest());
903 long oldFileLen = this.fs.getFileStatus(oldPath).getLen();
904 this.totalLogSize.addAndGet(oldFileLen);
905 LOG.info("Rolled WAL " + FSUtils.getPath(oldPath) + " with entries=" + oldNumEntries +
906 ", filesize=" + StringUtils.byteDesc(oldFileLen) + "; new WAL " +
907 newPathString);
908 } else {
909 LOG.info("New WAL " + newPathString);
910 }
911 } catch (InterruptedException ie) {
912
913 Thread.currentThread().interrupt();
914 } catch (IOException e) {
915 long count = getUnflushedEntriesCount();
916 LOG.error("Failed close of WAL writer " + oldPath + ", unflushedEntries=" + count, e);
917 throw new FailedLogCloseException(oldPath + ", unflushedEntries=" + count, e);
918 } finally {
919 try {
920
921 if (zigzagLatch != null) {
922 zigzagLatch.releaseSafePoint();
923
924
925
926
927
928
929 if (syncFuture != null) {
930 try {
931 blockOnSync(syncFuture);
932 } catch (IOException ioe) {
933 if (LOG.isTraceEnabled()) LOG.trace("Stale sync exception", ioe);
934 }
935 }
936 }
937 } finally {
938 scope.close();
939 }
940 }
941 return newPath;
942 }
943
944 long getUnflushedEntriesCount() {
945 long highestSynced = this.highestSyncedSequence.get();
946 return highestSynced > this.highestUnsyncedSequence?
947 0: this.highestUnsyncedSequence - highestSynced;
948 }
949
950 boolean isUnflushedEntries() {
951 return getUnflushedEntriesCount() > 0;
952 }
953
954
955
956
957
958 public static Path getWALArchivePath(Path archiveDir, Path p) {
959 return new Path(archiveDir, p.getName());
960 }
961
962 private void archiveLogFile(final Path p) throws IOException {
963 Path newPath = getWALArchivePath(this.fullPathArchiveDir, p);
964
965 if (!this.listeners.isEmpty()) {
966 for (WALActionsListener i : this.listeners) {
967 i.preLogArchive(p, newPath);
968 }
969 }
970 LOG.info("Archiving " + p + " to " + newPath);
971 if (!FSUtils.renameAndSetModifyTime(this.fs, p, newPath)) {
972 throw new IOException("Unable to rename " + p + " to " + newPath);
973 }
974
975 if (!this.listeners.isEmpty()) {
976 for (WALActionsListener i : this.listeners) {
977 i.postLogArchive(p, newPath);
978 }
979 }
980 }
981
982
983
984
985
986
987
988 protected Path computeFilename(final long filenum) {
989 if (filenum < 0) {
990 throw new RuntimeException("WAL file number can't be < 0");
991 }
992 String child = logFilePrefix + WAL_FILE_NAME_DELIMITER + filenum + logFileSuffix;
993 return new Path(fullPathLogDir, child);
994 }
995
996
997
998
999
1000
1001 public Path getCurrentFileName() {
1002 return computeFilename(this.filenum.get());
1003 }
1004
1005 @Override
1006 public String toString() {
1007 return "FSHLog " + logFilePrefix + ":" + logFileSuffix + "(num " + filenum + ")";
1008 }
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018 protected long getFileNumFromFileName(Path fileName) {
1019 if (fileName == null) throw new IllegalArgumentException("file name can't be null");
1020 if (!ourFiles.accept(fileName)) {
1021 throw new IllegalArgumentException("The log file " + fileName +
1022 " doesn't belong to this WAL. (" + toString() + ")");
1023 }
1024 final String fileNameString = fileName.toString();
1025 String chompedPath = fileNameString.substring(prefixPathStr.length(),
1026 (fileNameString.length() - logFileSuffix.length()));
1027 return Long.parseLong(chompedPath);
1028 }
1029
1030 @Override
1031 public void close() throws IOException {
1032 shutdown();
1033 final FileStatus[] files = getFiles();
1034 if (null != files && 0 != files.length) {
1035 for (FileStatus file : files) {
1036 Path p = getWALArchivePath(this.fullPathArchiveDir, file.getPath());
1037
1038 if (!this.listeners.isEmpty()) {
1039 for (WALActionsListener i : this.listeners) {
1040 i.preLogArchive(file.getPath(), p);
1041 }
1042 }
1043
1044 if (!FSUtils.renameAndSetModifyTime(fs, file.getPath(), p)) {
1045 throw new IOException("Unable to rename " + file.getPath() + " to " + p);
1046 }
1047
1048 if (!this.listeners.isEmpty()) {
1049 for (WALActionsListener i : this.listeners) {
1050 i.postLogArchive(file.getPath(), p);
1051 }
1052 }
1053 }
1054 LOG.debug("Moved " + files.length + " WAL file(s) to " +
1055 FSUtils.getPath(this.fullPathArchiveDir));
1056 }
1057 LOG.info("Closed WAL: " + toString());
1058 }
1059
1060 @Override
1061 public void shutdown() throws IOException {
1062 if (shutdown.compareAndSet(false, true)) {
1063 try {
1064
1065 closeBarrier.stopAndDrainOps();
1066 } catch (InterruptedException e) {
1067 LOG.error("Exception while waiting for cache flushes and log rolls", e);
1068 Thread.currentThread().interrupt();
1069 }
1070
1071
1072
1073
1074 if (this.disruptor != null) {
1075 long timeoutms = conf.getLong("hbase.wal.disruptor.shutdown.timeout.ms", 60000);
1076 try {
1077 this.disruptor.shutdown(timeoutms, TimeUnit.MILLISECONDS);
1078 } catch (TimeoutException e) {
1079 LOG.warn("Timed out bringing down disruptor after " + timeoutms + "ms; forcing halt " +
1080 "(It is a problem if this is NOT an ABORT! -- DATALOSS!!!!)");
1081 this.disruptor.halt();
1082 this.disruptor.shutdown();
1083 }
1084 }
1085
1086 if (this.appendExecutor != null) this.appendExecutor.shutdown();
1087
1088
1089 if (!this.listeners.isEmpty()) {
1090 for (WALActionsListener i : this.listeners) {
1091 i.logCloseRequested();
1092 }
1093 }
1094 this.closed = true;
1095 if (LOG.isDebugEnabled()) {
1096 LOG.debug("Closing WAL writer in " + FSUtils.getPath(fullPathLogDir));
1097 }
1098 if (this.writer != null) {
1099 this.writer.close();
1100 this.writer = null;
1101 }
1102 }
1103 }
1104
1105 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH_EXCEPTION",
1106 justification="Will never be null")
1107 @Override
1108 public long append(final HTableDescriptor htd, final HRegionInfo hri, final WALKey key,
1109 final WALEdit edits, final boolean inMemstore) throws IOException {
1110 if (this.closed) throw new IOException("Cannot append; log is closed");
1111
1112
1113 TraceScope scope = Trace.startSpan("FSHLog.append");
1114
1115
1116
1117
1118 FSWALEntry entry = null;
1119 long sequence = this.disruptor.getRingBuffer().next();
1120 try {
1121 RingBufferTruck truck = this.disruptor.getRingBuffer().get(sequence);
1122
1123 entry = new FSWALEntry(sequence, key, edits, htd, hri, inMemstore);
1124 truck.loadPayload(entry, scope.detach());
1125 } finally {
1126 this.disruptor.getRingBuffer().publish(sequence);
1127 }
1128 return sequence;
1129 }
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146 private class SyncRunner extends HasThread {
1147 private volatile long sequence;
1148
1149 private final BlockingQueue<SyncFuture> syncFutures;
1150 private volatile SyncFuture takeSyncFuture = null;
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162 SyncRunner(final String name, final int maxHandlersCount) {
1163 super(name);
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177 this.syncFutures = new LinkedBlockingQueue<SyncFuture>(maxHandlersCount * 3);
1178 }
1179
1180 void offer(final long sequence, final SyncFuture [] syncFutures, final int syncFutureCount) {
1181
1182 this.sequence = sequence;
1183 for (int i = 0; i < syncFutureCount; ++i) {
1184 this.syncFutures.add(syncFutures[i]);
1185 }
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195 private int releaseSyncFuture(final SyncFuture syncFuture, final long currentSequence,
1196 final Throwable t) {
1197 if (!syncFuture.done(currentSequence, t)) throw new IllegalStateException();
1198
1199 return 1;
1200 }
1201
1202
1203
1204
1205
1206
1207
1208 private int releaseSyncFutures(final long currentSequence, final Throwable t) {
1209 int syncCount = 0;
1210 for (SyncFuture syncFuture; (syncFuture = this.syncFutures.peek()) != null;) {
1211 if (syncFuture.getRingBufferSequence() > currentSequence) break;
1212 releaseSyncFuture(syncFuture, currentSequence, t);
1213 if (!this.syncFutures.remove(syncFuture)) {
1214 throw new IllegalStateException(syncFuture.toString());
1215 }
1216 syncCount++;
1217 }
1218 return syncCount;
1219 }
1220
1221
1222
1223
1224
1225 private long updateHighestSyncedSequence(long sequence) {
1226 long currentHighestSyncedSequence;
1227
1228 do {
1229 currentHighestSyncedSequence = highestSyncedSequence.get();
1230 if (currentHighestSyncedSequence >= sequence) {
1231
1232
1233 sequence = currentHighestSyncedSequence;
1234 break;
1235 }
1236 } while (!highestSyncedSequence.compareAndSet(currentHighestSyncedSequence, sequence));
1237 return sequence;
1238 }
1239
1240 boolean areSyncFuturesReleased() {
1241
1242
1243 return syncFutures.size() <= 0
1244 && takeSyncFuture == null;
1245 }
1246
1247 public void run() {
1248 long currentSequence;
1249 while (!isInterrupted()) {
1250 int syncCount = 0;
1251
1252 try {
1253 while (true) {
1254 takeSyncFuture = null;
1255
1256 takeSyncFuture = this.syncFutures.take();
1257 currentSequence = this.sequence;
1258 long syncFutureSequence = takeSyncFuture.getRingBufferSequence();
1259 if (syncFutureSequence > currentSequence) {
1260 throw new IllegalStateException("currentSequence=" + syncFutureSequence +
1261 ", syncFutureSequence=" + syncFutureSequence);
1262 }
1263
1264 long currentHighestSyncedSequence = highestSyncedSequence.get();
1265 if (currentSequence < currentHighestSyncedSequence) {
1266 syncCount += releaseSyncFuture(takeSyncFuture, currentHighestSyncedSequence, null);
1267
1268 continue;
1269 }
1270 break;
1271 }
1272
1273
1274 TraceScope scope = Trace.continueSpan(takeSyncFuture.getSpan());
1275 long start = System.nanoTime();
1276 Throwable lastException = null;
1277 try {
1278 Trace.addTimelineAnnotation("syncing writer");
1279 writer.sync();
1280 Trace.addTimelineAnnotation("writer synced");
1281 currentSequence = updateHighestSyncedSequence(currentSequence);
1282 } catch (IOException e) {
1283 LOG.error("Error syncing, request close of WAL", e);
1284 lastException = e;
1285 } catch (Exception e) {
1286 LOG.warn("UNEXPECTED", e);
1287 lastException = e;
1288 } finally {
1289
1290 takeSyncFuture.setSpan(scope.detach());
1291
1292 syncCount += releaseSyncFuture(takeSyncFuture, currentSequence, lastException);
1293
1294 syncCount += releaseSyncFutures(currentSequence, lastException);
1295 if (lastException != null) requestLogRoll();
1296 else checkLogRoll();
1297 }
1298 postSync(System.nanoTime() - start, syncCount);
1299 } catch (InterruptedException e) {
1300
1301 Thread.currentThread().interrupt();
1302 } catch (Throwable t) {
1303 LOG.warn("UNEXPECTED, continuing", t);
1304 }
1305 }
1306 }
1307 }
1308
1309
1310
1311
1312 public void checkLogRoll() {
1313
1314 if (!rollWriterLock.tryLock()) return;
1315 boolean lowReplication;
1316 try {
1317 lowReplication = checkLowReplication();
1318 } finally {
1319 rollWriterLock.unlock();
1320 }
1321 try {
1322 if (lowReplication || writer != null && writer.getLength() > logrollsize) {
1323 requestLogRoll(lowReplication);
1324 }
1325 } catch (IOException e) {
1326 LOG.warn("Writer.getLength() failed; continuing", e);
1327 }
1328 }
1329
1330
1331
1332
1333 private boolean checkLowReplication() {
1334 boolean logRollNeeded = false;
1335 this.lastTimeCheckLowReplication = EnvironmentEdgeManager.currentTime();
1336
1337
1338 try {
1339 int numCurrentReplicas = getLogReplication();
1340 if (numCurrentReplicas != 0 && numCurrentReplicas < this.minTolerableReplication) {
1341 if (this.lowReplicationRollEnabled) {
1342 if (this.consecutiveLogRolls.get() < this.lowReplicationRollLimit) {
1343 LOG.warn("HDFS pipeline error detected. " + "Found "
1344 + numCurrentReplicas + " replicas but expecting no less than "
1345 + this.minTolerableReplication + " replicas. "
1346 + " Requesting close of WAL. current pipeline: "
1347 + Arrays.toString(getPipeLine()));
1348 logRollNeeded = true;
1349
1350
1351
1352 this.consecutiveLogRolls.getAndIncrement();
1353 } else {
1354 LOG.warn("Too many consecutive RollWriter requests, it's a sign of "
1355 + "the total number of live datanodes is lower than the tolerable replicas.");
1356 this.consecutiveLogRolls.set(0);
1357 this.lowReplicationRollEnabled = false;
1358 }
1359 }
1360 } else if (numCurrentReplicas >= this.minTolerableReplication) {
1361 if (!this.lowReplicationRollEnabled) {
1362
1363
1364
1365 if (this.numEntries.get() <= 1) {
1366 return logRollNeeded;
1367 }
1368
1369
1370 this.lowReplicationRollEnabled = true;
1371 LOG.info("LowReplication-Roller was enabled.");
1372 }
1373 }
1374 } catch (Exception e) {
1375 LOG.warn("DFSOutputStream.getNumCurrentReplicas failed because of " + e +
1376 ", continuing...");
1377 }
1378 return logRollNeeded;
1379 }
1380
1381 private SyncFuture publishSyncOnRingBuffer(long sequence) {
1382 return publishSyncOnRingBuffer(sequence, null);
1383 }
1384
1385 private long getSequenceOnRingBuffer() {
1386 return this.disruptor.getRingBuffer().next();
1387 }
1388
1389 private SyncFuture publishSyncOnRingBuffer(Span span) {
1390 long sequence = this.disruptor.getRingBuffer().next();
1391 return publishSyncOnRingBuffer(sequence, span);
1392 }
1393
1394 private SyncFuture publishSyncOnRingBuffer(long sequence, Span span) {
1395 SyncFuture syncFuture = getSyncFuture(sequence, span);
1396 try {
1397 RingBufferTruck truck = this.disruptor.getRingBuffer().get(sequence);
1398 truck.loadPayload(syncFuture);
1399 } finally {
1400 this.disruptor.getRingBuffer().publish(sequence);
1401 }
1402 return syncFuture;
1403 }
1404
1405
1406 private Span publishSyncThenBlockOnCompletion(Span span) throws IOException {
1407 return blockOnSync(publishSyncOnRingBuffer(span));
1408 }
1409
1410 private Span blockOnSync(final SyncFuture syncFuture) throws IOException {
1411
1412 try {
1413 syncFuture.get(walSyncTimeout);
1414 return syncFuture.getSpan();
1415 } catch (TimeoutIOException tioe) {
1416
1417
1418
1419 this.cachedSyncFutures.remove();
1420 throw tioe;
1421 } catch (InterruptedException ie) {
1422 LOG.warn("Interrupted", ie);
1423 throw convertInterruptedExceptionToIOException(ie);
1424 } catch (ExecutionException e) {
1425 throw ensureIOException(e.getCause());
1426 }
1427 }
1428
1429 private IOException convertInterruptedExceptionToIOException(final InterruptedException ie) {
1430 Thread.currentThread().interrupt();
1431 IOException ioe = new InterruptedIOException();
1432 ioe.initCause(ie);
1433 return ioe;
1434 }
1435
1436 private SyncFuture getSyncFuture(final long sequence, Span span) {
1437 return cachedSyncFutures.get().reset(sequence);
1438 }
1439
1440 private void postSync(final long timeInNanos, final int handlerSyncs) {
1441 if (timeInNanos > this.slowSyncNs) {
1442 String msg =
1443 new StringBuilder().append("Slow sync cost: ")
1444 .append(timeInNanos / 1000000).append(" ms, current pipeline: ")
1445 .append(Arrays.toString(getPipeLine())).toString();
1446 Trace.addTimelineAnnotation(msg);
1447 LOG.info(msg);
1448 }
1449 if (!listeners.isEmpty()) {
1450 for (WALActionsListener listener : listeners) {
1451 listener.postSync(timeInNanos, handlerSyncs);
1452 }
1453 }
1454 }
1455
1456 private long postAppend(final Entry e, final long elapsedTime) throws IOException {
1457 long len = 0;
1458 if (!listeners.isEmpty()) {
1459 for (Cell cell : e.getEdit().getCells()) {
1460 len += CellUtil.estimatedSerializedSizeOf(cell);
1461 }
1462 for (WALActionsListener listener : listeners) {
1463 listener.postAppend(len, elapsedTime, e.getKey(), e.getEdit());
1464 }
1465 }
1466 return len;
1467 }
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482 @VisibleForTesting
1483 int getLogReplication() {
1484 try {
1485
1486 if (this.hdfs_out instanceof HdfsDataOutputStream) {
1487 return ((HdfsDataOutputStream) this.hdfs_out).getCurrentBlockReplication();
1488 }
1489 } catch (IOException e) {
1490 LOG.info("", e);
1491 }
1492 return 0;
1493 }
1494
1495 @Override
1496 public void sync() throws IOException {
1497 TraceScope scope = Trace.startSpan("FSHLog.sync");
1498 try {
1499 scope = Trace.continueSpan(publishSyncThenBlockOnCompletion(scope.detach()));
1500 } finally {
1501 assert scope == NullScope.INSTANCE || !scope.isDetached();
1502 scope.close();
1503 }
1504 }
1505
1506 @Override
1507 public void sync(long txid) throws IOException {
1508 if (this.highestSyncedSequence.get() >= txid){
1509
1510 return;
1511 }
1512 TraceScope scope = Trace.startSpan("FSHLog.sync");
1513 try {
1514 scope = Trace.continueSpan(publishSyncThenBlockOnCompletion(scope.detach()));
1515 } finally {
1516 assert scope == NullScope.INSTANCE || !scope.isDetached();
1517 scope.close();
1518 }
1519 }
1520
1521
1522 public void requestLogRoll() {
1523 requestLogRoll(false);
1524 }
1525
1526 private void requestLogRoll(boolean tooFewReplicas) {
1527 if (!this.listeners.isEmpty()) {
1528 for (WALActionsListener i: this.listeners) {
1529 i.logRollRequested(tooFewReplicas);
1530 }
1531 }
1532 }
1533
1534
1535
1536 public int getNumRolledLogFiles() {
1537 return byWalRegionSequenceIds.size();
1538 }
1539
1540
1541
1542 public int getNumLogFiles() {
1543
1544 return getNumRolledLogFiles() + 1;
1545 }
1546
1547
1548
1549 public long getLogFileSize() {
1550 return this.totalLogSize.get();
1551 }
1552
1553 @Override
1554 public Long startCacheFlush(final byte[] encodedRegionName, Set<byte[]> families) {
1555 if (!closeBarrier.beginOp()) {
1556 LOG.info("Flush not started for " + Bytes.toString(encodedRegionName) + "; server closing.");
1557 return null;
1558 }
1559 return this.sequenceIdAccounting.startCacheFlush(encodedRegionName, families);
1560 }
1561
1562 @Override
1563 public void completeCacheFlush(final byte [] encodedRegionName) {
1564 this.sequenceIdAccounting.completeCacheFlush(encodedRegionName);
1565 closeBarrier.endOp();
1566 }
1567
1568 @Override
1569 public void abortCacheFlush(byte[] encodedRegionName) {
1570 this.sequenceIdAccounting.abortCacheFlush(encodedRegionName);
1571 closeBarrier.endOp();
1572 }
1573
1574 @VisibleForTesting
1575 boolean isLowReplicationRollEnabled() {
1576 return lowReplicationRollEnabled;
1577 }
1578
1579 public static final long FIXED_OVERHEAD = ClassSize.align(
1580 ClassSize.OBJECT + (5 * ClassSize.REFERENCE) +
1581 ClassSize.ATOMIC_INTEGER + Bytes.SIZEOF_INT + (3 * Bytes.SIZEOF_LONG));
1582
1583
1584 private static void split(final Configuration conf, final Path p) throws IOException {
1585 FileSystem fs = FSUtils.getWALFileSystem(conf);
1586 if (!fs.exists(p)) {
1587 throw new FileNotFoundException(p.toString());
1588 }
1589 if (!fs.getFileStatus(p).isDirectory()) {
1590 throw new IOException(p + " is not a directory");
1591 }
1592
1593 final Path baseDir = FSUtils.getWALRootDir(conf);
1594 final Path archiveDir = new Path(baseDir, HConstants.HREGION_OLDLOGDIR_NAME);
1595 WALSplitter.split(baseDir, p, archiveDir, fs, conf, WALFactory.getInstance(conf));
1596 }
1597
1598
1599 @Override
1600 public long getEarliestMemstoreSeqNum(byte[] encodedRegionName) {
1601
1602 return this.sequenceIdAccounting.getLowestSequenceId(encodedRegionName);
1603 }
1604
1605 @Override
1606 public long getEarliestMemstoreSeqNum(byte[] encodedRegionName, byte[] familyName) {
1607
1608
1609
1610
1611
1612
1613
1614
1615 return this.sequenceIdAccounting.getLowestSequenceId(encodedRegionName, familyName);
1616 }
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644 static class SafePointZigZagLatch {
1645
1646
1647
1648 private volatile CountDownLatch safePointAttainedLatch = new CountDownLatch(1);
1649
1650
1651
1652 private volatile CountDownLatch safePointReleasedLatch = new CountDownLatch(1);
1653
1654 private void checkIfSyncFailed(SyncFuture syncFuture) throws FailedSyncBeforeLogCloseException {
1655 if (syncFuture.isThrowable()) {
1656 throw new FailedSyncBeforeLogCloseException(syncFuture.getThrowable());
1657 }
1658 }
1659
1660
1661
1662
1663
1664
1665
1666
1667 SyncFuture waitSafePoint(SyncFuture syncFuture) throws InterruptedException,
1668 FailedSyncBeforeLogCloseException {
1669 while (!this.safePointAttainedLatch.await(1, TimeUnit.MILLISECONDS)) {
1670 checkIfSyncFailed(syncFuture);
1671 }
1672 checkIfSyncFailed(syncFuture);
1673 return syncFuture;
1674 }
1675
1676
1677
1678
1679
1680
1681
1682 void safePointAttained() throws InterruptedException {
1683 this.safePointAttainedLatch.countDown();
1684 this.safePointReleasedLatch.await();
1685 }
1686
1687
1688
1689
1690
1691 void releaseSafePoint() {
1692 this.safePointReleasedLatch.countDown();
1693 }
1694
1695
1696
1697
1698 boolean isCocked() {
1699 return this.safePointAttainedLatch.getCount() > 0 &&
1700 this.safePointReleasedLatch.getCount() > 0;
1701 }
1702 }
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727 class RingBufferEventHandler implements EventHandler<RingBufferTruck>, LifecycleAware {
1728 private final SyncRunner [] syncRunners;
1729 private final SyncFuture [] syncFutures;
1730
1731
1732 private volatile int syncFuturesCount = 0;
1733 private volatile SafePointZigZagLatch zigzagLatch;
1734
1735
1736
1737
1738 private Exception exception = null;
1739
1740
1741
1742 private final Object safePointWaiter = new Object();
1743 private volatile boolean shutdown = false;
1744
1745
1746
1747
1748 private int syncRunnerIndex;
1749
1750 RingBufferEventHandler(final int syncRunnerCount, final int maxHandlersCount) {
1751 this.syncFutures = new SyncFuture[maxHandlersCount];
1752 this.syncRunners = new SyncRunner[syncRunnerCount];
1753 for (int i = 0; i < syncRunnerCount; i++) {
1754 this.syncRunners[i] = new SyncRunner("sync." + i, maxHandlersCount);
1755 }
1756 }
1757
1758 private void cleanupOutstandingSyncsOnException(final long sequence, final Exception e) {
1759
1760 for (int i = 0; i < this.syncFuturesCount; i++) this.syncFutures[i].done(sequence, e);
1761 this.syncFuturesCount = 0;
1762 }
1763
1764
1765
1766
1767 private boolean isOutstandingSyncs() {
1768
1769 for (int i = 0; i < this.syncFuturesCount; i++) {
1770 if (!this.syncFutures[i].isDone()) return true;
1771 }
1772
1773 return false;
1774 }
1775
1776 private boolean isOutstandingSyncsFromRunners() {
1777
1778 for (SyncRunner syncRunner: syncRunners) {
1779 if(syncRunner.isAlive() && !syncRunner.areSyncFuturesReleased()) {
1780 return true;
1781 }
1782 }
1783 return false;
1784 }
1785
1786 @Override
1787
1788 public void onEvent(final RingBufferTruck truck, final long sequence, boolean endOfBatch)
1789 throws Exception {
1790
1791
1792
1793
1794
1795
1796
1797
1798 try {
1799 if (truck.hasSyncFuturePayload()) {
1800 this.syncFutures[this.syncFuturesCount++] = truck.unloadSyncFuturePayload();
1801
1802 if (this.syncFuturesCount == this.syncFutures.length) endOfBatch = true;
1803 } else if (truck.hasFSWALEntryPayload()) {
1804 TraceScope scope = Trace.continueSpan(truck.unloadSpanPayload());
1805 try {
1806 FSWALEntry entry = truck.unloadFSWALEntryPayload();
1807 if (this.exception != null) {
1808
1809
1810
1811
1812
1813 entry.stampRegionSequenceId();
1814
1815 return;
1816 }
1817 append(entry);
1818 } catch (Exception e) {
1819
1820 this.exception = e;
1821
1822
1823
1824 cleanupOutstandingSyncsOnException(sequence,
1825 this.exception instanceof DamagedWALException ? this.exception
1826 : new DamagedWALException("On sync", this.exception));
1827
1828 return;
1829 } finally {
1830 assert scope == NullScope.INSTANCE || !scope.isDetached();
1831 scope.close();
1832 }
1833 } else {
1834
1835 cleanupOutstandingSyncsOnException(sequence,
1836 new IllegalStateException("Neither append nor sync"));
1837
1838 return;
1839 }
1840
1841
1842
1843 if (this.exception == null) {
1844
1845
1846 if (!endOfBatch || this.syncFuturesCount <= 0) return;
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856 this.syncRunnerIndex = (this.syncRunnerIndex + 1) % this.syncRunners.length;
1857 try {
1858
1859
1860 this.syncRunners[this.syncRunnerIndex].offer(sequence, this.syncFutures,
1861 this.syncFuturesCount);
1862 } catch (Exception e) {
1863
1864 requestLogRoll();
1865 this.exception = new DamagedWALException("Failed offering sync", e);
1866 }
1867 }
1868
1869 if (this.exception != null) {
1870 cleanupOutstandingSyncsOnException(sequence,
1871 this.exception instanceof DamagedWALException?
1872 this.exception:
1873 new DamagedWALException("On sync", this.exception));
1874 }
1875 attainSafePoint(sequence);
1876 this.syncFuturesCount = 0;
1877 } catch (Throwable t) {
1878 LOG.error("UNEXPECTED!!! syncFutures.length=" + this.syncFutures.length, t);
1879 }
1880 }
1881
1882 SafePointZigZagLatch attainSafePoint() {
1883 this.zigzagLatch = new SafePointZigZagLatch();
1884 return this.zigzagLatch;
1885 }
1886
1887
1888
1889
1890
1891 private void attainSafePoint(final long currentSequence) {
1892 if (this.zigzagLatch == null || !this.zigzagLatch.isCocked()) return;
1893
1894 beforeWaitOnSafePoint();
1895 try {
1896
1897
1898
1899 while ((!this.shutdown && this.zigzagLatch.isCocked()
1900 && highestSyncedSequence.get() < currentSequence &&
1901
1902
1903 isOutstandingSyncs())
1904
1905 || isOutstandingSyncsFromRunners()) {
1906 synchronized (this.safePointWaiter) {
1907 this.safePointWaiter.wait(0, 1);
1908 }
1909 }
1910
1911
1912
1913 this.exception = null;
1914 this.zigzagLatch.safePointAttained();
1915 } catch (InterruptedException e) {
1916 LOG.warn("Interrupted ", e);
1917 Thread.currentThread().interrupt();
1918 }
1919 }
1920
1921
1922
1923
1924
1925
1926 void append(final FSWALEntry entry) throws Exception {
1927
1928 atHeadOfRingBufferEventHandlerAppend();
1929
1930 long start = EnvironmentEdgeManager.currentTime();
1931 byte [] encodedRegionName = entry.getKey().getEncodedRegionName();
1932 long regionSequenceId = WALKey.NO_SEQUENCE_ID;
1933 try {
1934
1935
1936
1937 regionSequenceId = entry.stampRegionSequenceId();
1938
1939
1940
1941 if (entry.getEdit().isEmpty()) {
1942 return;
1943 }
1944
1945
1946 if (!coprocessorHost.preWALWrite(entry.getHRegionInfo(), entry.getKey(),
1947 entry.getEdit())) {
1948 if (entry.getEdit().isReplay()) {
1949
1950 entry.getKey().setScopes(null);
1951 }
1952 }
1953 if (!listeners.isEmpty()) {
1954 for (WALActionsListener i: listeners) {
1955
1956 i.visitLogEntryBeforeWrite(entry.getHTableDescriptor(), entry.getKey(),
1957 entry.getEdit());
1958 }
1959 }
1960
1961 writer.append(entry);
1962 assert highestUnsyncedSequence < entry.getSequence();
1963 highestUnsyncedSequence = entry.getSequence();
1964 sequenceIdAccounting.update(encodedRegionName, entry.getFamilyNames(), regionSequenceId,
1965 entry.isInMemstore());
1966 coprocessorHost.postWALWrite(entry.getHRegionInfo(), entry.getKey(), entry.getEdit());
1967
1968 postAppend(entry, EnvironmentEdgeManager.currentTime() - start);
1969 } catch (Exception e) {
1970 String msg = "Append sequenceId=" + regionSequenceId + ", requesting roll of WAL";
1971 LOG.warn(msg, e);
1972 requestLogRoll();
1973 throw new DamagedWALException(msg, e);
1974 }
1975 numEntries.incrementAndGet();
1976 }
1977
1978 @Override
1979 public void onStart() {
1980 for (SyncRunner syncRunner: this.syncRunners) syncRunner.start();
1981 }
1982
1983 @Override
1984 public void onShutdown() {
1985 for (SyncRunner syncRunner: this.syncRunners) syncRunner.interrupt();
1986 }
1987 }
1988
1989
1990
1991
1992 @VisibleForTesting
1993 void atHeadOfRingBufferEventHandlerAppend() {
1994
1995 }
1996
1997 private static IOException ensureIOException(final Throwable t) {
1998 return (t instanceof IOException)? (IOException)t: new IOException(t);
1999 }
2000
2001 private static void usage() {
2002 System.err.println("Usage: FSHLog <ARGS>");
2003 System.err.println("Arguments:");
2004 System.err.println(" --dump Dump textual representation of passed one or more files");
2005 System.err.println(" For example: " +
2006 "FSHLog --dump hdfs://example.com:9000/hbase/.logs/MACHINE/LOGFILE");
2007 System.err.println(" --split Split the passed directory of WAL logs");
2008 System.err.println(" For example: " +
2009 "FSHLog --split hdfs://example.com:9000/hbase/.logs/DIR");
2010 }
2011
2012
2013
2014
2015
2016
2017
2018
2019 public static void main(String[] args) throws IOException {
2020 if (args.length < 2) {
2021 usage();
2022 System.exit(-1);
2023 }
2024
2025 if (args[0].compareTo("--dump") == 0) {
2026 WALPrettyPrinter.run(Arrays.copyOfRange(args, 1, args.length));
2027 } else if (args[0].compareTo("--perf") == 0) {
2028 LOG.fatal("Please use the WALPerformanceEvaluation tool instead. i.e.:");
2029 LOG.fatal("\thbase org.apache.hadoop.hbase.wal.WALPerformanceEvaluation --iterations " +
2030 args[1]);
2031 System.exit(-1);
2032 } else if (args[0].compareTo("--split") == 0) {
2033 Configuration conf = HBaseConfiguration.create();
2034 for (int i = 1; i < args.length; i++) {
2035 try {
2036 Path logPath = new Path(args[i]);
2037 FSUtils.setFsDefault(conf, logPath);
2038 split(conf, logPath);
2039 } catch (IOException t) {
2040 t.printStackTrace(System.err);
2041 System.exit(-1);
2042 }
2043 }
2044 } else {
2045 usage();
2046 System.exit(-1);
2047 }
2048 }
2049
2050
2051
2052
2053 @VisibleForTesting
2054 DatanodeInfo[] getPipeLine() {
2055 if (this.hdfs_out != null) {
2056 if (this.hdfs_out.getWrappedStream() instanceof DFSOutputStream) {
2057 return ((DFSOutputStream) this.hdfs_out.getWrappedStream()).getPipeline();
2058 }
2059 }
2060 return new DatanodeInfo[0];
2061 }
2062
2063
2064
2065
2066
2067 public long getLastTimeCheckLowReplication() {
2068 return this.lastTimeCheckLowReplication;
2069 }
2070 }