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