1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.wal;
20
21 import java.io.EOFException;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InterruptedIOException;
25 import java.text.ParseException;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.NavigableSet;
34 import java.util.Set;
35 import java.util.TreeMap;
36 import java.util.TreeSet;
37 import java.util.UUID;
38 import java.util.concurrent.Callable;
39 import java.util.concurrent.CompletionService;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.ExecutorCompletionService;
43 import java.util.concurrent.Future;
44 import java.util.concurrent.ThreadFactory;
45 import java.util.concurrent.ThreadPoolExecutor;
46 import java.util.concurrent.TimeUnit;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 import java.util.concurrent.atomic.AtomicLong;
49 import java.util.concurrent.atomic.AtomicReference;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55 import org.apache.hadoop.conf.Configuration;
56 import org.apache.hadoop.fs.FileAlreadyExistsException;
57 import org.apache.hadoop.fs.FileStatus;
58 import org.apache.hadoop.fs.FileSystem;
59 import org.apache.hadoop.fs.Path;
60 import org.apache.hadoop.fs.PathFilter;
61 import org.apache.hadoop.hbase.Cell;
62 import org.apache.hadoop.hbase.CellScanner;
63 import org.apache.hadoop.hbase.CellUtil;
64 import org.apache.hadoop.hbase.CoordinatedStateException;
65 import org.apache.hadoop.hbase.CoordinatedStateManager;
66 import org.apache.hadoop.hbase.HBaseConfiguration;
67 import org.apache.hadoop.hbase.HConstants;
68 import org.apache.hadoop.hbase.HRegionInfo;
69 import org.apache.hadoop.hbase.HRegionLocation;
70 import org.apache.hadoop.hbase.RemoteExceptionHandler;
71 import org.apache.hadoop.hbase.ServerName;
72 import org.apache.hadoop.hbase.TableName;
73 import org.apache.hadoop.hbase.TableNotFoundException;
74 import org.apache.hadoop.hbase.TableStateManager;
75 import org.apache.hadoop.hbase.classification.InterfaceAudience;
76 import org.apache.hadoop.hbase.client.ConnectionUtils;
77 import org.apache.hadoop.hbase.client.Delete;
78 import org.apache.hadoop.hbase.client.Durability;
79 import org.apache.hadoop.hbase.client.HConnection;
80 import org.apache.hadoop.hbase.client.HConnectionManager;
81 import org.apache.hadoop.hbase.client.Mutation;
82 import org.apache.hadoop.hbase.client.Put;
83 import org.apache.hadoop.hbase.coordination.BaseCoordinatedStateManager;
84 import org.apache.hadoop.hbase.coordination.ZKSplitLogManagerCoordination;
85 import org.apache.hadoop.hbase.exceptions.RegionOpeningException;
86 import org.apache.hadoop.hbase.io.HeapSize;
87 import org.apache.hadoop.hbase.master.SplitLogManager;
88 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
89 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
90 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
91 import org.apache.hadoop.hbase.protobuf.RequestConverter;
92 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService.BlockingInterface;
93 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
94 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse;
95 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.WALEntry;
96 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto.MutationType;
97 import org.apache.hadoop.hbase.protobuf.generated.ClusterStatusProtos.RegionStoreSequenceIds;
98 import org.apache.hadoop.hbase.protobuf.generated.ClusterStatusProtos.StoreSequenceId;
99 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
100 import org.apache.hadoop.hbase.protobuf.generated.WALProtos.CompactionDescriptor;
101 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
102 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode;
103 import org.apache.hadoop.hbase.regionserver.HRegion;
104 import org.apache.hadoop.hbase.regionserver.LastSequenceId;
105
106 import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
107 import org.apache.hadoop.hbase.regionserver.wal.HLogKey;
108 import org.apache.hadoop.hbase.regionserver.wal.WALCellCodec;
109 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
110 import org.apache.hadoop.hbase.regionserver.wal.WALEditsReplaySink;
111 import org.apache.hadoop.hbase.util.Bytes;
112 import org.apache.hadoop.hbase.util.CancelableProgressable;
113 import org.apache.hadoop.hbase.util.ClassSize;
114 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
115 import org.apache.hadoop.hbase.util.FSUtils;
116 import org.apache.hadoop.hbase.util.Pair;
117 import org.apache.hadoop.hbase.util.Threads;
118 import org.apache.hadoop.hbase.wal.WAL.Entry;
119 import org.apache.hadoop.hbase.wal.WAL.Reader;
120 import org.apache.hadoop.hbase.wal.WALProvider.Writer;
121 import org.apache.hadoop.hbase.zookeeper.ZKSplitLog;
122 import org.apache.hadoop.io.MultipleIOException;
123
124 import com.google.common.annotations.VisibleForTesting;
125 import com.google.common.base.Preconditions;
126 import com.google.common.collect.Lists;
127 import com.google.protobuf.ServiceException;
128 import com.google.protobuf.TextFormat;
129
130
131
132
133
134
135 @InterfaceAudience.Private
136 public class WALSplitter {
137 private static final Log LOG = LogFactory.getLog(WALSplitter.class);
138
139
140 public static final boolean SPLIT_SKIP_ERRORS_DEFAULT = false;
141
142
143 protected final Path walDir;
144 protected final FileSystem walFS;
145 protected final Configuration conf;
146
147
148
149 PipelineController controller;
150 OutputSink outputSink;
151 EntryBuffers entryBuffers;
152
153 private Set<TableName> disablingOrDisabledTables =
154 new HashSet<TableName>();
155 private BaseCoordinatedStateManager csm;
156 private final WALFactory walFactory;
157
158 private MonitoredTask status;
159
160
161 protected final LastSequenceId sequenceIdChecker;
162
163 protected boolean distributedLogReplay;
164
165 private final boolean splitWriterCreationBounded;
166
167
168
169 protected Map<String, Long> lastFlushedSequenceIds = new ConcurrentHashMap<String, Long>();
170
171
172 protected Map<String, Map<byte[], Long>> regionMaxSeqIdInStores =
173 new ConcurrentHashMap<String, Map<byte[], Long>>();
174
175
176 protected String failedServerName = "";
177
178
179 private final int numWriterThreads;
180
181
182 private final int minBatchSize;
183
184
185 private FileStatus fileBeingSplit;
186
187 public final static String SPLIT_WRITER_CREATION_BOUNDED = "hbase.split.writer.creation.bounded";
188
189 @VisibleForTesting
190 WALSplitter(final WALFactory factory, Configuration conf, Path walDir,
191 FileSystem walFS, LastSequenceId idChecker,
192 CoordinatedStateManager csm, RecoveryMode mode) {
193 this.conf = HBaseConfiguration.create(conf);
194 String codecClassName = conf
195 .get(WALCellCodec.WAL_CELL_CODEC_CLASS_KEY, WALCellCodec.class.getName());
196 this.conf.set(HConstants.RPC_CODEC_CONF_KEY, codecClassName);
197 this.walDir = walDir;
198 this.walFS = walFS;
199 this.sequenceIdChecker = idChecker;
200 this.csm = (BaseCoordinatedStateManager)csm;
201 this.walFactory = factory;
202 this.controller = new PipelineController();
203 this.splitWriterCreationBounded = conf.getBoolean(SPLIT_WRITER_CREATION_BOUNDED, false);
204 entryBuffers = new EntryBuffers(controller,
205 this.conf.getInt("hbase.regionserver.hlog.splitlog.buffersize",
206 128*1024*1024), splitWriterCreationBounded);
207
208
209
210 this.minBatchSize = this.conf.getInt("hbase.regionserver.wal.logreplay.batch.size", 64);
211 this.distributedLogReplay = (RecoveryMode.LOG_REPLAY == mode);
212
213 this.numWriterThreads = this.conf.getInt("hbase.regionserver.hlog.splitlog.writer.threads", 3);
214 if (csm != null && this.distributedLogReplay) {
215 outputSink = new LogReplayOutputSink(controller, entryBuffers, numWriterThreads);
216 } else {
217 if (this.distributedLogReplay) {
218 LOG.info("ZooKeeperWatcher is passed in as NULL so disable distrubitedLogRepaly.");
219 }
220 this.distributedLogReplay = false;
221 if(splitWriterCreationBounded){
222 outputSink = new BoundedLogWriterCreationOutputSink(controller,
223 entryBuffers, numWriterThreads);
224 }else {
225 outputSink = new LogRecoveredEditsOutputSink(controller, entryBuffers, numWriterThreads);
226 }
227 }
228
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public static boolean splitLogFile(Path walDir, FileStatus logfile, FileSystem walFS,
248 Configuration conf, CancelableProgressable reporter, LastSequenceId idChecker,
249 CoordinatedStateManager cp, RecoveryMode mode, final WALFactory factory) throws IOException {
250 WALSplitter s = new WALSplitter(factory, conf, walDir, walFS, idChecker, cp, mode);
251 return s.splitLogFile(logfile, reporter);
252 }
253
254
255
256
257
258 public static List<Path> split(Path walRootDir, Path logDir, Path oldLogDir,
259 FileSystem walFs, Configuration conf, final WALFactory factory) throws IOException {
260 final FileStatus[] logfiles = SplitLogManager.getFileList(conf,
261 Collections.singletonList(logDir), null);
262 List<Path> splits = new ArrayList<Path>();
263 if (logfiles != null && logfiles.length > 0) {
264 for (FileStatus logfile: logfiles) {
265 WALSplitter s = new WALSplitter(factory, conf, walRootDir, walFs, null, null,
266 RecoveryMode.LOG_SPLITTING);
267 if (s.splitLogFile(logfile, null)) {
268 finishSplitLogFile(walRootDir, oldLogDir, logfile.getPath(), conf);
269 if (s.outputSink.splits != null) {
270 splits.addAll(s.outputSink.splits);
271 }
272 }
273 }
274 }
275 if (!walFs.delete(logDir, true)) {
276 throw new IOException("Unable to delete src dir: " + logDir);
277 }
278 return splits;
279 }
280
281
282
283
284
285 @VisibleForTesting
286 boolean splitLogFile(FileStatus logfile, CancelableProgressable reporter) throws IOException {
287 Preconditions.checkState(status == null);
288 Preconditions.checkArgument(logfile.isFile(),
289 "passed in file status is for something other than a regular file.");
290 boolean isCorrupted = false;
291 boolean skipErrors = conf.getBoolean("hbase.hlog.split.skip.errors",
292 SPLIT_SKIP_ERRORS_DEFAULT);
293 int interval = conf.getInt("hbase.splitlog.report.interval.loglines", 1024);
294 Path logPath = logfile.getPath();
295 boolean outputSinkStarted = false;
296 boolean progress_failed = false;
297 int editsCount = 0;
298 int editsSkipped = 0;
299
300 status =
301 TaskMonitor.get().createStatus(
302 "Splitting log file " + logfile.getPath() + "into a temporary staging area.");
303 Reader in = null;
304 this.fileBeingSplit = logfile;
305 try {
306 long logLength = logfile.getLen();
307 LOG.info("Splitting wal: " + logPath + ", length=" + logLength);
308 LOG.info("DistributedLogReplay = " + this.distributedLogReplay);
309 status.setStatus("Opening log file");
310 if (reporter != null && !reporter.progress()) {
311 progress_failed = true;
312 return false;
313 }
314 try {
315 in = getReader(logfile, skipErrors, reporter);
316 } catch (CorruptedLogFileException e) {
317 LOG.warn("Could not get reader, corrupted log file " + logPath, e);
318 ZKSplitLog.markCorrupted(walDir, logfile.getPath().getName(), walFS);
319 isCorrupted = true;
320 }
321 if (in == null) {
322 LOG.warn("Nothing to split in log file " + logPath);
323 return true;
324 }
325 if (csm != null) {
326 try {
327 TableStateManager tsm = csm.getTableStateManager();
328 disablingOrDisabledTables = tsm.getTablesInStates(
329 ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING);
330 } catch (CoordinatedStateException e) {
331 throw new IOException("Can't get disabling/disabled tables", e);
332 }
333 }
334 int numOpenedFilesBeforeReporting = conf.getInt("hbase.splitlog.report.openedfiles", 3);
335 int numOpenedFilesLastCheck = 0;
336 outputSink.setReporter(reporter);
337 outputSink.startWriterThreads();
338 outputSinkStarted = true;
339 Entry entry;
340 Long lastFlushedSequenceId = -1L;
341 ServerName serverName = DefaultWALProvider.getServerNameFromWALDirectoryName(logPath);
342 failedServerName = (serverName == null) ? "" : serverName.getServerName();
343 while ((entry = getNextLogLine(in, logPath, skipErrors)) != null) {
344 byte[] region = entry.getKey().getEncodedRegionName();
345 String encodedRegionNameAsStr = Bytes.toString(region);
346 lastFlushedSequenceId = lastFlushedSequenceIds.get(encodedRegionNameAsStr);
347 if (lastFlushedSequenceId == null) {
348 if (this.distributedLogReplay) {
349 RegionStoreSequenceIds ids =
350 csm.getSplitLogWorkerCoordination().getRegionFlushedSequenceId(failedServerName,
351 encodedRegionNameAsStr);
352 if (ids != null) {
353 lastFlushedSequenceId = ids.getLastFlushedSequenceId();
354 if (LOG.isDebugEnabled()) {
355 LOG.debug("DLR Last flushed sequenceid for " + encodedRegionNameAsStr + ": " +
356 TextFormat.shortDebugString(ids));
357 }
358 }
359 } else if (sequenceIdChecker != null) {
360 RegionStoreSequenceIds ids = sequenceIdChecker.getLastSequenceId(region);
361 Map<byte[], Long> maxSeqIdInStores = new TreeMap<byte[], Long>(Bytes.BYTES_COMPARATOR);
362 for (StoreSequenceId storeSeqId : ids.getStoreSequenceIdList()) {
363 maxSeqIdInStores.put(storeSeqId.getFamilyName().toByteArray(),
364 storeSeqId.getSequenceId());
365 }
366 regionMaxSeqIdInStores.put(encodedRegionNameAsStr, maxSeqIdInStores);
367 lastFlushedSequenceId = ids.getLastFlushedSequenceId();
368 if (LOG.isDebugEnabled()) {
369 LOG.debug("DLS Last flushed sequenceid for " + encodedRegionNameAsStr + ": " +
370 TextFormat.shortDebugString(ids));
371 }
372 }
373 if (lastFlushedSequenceId == null) {
374 lastFlushedSequenceId = -1L;
375 }
376 lastFlushedSequenceIds.put(encodedRegionNameAsStr, lastFlushedSequenceId);
377 }
378 if (lastFlushedSequenceId >= entry.getKey().getLogSeqNum()) {
379 editsSkipped++;
380 continue;
381 }
382
383 if (entry.getEdit().isMetaEdit() && !outputSink.keepRegionEvent(entry)) {
384 editsSkipped++;
385 continue;
386 }
387 entryBuffers.appendEntry(entry);
388 editsCount++;
389 int moreWritersFromLastCheck = this.getNumOpenWriters() - numOpenedFilesLastCheck;
390
391 if (editsCount % interval == 0
392 || moreWritersFromLastCheck > numOpenedFilesBeforeReporting) {
393 numOpenedFilesLastCheck = this.getNumOpenWriters();
394 String countsStr = (editsCount - (editsSkipped + outputSink.getSkippedEdits()))
395 + " edits, skipped " + editsSkipped + " edits.";
396 status.setStatus("Split " + countsStr);
397 if (reporter != null && !reporter.progress()) {
398 progress_failed = true;
399 return false;
400 }
401 }
402 }
403 } catch (InterruptedException ie) {
404 IOException iie = new InterruptedIOException();
405 iie.initCause(ie);
406 throw iie;
407 } catch (CorruptedLogFileException e) {
408 LOG.warn("Could not parse, corrupted log file " + logPath, e);
409 csm.getSplitLogWorkerCoordination().markCorrupted(walDir,
410 logfile.getPath().getName(), walFS);
411 isCorrupted = true;
412 } catch (IOException e) {
413 e = RemoteExceptionHandler.checkIOException(e);
414 throw e;
415 } finally {
416 LOG.debug("Finishing writing output logs and closing down.");
417 try {
418 if (null != in) {
419 in.close();
420 }
421 } catch (IOException exception) {
422 LOG.warn("Could not close wal reader: " + exception.getMessage());
423 LOG.debug("exception details", exception);
424 }
425 try {
426 if (outputSinkStarted) {
427
428
429 progress_failed = true;
430 progress_failed = outputSink.finishWritingAndClose() == null;
431 }
432 } finally {
433 String msg =
434 "Processed " + editsCount + " edits across " + outputSink.getNumberOfRecoveredRegions()
435 + " regions; edits skipped=" + editsSkipped + "; log file=" + logPath +
436 ", length=" + logfile.getLen() +
437 ", corrupted=" + isCorrupted + ", progress failed=" + progress_failed;
438 LOG.info(msg);
439 status.markComplete(msg);
440 }
441 }
442 return !progress_failed;
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 public static void finishSplitLogFile(String logfile,
457 Configuration conf) throws IOException {
458 Path walDir = FSUtils.getWALRootDir(conf);
459 Path oldLogDir = new Path(walDir, HConstants.HREGION_OLDLOGDIR_NAME);
460 Path logPath;
461 if (FSUtils.isStartingWithPath(walDir, logfile)) {
462 logPath = new Path(logfile);
463 } else {
464 logPath = new Path(walDir, logfile);
465 }
466 finishSplitLogFile(walDir, oldLogDir, logPath, conf);
467 }
468
469 private static void finishSplitLogFile(Path walDir, Path oldLogDir,
470 Path logPath, Configuration conf) throws IOException {
471 List<Path> processedLogs = new ArrayList<Path>();
472 List<Path> corruptedLogs = new ArrayList<Path>();
473 FileSystem walFS = walDir.getFileSystem(conf);
474 if (ZKSplitLog.isCorrupted(walDir, logPath.getName(), walFS)) {
475 corruptedLogs.add(logPath);
476 } else {
477 processedLogs.add(logPath);
478 }
479 archiveLogs(corruptedLogs, processedLogs, oldLogDir, walFS, conf);
480 Path stagingDir = ZKSplitLog.getSplitLogDir(walDir, logPath.getName());
481 walFS.delete(stagingDir, true);
482 }
483
484
485
486
487
488
489
490
491
492
493
494
495
496 private static void archiveLogs(
497 final List<Path> corruptedLogs,
498 final List<Path> processedLogs, final Path oldLogDir,
499 final FileSystem walFS, final Configuration conf) throws IOException {
500 final Path corruptDir = new Path(FSUtils.getWALRootDir(conf), conf.get(
501 "hbase.regionserver.hlog.splitlog.corrupt.dir", HConstants.CORRUPT_DIR_NAME));
502
503 if (!walFS.mkdirs(corruptDir)) {
504 LOG.info("Unable to mkdir " + corruptDir);
505 }
506 walFS.mkdirs(oldLogDir);
507
508
509
510 for (Path corrupted : corruptedLogs) {
511 Path p = new Path(corruptDir, corrupted.getName());
512 if (walFS.exists(corrupted)) {
513 if (!walFS.rename(corrupted, p)) {
514 LOG.warn("Unable to move corrupted log " + corrupted + " to " + p);
515 } else {
516 LOG.warn("Moved corrupted log " + corrupted + " to " + p);
517 }
518 }
519 }
520
521 for (Path p : processedLogs) {
522 Path newPath = FSHLog.getWALArchivePath(oldLogDir, p);
523 if (walFS.exists(p)) {
524 if (!FSUtils.renameAndSetModifyTime(walFS, p, newPath)) {
525 LOG.warn("Unable to move " + p + " to " + newPath);
526 } else {
527 LOG.info("Archived processed log " + p + " to " + newPath);
528 }
529 }
530 }
531 }
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 @SuppressWarnings("deprecation")
547 @VisibleForTesting
548 static Path getRegionSplitEditsPath(final Entry logEntry, String fileNameBeingSplit,
549 String tmpDirName, Configuration conf) throws IOException {
550 FileSystem walFS = FSUtils.getWALFileSystem(conf);
551 Path tableDir = FSUtils.getWALTableDir(conf, logEntry.getKey().getTablename());
552 String encodedRegionName = Bytes.toString(logEntry.getKey().getEncodedRegionName());
553 Path regionDir = HRegion.getRegionDir(tableDir, encodedRegionName);
554 Path dir = getRegionDirRecoveredEditsDir(regionDir);
555
556 if (walFS.exists(dir) && walFS.isFile(dir)) {
557 Path tmp = new Path(tmpDirName);
558 if (!walFS.exists(tmp)) {
559 walFS.mkdirs(tmp);
560 }
561 tmp = new Path(tmp,
562 HConstants.RECOVERED_EDITS_DIR + "_" + encodedRegionName);
563 LOG.warn("Found existing old file: " + dir + ". It could be some "
564 + "leftover of an old installation. It should be a folder instead. "
565 + "So moving it to " + tmp);
566 if (!walFS.rename(dir, tmp)) {
567 LOG.warn("Failed to sideline old file " + dir);
568 }
569 }
570
571 if (!walFS.exists(dir) && !walFS.mkdirs(dir)) {
572 LOG.warn("mkdir failed on " + dir);
573 }
574
575
576
577 String fileName = formatRecoveredEditsFileName(logEntry.getKey().getLogSeqNum());
578 fileName = getTmpRecoveredEditsFileName(fileName + "-" + fileNameBeingSplit);
579 return new Path(dir, fileName);
580 }
581
582 private static String getTmpRecoveredEditsFileName(String fileName) {
583 return fileName + RECOVERED_LOG_TMPFILE_SUFFIX;
584 }
585
586
587
588
589
590
591
592
593
594 private static Path getCompletedRecoveredEditsFilePath(Path srcPath,
595 long maximumEditLogSeqNum) {
596 String fileName = formatRecoveredEditsFileName(maximumEditLogSeqNum);
597 return new Path(srcPath.getParent(), fileName);
598 }
599
600 @VisibleForTesting
601 static String formatRecoveredEditsFileName(final long seqid) {
602 return String.format("%019d", seqid);
603 }
604
605 private static final Pattern EDITFILES_NAME_PATTERN = Pattern.compile("-?[0-9]+");
606 private static final String RECOVERED_LOG_TMPFILE_SUFFIX = ".temp";
607
608
609
610
611
612
613
614 public static Path getRegionDirRecoveredEditsDir(final Path regionDir) {
615 return new Path(regionDir, HConstants.RECOVERED_EDITS_DIR);
616 }
617
618
619
620
621
622
623
624
625
626
627 public static NavigableSet<Path> getSplitEditFilesSorted(final FileSystem walFS,
628 final Path regionDir) throws IOException {
629 NavigableSet<Path> filesSorted = new TreeSet<Path>();
630 Path editsdir = getRegionDirRecoveredEditsDir(regionDir);
631 if (!walFS.exists(editsdir)) {
632 return filesSorted;
633 }
634 FileStatus[] files = FSUtils.listStatus(walFS, editsdir, new PathFilter() {
635 @Override
636 public boolean accept(Path p) {
637 boolean result = false;
638 try {
639
640
641
642
643 Matcher m = EDITFILES_NAME_PATTERN.matcher(p.getName());
644 result = walFS.isFile(p) && m.matches();
645
646
647 if (p.getName().endsWith(RECOVERED_LOG_TMPFILE_SUFFIX)) {
648 result = false;
649 }
650
651 if (isSequenceIdFile(p)) {
652 result = false;
653 }
654 } catch (IOException e) {
655 LOG.warn("Failed isFile check on " + p);
656 }
657 return result;
658 }
659 });
660 if (files == null) {
661 return filesSorted;
662 }
663 for (FileStatus status : files) {
664 filesSorted.add(status.getPath());
665 }
666 return filesSorted;
667 }
668
669
670
671
672
673
674
675
676
677
678 public static Path moveAsideBadEditsFile(final FileSystem walFS, final Path edits)
679 throws IOException {
680 Path moveAsideName = new Path(edits.getParent(), edits.getName() + "."
681 + System.currentTimeMillis());
682 if (!walFS.rename(edits, moveAsideName)) {
683 LOG.warn("Rename failed from " + edits + " to " + moveAsideName);
684 }
685 return moveAsideName;
686 }
687
688 private static final String SEQUENCE_ID_FILE_SUFFIX = ".seqid";
689 private static final String OLD_SEQUENCE_ID_FILE_SUFFIX = "_seqid";
690 private static final int SEQUENCE_ID_FILE_SUFFIX_LENGTH = SEQUENCE_ID_FILE_SUFFIX.length();
691
692
693
694
695 @VisibleForTesting
696 public static boolean isSequenceIdFile(final Path file) {
697 return file.getName().endsWith(SEQUENCE_ID_FILE_SUFFIX)
698 || file.getName().endsWith(OLD_SEQUENCE_ID_FILE_SUFFIX);
699 }
700
701
702
703
704
705
706
707
708
709
710 public static long writeRegionSequenceIdFile(final FileSystem walFS, final Path regionDir,
711 long newSeqId, long saftyBumper) throws IOException {
712
713 Path editsdir = WALSplitter.getRegionDirRecoveredEditsDir(regionDir);
714 long maxSeqId = 0;
715 FileStatus[] files = null;
716 if (walFS.exists(editsdir)) {
717 files = FSUtils.listStatus(walFS, editsdir, new PathFilter() {
718 @Override
719 public boolean accept(Path p) {
720 return isSequenceIdFile(p);
721 }
722 });
723 if (files != null) {
724 for (FileStatus status : files) {
725 String fileName = status.getPath().getName();
726 try {
727 Long tmpSeqId = Long.parseLong(fileName.substring(0, fileName.length()
728 - SEQUENCE_ID_FILE_SUFFIX_LENGTH));
729 maxSeqId = Math.max(tmpSeqId, maxSeqId);
730 } catch (NumberFormatException ex) {
731 LOG.warn("Invalid SeqId File Name=" + fileName);
732 }
733 }
734 }
735 }
736 if (maxSeqId > newSeqId) {
737 newSeqId = maxSeqId;
738 }
739 newSeqId += saftyBumper;
740
741
742 Path newSeqIdFile = new Path(editsdir, newSeqId + SEQUENCE_ID_FILE_SUFFIX);
743 if (newSeqId != maxSeqId) {
744 try {
745 if (!walFS.createNewFile(newSeqIdFile) && !walFS.exists(newSeqIdFile)) {
746 throw new IOException("Failed to create SeqId file:" + newSeqIdFile);
747 }
748 if (LOG.isDebugEnabled()) {
749 LOG.debug("Wrote region seqId=" + newSeqIdFile + " to file, newSeqId=" + newSeqId
750 + ", maxSeqId=" + maxSeqId);
751 }
752 } catch (FileAlreadyExistsException ignored) {
753
754 }
755 }
756
757 if (files != null) {
758 for (FileStatus status : files) {
759 if (newSeqIdFile.equals(status.getPath())) {
760 continue;
761 }
762 walFS.delete(status.getPath(), false);
763 }
764 }
765 return newSeqId;
766 }
767
768
769
770
771
772
773
774
775
776 protected Reader getReader(FileStatus file, boolean skipErrors, CancelableProgressable reporter)
777 throws IOException, CorruptedLogFileException {
778 Path path = file.getPath();
779 long length = file.getLen();
780 Reader in;
781
782
783
784
785 if (length <= 0) {
786 LOG.warn("File " + path + " might be still open, length is 0");
787 }
788
789 try {
790 FSUtils.getInstance(walFS, conf).recoverFileLease(walFS, path, conf, reporter);
791 try {
792 in = getReader(path, reporter);
793 } catch (EOFException e) {
794 if (length <= 0) {
795
796
797
798
799
800 LOG.warn("Could not open " + path + " for reading. File is empty", e);
801 return null;
802 } else {
803
804 return null;
805 }
806 }
807 } catch (IOException e) {
808 if (e instanceof FileNotFoundException) {
809
810 LOG.warn("File " + path + " doesn't exist anymore.", e);
811 return null;
812 }
813 if (!skipErrors || e instanceof InterruptedIOException) {
814 throw e;
815 }
816 CorruptedLogFileException t =
817 new CorruptedLogFileException("skipErrors=true Could not open wal " +
818 path + " ignoring");
819 t.initCause(e);
820 throw t;
821 }
822 return in;
823 }
824
825 static private Entry getNextLogLine(Reader in, Path path, boolean skipErrors)
826 throws CorruptedLogFileException, IOException {
827 try {
828 return in.next();
829 } catch (EOFException eof) {
830
831 LOG.info("EOF from wal " + path + ". continuing");
832 return null;
833 } catch (IOException e) {
834
835
836 if (e.getCause() != null &&
837 (e.getCause() instanceof ParseException ||
838 e.getCause() instanceof org.apache.hadoop.fs.ChecksumException)) {
839 LOG.warn("Parse exception " + e.getCause().toString() + " from wal "
840 + path + ". continuing");
841 return null;
842 }
843 if (!skipErrors) {
844 throw e;
845 }
846 CorruptedLogFileException t =
847 new CorruptedLogFileException("skipErrors=true Ignoring exception" +
848 " while parsing wal " + path + ". Marking as corrupted");
849 t.initCause(e);
850 throw t;
851 }
852 }
853
854
855
856
857
858 protected Writer createWriter(Path logfile)
859 throws IOException {
860 return walFactory.createRecoveredEditsWriter(walFS, logfile);
861 }
862
863
864
865
866
867 protected Reader getReader(Path curLogFile, CancelableProgressable reporter) throws IOException {
868 return walFactory.createReader(walFS, curLogFile, reporter);
869 }
870
871
872
873
874 private int getNumOpenWriters() {
875 int result = 0;
876 if (this.outputSink != null) {
877 result += this.outputSink.getNumOpenWriters();
878 }
879 return result;
880 }
881
882
883
884
885 public static class PipelineController {
886
887
888 AtomicReference<Throwable> thrown = new AtomicReference<Throwable>();
889
890
891
892 public final Object dataAvailable = new Object();
893
894 void writerThreadError(Throwable t) {
895 thrown.compareAndSet(null, t);
896 }
897
898
899
900
901 void checkForErrors() throws IOException {
902 Throwable thrown = this.thrown.get();
903 if (thrown == null) return;
904 if (thrown instanceof IOException) {
905 throw new IOException(thrown);
906 } else {
907 throw new RuntimeException(thrown);
908 }
909 }
910 }
911
912
913
914
915
916
917
918
919 public static class EntryBuffers {
920 PipelineController controller;
921
922 Map<byte[], RegionEntryBuffer> buffers =
923 new TreeMap<byte[], RegionEntryBuffer>(Bytes.BYTES_COMPARATOR);
924
925
926
927
928 Set<byte[]> currentlyWriting = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
929
930 long totalBuffered = 0;
931 final long maxHeapUsage;
932 boolean splitWriterCreationBounded;
933
934
935 public EntryBuffers(PipelineController controller, long maxHeapUsage) {
936 this(controller, maxHeapUsage, false);
937 }
938
939 public EntryBuffers(PipelineController controller, long maxHeapUsage,
940 boolean splitWriterCreationBounded) {
941 this.controller = controller;
942 this.maxHeapUsage = maxHeapUsage;
943 this.splitWriterCreationBounded = splitWriterCreationBounded;
944 }
945
946
947
948
949
950
951
952
953 public void appendEntry(Entry entry) throws InterruptedException, IOException {
954 WALKey key = entry.getKey();
955
956 RegionEntryBuffer buffer;
957 long incrHeap;
958 synchronized (this) {
959 buffer = buffers.get(key.getEncodedRegionName());
960 if (buffer == null) {
961 buffer = new RegionEntryBuffer(key.getTablename(), key.getEncodedRegionName());
962 buffers.put(key.getEncodedRegionName(), buffer);
963 }
964 incrHeap= buffer.appendEntry(entry);
965 }
966
967
968 synchronized (controller.dataAvailable) {
969 totalBuffered += incrHeap;
970 while (totalBuffered > maxHeapUsage && controller.thrown.get() == null) {
971 LOG.debug("Used " + totalBuffered + " bytes of buffered edits, waiting for IO threads...");
972 controller.dataAvailable.wait(2000);
973 }
974 controller.dataAvailable.notifyAll();
975 }
976 controller.checkForErrors();
977 }
978
979
980
981
982 synchronized RegionEntryBuffer getChunkToWrite() {
983
984
985
986
987 if(splitWriterCreationBounded && totalBuffered < maxHeapUsage){
988 return null;
989 }
990
991 long biggestSize = 0;
992 byte[] biggestBufferKey = null;
993
994 for (Map.Entry<byte[], RegionEntryBuffer> entry : buffers.entrySet()) {
995 long size = entry.getValue().heapSize();
996 if (size > biggestSize && (!currentlyWriting.contains(entry.getKey()))) {
997 biggestSize = size;
998 biggestBufferKey = entry.getKey();
999 }
1000 }
1001 if (biggestBufferKey == null) {
1002 return null;
1003 }
1004
1005 RegionEntryBuffer buffer = buffers.remove(biggestBufferKey);
1006 currentlyWriting.add(biggestBufferKey);
1007 return buffer;
1008 }
1009
1010 void doneWriting(RegionEntryBuffer buffer) {
1011 synchronized (this) {
1012 boolean removed = currentlyWriting.remove(buffer.encodedRegionName);
1013 assert removed;
1014 }
1015 long size = buffer.heapSize();
1016
1017 synchronized (controller.dataAvailable) {
1018 totalBuffered -= size;
1019
1020 controller.dataAvailable.notifyAll();
1021 }
1022 }
1023
1024 synchronized boolean isRegionCurrentlyWriting(byte[] region) {
1025 return currentlyWriting.contains(region);
1026 }
1027
1028 public void waitUntilDrained() {
1029 synchronized (controller.dataAvailable) {
1030 while (totalBuffered > 0) {
1031 try {
1032 controller.dataAvailable.wait(2000);
1033 } catch (InterruptedException e) {
1034 LOG.warn("Got interrupted while waiting for EntryBuffers is drained");
1035 Thread.interrupted();
1036 break;
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043
1044
1045
1046
1047
1048
1049 public static class RegionEntryBuffer implements HeapSize {
1050 long heapInBuffer = 0;
1051 List<Entry> entryBuffer;
1052 TableName tableName;
1053 byte[] encodedRegionName;
1054
1055 RegionEntryBuffer(TableName tableName, byte[] region) {
1056 this.tableName = tableName;
1057 this.encodedRegionName = region;
1058 this.entryBuffer = new LinkedList<Entry>();
1059 }
1060
1061 long appendEntry(Entry entry) {
1062 internify(entry);
1063 entryBuffer.add(entry);
1064 long incrHeap = entry.getEdit().heapSize() +
1065 ClassSize.align(2 * ClassSize.REFERENCE) +
1066 0;
1067 heapInBuffer += incrHeap;
1068 return incrHeap;
1069 }
1070
1071 private void internify(Entry entry) {
1072 WALKey k = entry.getKey();
1073 k.internTableName(this.tableName);
1074 k.internEncodedRegionName(this.encodedRegionName);
1075 }
1076
1077 @Override
1078 public long heapSize() {
1079 return heapInBuffer;
1080 }
1081
1082 public byte[] getEncodedRegionName() {
1083 return encodedRegionName;
1084 }
1085
1086 public List<Entry> getEntryBuffer() {
1087 return entryBuffer;
1088 }
1089
1090 public TableName getTableName() {
1091 return tableName;
1092 }
1093 }
1094
1095 public static class WriterThread extends Thread {
1096 private volatile boolean shouldStop = false;
1097 private PipelineController controller;
1098 private EntryBuffers entryBuffers;
1099 private OutputSink outputSink = null;
1100
1101 WriterThread(PipelineController controller, EntryBuffers entryBuffers, OutputSink sink, int i){
1102 super(Thread.currentThread().getName() + "-Writer-" + i);
1103 this.controller = controller;
1104 this.entryBuffers = entryBuffers;
1105 outputSink = sink;
1106 }
1107
1108 @Override
1109 public void run() {
1110 try {
1111 doRun();
1112 } catch (Throwable t) {
1113 LOG.error("Exiting thread", t);
1114 controller.writerThreadError(t);
1115 }
1116 }
1117
1118 private void doRun() throws IOException {
1119 if (LOG.isTraceEnabled()) LOG.trace("Writer thread starting");
1120 while (true) {
1121 RegionEntryBuffer buffer = entryBuffers.getChunkToWrite();
1122 if (buffer == null) {
1123
1124 synchronized (controller.dataAvailable) {
1125 if (shouldStop && !this.outputSink.flush()) {
1126 return;
1127 }
1128 try {
1129 controller.dataAvailable.wait(500);
1130 } catch (InterruptedException ie) {
1131 if (!shouldStop) {
1132 throw new RuntimeException(ie);
1133 }
1134 }
1135 }
1136 continue;
1137 }
1138
1139 assert buffer != null;
1140 try {
1141 writeBuffer(buffer);
1142 } finally {
1143 entryBuffers.doneWriting(buffer);
1144 }
1145 }
1146 }
1147
1148 private void writeBuffer(RegionEntryBuffer buffer) throws IOException {
1149 outputSink.append(buffer);
1150 }
1151
1152 void finish() {
1153 synchronized (controller.dataAvailable) {
1154 shouldStop = true;
1155 controller.dataAvailable.notifyAll();
1156 }
1157 }
1158 }
1159
1160
1161
1162
1163
1164 public static abstract class OutputSink {
1165
1166 protected PipelineController controller;
1167 protected EntryBuffers entryBuffers;
1168
1169 protected ConcurrentHashMap<String, SinkWriter> writers = new ConcurrentHashMap<>();
1170 protected ConcurrentHashMap<String, Long> regionMaximumEditLogSeqNum =
1171 new ConcurrentHashMap<>();
1172
1173 protected final List<WriterThread> writerThreads = Lists.newArrayList();
1174
1175
1176 protected final Set<byte[]> blacklistedRegions = Collections
1177 .synchronizedSet(new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR));
1178
1179 protected boolean closeAndCleanCompleted = false;
1180
1181 protected boolean writersClosed = false;
1182
1183 protected final int numThreads;
1184
1185 protected CancelableProgressable reporter = null;
1186
1187 protected AtomicLong skippedEdits = new AtomicLong();
1188
1189 protected List<Path> splits = null;
1190
1191 public OutputSink(PipelineController controller, EntryBuffers entryBuffers, int numWriters) {
1192 numThreads = numWriters;
1193 this.controller = controller;
1194 this.entryBuffers = entryBuffers;
1195 }
1196
1197 void setReporter(CancelableProgressable reporter) {
1198 this.reporter = reporter;
1199 }
1200
1201
1202
1203
1204 public synchronized void startWriterThreads() {
1205 for (int i = 0; i < numThreads; i++) {
1206 WriterThread t = new WriterThread(controller, entryBuffers, this, i);
1207 t.start();
1208 writerThreads.add(t);
1209 }
1210 }
1211
1212
1213
1214
1215
1216 void updateRegionMaximumEditLogSeqNum(Entry entry) {
1217
1218 synchronized (regionMaximumEditLogSeqNum) {
1219 String encodedRegionName = Bytes.toString(entry.getKey().getEncodedRegionName());
1220 Long currentMaxSeqNum = regionMaximumEditLogSeqNum.get(encodedRegionName);
1221 if (currentMaxSeqNum == null || entry.getKey().getLogSeqNum() > currentMaxSeqNum) {
1222 regionMaximumEditLogSeqNum.put(encodedRegionName, entry.getKey()
1223 .getLogSeqNum());
1224 }
1225 }
1226 }
1227
1228 Long getRegionMaximumEditLogSeqNum(byte[] region) {
1229 return regionMaximumEditLogSeqNum.get(Bytes.toString(region));
1230 }
1231
1232
1233
1234
1235 int getNumOpenWriters() {
1236 return this.writers.size();
1237 }
1238
1239 long getSkippedEdits() {
1240 return this.skippedEdits.get();
1241 }
1242
1243
1244
1245
1246
1247
1248 protected boolean finishWriting(boolean interrupt) throws IOException {
1249 LOG.debug("Waiting for split writer threads to finish");
1250 boolean progress_failed = false;
1251 for (WriterThread t : writerThreads) {
1252 t.finish();
1253 }
1254 if (interrupt) {
1255 for (WriterThread t : writerThreads) {
1256 t.interrupt();
1257 }
1258 }
1259
1260 for (WriterThread t : writerThreads) {
1261 if (!progress_failed && reporter != null && !reporter.progress()) {
1262 progress_failed = true;
1263 }
1264 try {
1265 t.join();
1266 } catch (InterruptedException ie) {
1267 IOException iie = new InterruptedIOException();
1268 iie.initCause(ie);
1269 throw iie;
1270 }
1271 }
1272 controller.checkForErrors();
1273 LOG.info(this.writerThreads.size() + " split writers finished; closing...");
1274 return (!progress_failed);
1275 }
1276
1277 public abstract List<Path> finishWritingAndClose() throws IOException;
1278
1279
1280
1281
1282 public abstract Map<byte[], Long> getOutputCounts();
1283
1284
1285
1286
1287 public abstract int getNumberOfRecoveredRegions();
1288
1289
1290
1291
1292
1293 public abstract void append(RegionEntryBuffer buffer) throws IOException;
1294
1295
1296
1297
1298
1299 public boolean flush() throws IOException {
1300 return false;
1301 }
1302
1303
1304
1305
1306
1307
1308
1309 public abstract boolean keepRegionEvent(Entry entry);
1310 }
1311
1312
1313
1314
1315 class LogRecoveredEditsOutputSink extends OutputSink {
1316
1317 public LogRecoveredEditsOutputSink(PipelineController controller, EntryBuffers entryBuffers,
1318 int numWriters) {
1319
1320
1321
1322
1323
1324 super(controller, entryBuffers, numWriters);
1325 }
1326
1327
1328
1329
1330
1331 @Override
1332 public List<Path> finishWritingAndClose() throws IOException {
1333 boolean isSuccessful = false;
1334 List<Path> result = null;
1335 try {
1336 isSuccessful = finishWriting(false);
1337 } finally {
1338 result = close();
1339 List<IOException> thrown = closeLogWriters(null);
1340 if (thrown != null && !thrown.isEmpty()) {
1341 throw MultipleIOException.createIOException(thrown);
1342 }
1343 }
1344 if (isSuccessful) {
1345 splits = result;
1346 }
1347 return splits;
1348 }
1349
1350
1351 void deleteOneWithFewerEntries(WriterAndPath wap, Path dst)
1352 throws IOException {
1353 long dstMinLogSeqNum = -1L;
1354 try (WAL.Reader reader = walFactory.createReader(walFS, dst)) {
1355 WAL.Entry entry = reader.next();
1356 if (entry != null) {
1357 dstMinLogSeqNum = entry.getKey().getLogSeqNum();
1358 }
1359 } catch (EOFException e) {
1360 if (LOG.isDebugEnabled()) {
1361 LOG.debug(
1362 "Got EOF when reading first WAL entry from " + dst + ", an empty or broken WAL file?",
1363 e);
1364 }
1365 }
1366 if (wap.minLogSeqNum < dstMinLogSeqNum) {
1367 LOG.warn("Found existing old edits file. It could be the result of a previous failed"
1368 + " split attempt or we have duplicated wal entries. Deleting " + dst + ", length="
1369 + walFS.getFileStatus(dst).getLen());
1370 if (!walFS.delete(dst, false)) {
1371 LOG.warn("Failed deleting of old " + dst);
1372 throw new IOException("Failed deleting of old " + dst);
1373 }
1374 } else {
1375 LOG.warn("Found existing old edits file and we have less entries. Deleting " + wap.p
1376 + ", length=" + walFS.getFileStatus(wap.p).getLen());
1377 if (!walFS.delete(wap.p, false)) {
1378 LOG.warn("Failed deleting of " + wap.p);
1379 throw new IOException("Failed deleting of " + wap.p);
1380 }
1381 }
1382 }
1383
1384
1385
1386
1387
1388 List<Path> close() throws IOException {
1389 Preconditions.checkState(!closeAndCleanCompleted);
1390
1391 final List<Path> paths = new ArrayList<Path>();
1392 final List<IOException> thrown = Lists.newArrayList();
1393 ThreadPoolExecutor closeThreadPool = Threads.getBoundedCachedThreadPool(numThreads, 30L,
1394 TimeUnit.SECONDS, new ThreadFactory() {
1395 private int count = 1;
1396
1397 @Override
1398 public Thread newThread(Runnable r) {
1399 Thread t = new Thread(r, "split-log-closeStream-" + count++);
1400 return t;
1401 }
1402 });
1403 CompletionService<Void> completionService =
1404 new ExecutorCompletionService<Void>(closeThreadPool);
1405 boolean progress_failed;
1406 try{
1407 progress_failed = executeCloseTask(completionService, thrown, paths);
1408 } catch (InterruptedException e) {
1409 IOException iie = new InterruptedIOException();
1410 iie.initCause(e);
1411 throw iie;
1412 } catch (ExecutionException e) {
1413 throw new IOException(e.getCause());
1414 } finally {
1415 closeThreadPool.shutdownNow();
1416 }
1417
1418 if (!thrown.isEmpty()) {
1419 throw MultipleIOException.createIOException(thrown);
1420 }
1421 writersClosed = true;
1422 closeAndCleanCompleted = true;
1423 if (progress_failed) {
1424 return null;
1425 }
1426 return paths;
1427 }
1428
1429 boolean executeCloseTask(CompletionService<Void> completionService,
1430 final List<IOException> thrown, final List<Path> paths)
1431 throws InterruptedException, ExecutionException {
1432 for (final Map.Entry<String, SinkWriter> writersEntry : writers.entrySet()) {
1433 if (LOG.isTraceEnabled()) {
1434 LOG.trace("Submitting close of " + ((WriterAndPath) writersEntry.getValue()).p);
1435 }
1436 completionService.submit(new Callable<Void>() {
1437 @Override public Void call() throws Exception {
1438 WriterAndPath wap = (WriterAndPath) writersEntry.getValue();
1439 Path dst = closeWriter(writersEntry.getKey(), wap, thrown);
1440 paths.add(dst);
1441 return null;
1442 }
1443 });
1444 }
1445 boolean progress_failed = false;
1446 for (int i = 0, n = this.writers.size(); i < n; i++) {
1447 Future<Void> future = completionService.take();
1448 future.get();
1449 if (!progress_failed && reporter != null && !reporter.progress()) {
1450 progress_failed = true;
1451 }
1452 }
1453 return progress_failed;
1454 }
1455
1456 Path closeWriter(String encodedRegionName, WriterAndPath wap, List<IOException> thrown)
1457 throws IOException {
1458 if (LOG.isTraceEnabled()) {
1459 LOG.trace("Closing " + wap.p);
1460 }
1461
1462 try {
1463 wap.w.close();
1464 } catch (IOException ioe) {
1465 LOG.error("Couldn't close log at " + wap.p, ioe);
1466 thrown.add(ioe);
1467 return null;
1468 }
1469 if (LOG.isDebugEnabled()) {
1470 LOG.debug("Closed wap " + wap.p + " (wrote " + wap.editsWritten
1471 + " edits, skipped " + wap.editsSkipped + " edits in "
1472 + (wap.nanosSpent / 1000 / 1000) + "ms");
1473 }
1474 if (wap.editsWritten == 0) {
1475
1476 if (walFS.exists(wap.p) && !walFS.delete(wap.p, false)) {
1477 LOG.warn("Failed deleting empty " + wap.p);
1478 throw new IOException("Failed deleting empty " + wap.p);
1479 }
1480 return null;
1481 }
1482
1483 Path dst = getCompletedRecoveredEditsFilePath(wap.p,
1484 regionMaximumEditLogSeqNum.get(encodedRegionName));
1485 try {
1486 if (!dst.equals(wap.p) && walFS.exists(dst)) {
1487 deleteOneWithFewerEntries(wap, dst);
1488 }
1489
1490
1491
1492 if (walFS.exists(wap.p)) {
1493 if (!walFS.rename(wap.p, dst)) {
1494 throw new IOException("Failed renaming " + wap.p + " to " + dst);
1495 }
1496 LOG.info("Rename " + wap.p + " to " + dst);
1497 }
1498 } catch (IOException ioe) {
1499 LOG.error("Couldn't rename " + wap.p + " to " + dst, ioe);
1500 thrown.add(ioe);
1501 return null;
1502 }
1503 return dst;
1504 }
1505
1506
1507 private List<IOException> closeLogWriters(List<IOException> thrown) throws IOException {
1508 if (writersClosed) {
1509 return thrown;
1510 }
1511
1512 if (thrown == null) {
1513 thrown = Lists.newArrayList();
1514 }
1515 try {
1516 for (WriterThread t : writerThreads) {
1517 while (t.isAlive()) {
1518 t.shouldStop = true;
1519 t.interrupt();
1520 try {
1521 t.join(10);
1522 } catch (InterruptedException e) {
1523 IOException iie = new InterruptedIOException();
1524 iie.initCause(e);
1525 throw iie;
1526 }
1527 }
1528 }
1529 } finally {
1530 WriterAndPath wap = null;
1531 for (SinkWriter tmpWAP : writers.values()) {
1532 try {
1533 wap = (WriterAndPath) tmpWAP;
1534 wap.w.close();
1535 } catch (IOException ioe) {
1536 LOG.error("Couldn't close log at " + wap.p, ioe);
1537 thrown.add(ioe);
1538 continue;
1539 }
1540 LOG.info(
1541 "Closed log " + wap.p + " (wrote " + wap.editsWritten + " edits in " + (wap.nanosSpent
1542 / 1000 / 1000) + "ms)");
1543 }
1544 writersClosed = true;
1545 }
1546
1547 return thrown;
1548 }
1549
1550
1551
1552
1553
1554
1555 WriterAndPath getWriterAndPath(Entry entry, boolean reusable) throws IOException {
1556 byte region[] = entry.getKey().getEncodedRegionName();
1557 String regionName = Bytes.toString(region);
1558 WriterAndPath ret = (WriterAndPath) writers.get(regionName);
1559 if (ret != null) {
1560 return ret;
1561 }
1562
1563
1564 if (blacklistedRegions.contains(region)) {
1565 return null;
1566 }
1567 ret = createWAP(region, entry);
1568 if (ret == null) {
1569 blacklistedRegions.add(region);
1570 return null;
1571 }
1572
1573 if(reusable) {
1574 writers.put(regionName, ret);
1575 }
1576 return ret;
1577 }
1578
1579
1580
1581
1582 WriterAndPath createWAP(byte[] region, Entry entry) throws IOException {
1583 String tmpDirName = conf.get(HConstants.TEMPORARY_FS_DIRECTORY_KEY,
1584 HConstants.DEFAULT_TEMPORARY_HDFS_DIRECTORY);
1585 Path regionedits = getRegionSplitEditsPath(entry,
1586 fileBeingSplit.getPath().getName(), tmpDirName, conf);
1587 if (regionedits == null) {
1588 return null;
1589 }
1590 if (walFS.exists(regionedits)) {
1591 LOG.warn("Found old edits file. It could be the "
1592 + "result of a previous failed split attempt. Deleting " + regionedits + ", length="
1593 + walFS.getFileStatus(regionedits).getLen());
1594 if (!walFS.delete(regionedits, false)) {
1595 LOG.warn("Failed delete of old " + regionedits);
1596 }
1597 }
1598 Writer w = createWriter(regionedits);
1599 LOG.debug("Creating writer path=" + regionedits);
1600 return new WriterAndPath(regionedits, w, entry.getKey().getLogSeqNum());
1601 }
1602
1603 void filterCellByStore(Entry logEntry) {
1604 Map<byte[], Long> maxSeqIdInStores =
1605 regionMaxSeqIdInStores.get(Bytes.toString(logEntry.getKey().getEncodedRegionName()));
1606 if (maxSeqIdInStores == null || maxSeqIdInStores.isEmpty()) {
1607 return;
1608 }
1609
1610
1611 ArrayList<Cell> keptCells = new ArrayList<Cell>(logEntry.getEdit().getCells().size());
1612 for (Cell cell : logEntry.getEdit().getCells()) {
1613 if (CellUtil.matchingFamily(cell, WALEdit.METAFAMILY)) {
1614 keptCells.add(cell);
1615 } else {
1616 byte[] family = CellUtil.cloneFamily(cell);
1617 Long maxSeqId = maxSeqIdInStores.get(family);
1618
1619
1620 if (maxSeqId == null || maxSeqId.longValue() < logEntry.getKey().getLogSeqNum()) {
1621 keptCells.add(cell);
1622 }
1623 }
1624 }
1625
1626
1627
1628
1629 logEntry.getEdit().setCells(keptCells);
1630 }
1631
1632 @Override
1633 public void append(RegionEntryBuffer buffer) throws IOException {
1634 appendBuffer(buffer, true);
1635 }
1636
1637
1638 WriterAndPath appendBuffer(RegionEntryBuffer buffer, boolean reusable) throws IOException{
1639 List<Entry> entries = buffer.entryBuffer;
1640 if (entries.isEmpty()) {
1641 LOG.warn("got an empty buffer, skipping");
1642 return null;
1643 }
1644 WriterAndPath wap = null;
1645
1646 long startTime = System.nanoTime();
1647 try {
1648 int editsCount = 0;
1649
1650 for (Entry logEntry : entries) {
1651 if (wap == null) {
1652 wap = getWriterAndPath(logEntry, reusable);
1653 if (wap == null) {
1654 if (LOG.isDebugEnabled()) {
1655 LOG.debug("getWriterAndPath decided we don't need to write edits for " + logEntry);
1656 }
1657 return null;
1658 }
1659 }
1660 filterCellByStore(logEntry);
1661 if (!logEntry.getEdit().isEmpty()) {
1662 wap.w.append(logEntry);
1663 this.updateRegionMaximumEditLogSeqNum(logEntry);
1664 editsCount++;
1665 } else {
1666 wap.incrementSkippedEdits(1);
1667 }
1668 }
1669
1670 wap.incrementEdits(editsCount);
1671 wap.incrementNanoTime(System.nanoTime() - startTime);
1672 } catch (IOException e) {
1673 e = RemoteExceptionHandler.checkIOException(e);
1674 LOG.fatal(" Got while writing log entry to log", e);
1675 throw e;
1676 }
1677 return wap;
1678 }
1679
1680 @Override
1681 public boolean keepRegionEvent(Entry entry) {
1682 ArrayList<Cell> cells = entry.getEdit().getCells();
1683 for (int i = 0; i < cells.size(); i++) {
1684 if (WALEdit.isCompactionMarker(cells.get(i))) {
1685 return true;
1686 }
1687 }
1688 return false;
1689 }
1690
1691
1692
1693
1694 @Override
1695 public Map<byte[], Long> getOutputCounts() {
1696 TreeMap<byte[], Long> ret = new TreeMap<byte[], Long>(Bytes.BYTES_COMPARATOR);
1697 synchronized (writers) {
1698 for (Map.Entry<String, SinkWriter> entry : writers.entrySet()) {
1699 ret.put(Bytes.toBytes(entry.getKey()), entry.getValue().editsWritten);
1700 }
1701 }
1702 return ret;
1703 }
1704
1705 @Override
1706 public int getNumberOfRecoveredRegions() {
1707 return writers.size();
1708 }
1709 }
1710
1711
1712
1713
1714 class BoundedLogWriterCreationOutputSink extends LogRecoveredEditsOutputSink{
1715
1716 ConcurrentHashMap<String, Long> regionRecoverStatMap = new ConcurrentHashMap<>();
1717
1718
1719 public BoundedLogWriterCreationOutputSink(PipelineController controller,
1720 EntryBuffers entryBuffers, int numWriters){
1721 super(controller, entryBuffers, numWriters);
1722 }
1723
1724 @Override
1725 public List<Path> finishWritingAndClose() throws IOException {
1726 boolean isSuccessful;
1727 List<Path> result;
1728 try {
1729 isSuccessful = finishWriting(false);
1730 } finally {
1731 result = close();
1732 }
1733 if (isSuccessful) {
1734 splits = result;
1735 }
1736 return splits;
1737 }
1738
1739 @Override
1740 boolean executeCloseTask(CompletionService<Void> closeCompletionService,
1741 final List<IOException> thrown, final List<Path> paths)
1742 throws InterruptedException, ExecutionException {
1743 for (final Map.Entry<byte[], RegionEntryBuffer> buffer : entryBuffers.buffers.entrySet()) {
1744 LOG.info("Submitting write then close of " +
1745 Bytes.toString(buffer.getValue().encodedRegionName));
1746 closeCompletionService.submit(new Callable<Void>() {
1747 public Void call() throws Exception {
1748 Path dst = writeThenClose(buffer.getValue());
1749 paths.add(dst);
1750 return null;
1751 }
1752 });
1753 }
1754
1755 boolean progress_failed = false;
1756 for (int i = 0, n = entryBuffers.buffers.size(); i < n; i++) {
1757 Future<Void> future = closeCompletionService.take();
1758 future.get();
1759 if (!progress_failed && reporter != null && !reporter.progress()) {
1760 progress_failed = true;
1761 }
1762 }
1763 return progress_failed;
1764 }
1765
1766 @Override
1767 public Map<byte[], Long> getOutputCounts() {
1768 Map<byte[], Long> regionRecoverStatMapResult = new HashMap<>();
1769 for(Map.Entry<String, Long> entry: regionRecoverStatMap.entrySet()){
1770 regionRecoverStatMapResult.put(Bytes.toBytes(entry.getKey()), entry.getValue());
1771 }
1772 return regionRecoverStatMapResult;
1773 }
1774
1775 @Override
1776 public int getNumberOfRecoveredRegions() {
1777 return regionRecoverStatMap.size();
1778 }
1779
1780 @Override
1781 public void append(RegionEntryBuffer buffer) throws IOException {
1782 writeThenClose(buffer);
1783 }
1784
1785 private Path writeThenClose(RegionEntryBuffer buffer) throws IOException {
1786 WriterAndPath wap = appendBuffer(buffer, false);
1787 Path dst = null;
1788 if(wap != null){
1789 String encodedRegionName = Bytes.toString(buffer.encodedRegionName);
1790 Long value = regionRecoverStatMap.putIfAbsent(encodedRegionName, wap.editsWritten);
1791 if(value != null){
1792 Long newValue = regionRecoverStatMap.get(encodedRegionName) + wap.editsWritten;
1793 regionRecoverStatMap.put(encodedRegionName, newValue);
1794 }
1795 }
1796
1797 List<IOException> thrown = new ArrayList<>();
1798 if(wap != null) {
1799 dst = closeWriter(Bytes.toString(buffer.encodedRegionName), wap, thrown);
1800 }
1801
1802 if(!thrown.isEmpty()){
1803 throw MultipleIOException.createIOException(thrown);
1804 }
1805 return dst;
1806 }
1807 }
1808
1809
1810
1811
1812
1813 public abstract static class SinkWriter {
1814
1815 long editsWritten = 0;
1816
1817 long editsSkipped = 0;
1818
1819 long nanosSpent = 0;
1820
1821 void incrementEdits(int edits) {
1822 editsWritten += edits;
1823 }
1824
1825 void incrementSkippedEdits(int skipped) {
1826 editsSkipped += skipped;
1827 }
1828
1829 void incrementNanoTime(long nanos) {
1830 nanosSpent += nanos;
1831 }
1832 }
1833
1834
1835
1836
1837
1838 private final static class WriterAndPath extends SinkWriter {
1839 final Path p;
1840 final Writer w;
1841 final long minLogSeqNum;
1842
1843 WriterAndPath(final Path p, final Writer w, final long minLogSeqNum) {
1844 this.p = p;
1845 this.w = w;
1846 this.minLogSeqNum = minLogSeqNum;
1847 }
1848 }
1849
1850
1851
1852
1853 class LogReplayOutputSink extends OutputSink {
1854 private static final double BUFFER_THRESHOLD = 0.35;
1855 private static final String KEY_DELIMITER = "#";
1856
1857 private long waitRegionOnlineTimeOut;
1858 private final Set<String> recoveredRegions = Collections.synchronizedSet(new HashSet<String>());
1859 private final Map<String, RegionServerWriter> writers =
1860 new ConcurrentHashMap<String, RegionServerWriter>();
1861
1862 private final Map<String, HRegionLocation> onlineRegions =
1863 new ConcurrentHashMap<String, HRegionLocation>();
1864
1865 private Map<TableName, HConnection> tableNameToHConnectionMap = Collections
1866 .synchronizedMap(new TreeMap<TableName, HConnection>());
1867
1868
1869
1870
1871 private Map<String, List<Pair<HRegionLocation, Entry>>> serverToBufferQueueMap =
1872 new ConcurrentHashMap<String, List<Pair<HRegionLocation, Entry>>>();
1873 private List<Throwable> thrown = new ArrayList<Throwable>();
1874
1875
1876
1877
1878
1879 private LogRecoveredEditsOutputSink logRecoveredEditsOutputSink;
1880 private boolean hasEditsInDisablingOrDisabledTables = false;
1881
1882 public LogReplayOutputSink(PipelineController controller, EntryBuffers entryBuffers,
1883 int numWriters) {
1884 super(controller, entryBuffers, numWriters);
1885 this.waitRegionOnlineTimeOut = conf.getInt(HConstants.HBASE_SPLITLOG_MANAGER_TIMEOUT,
1886 ZKSplitLogManagerCoordination.DEFAULT_TIMEOUT);
1887 this.logRecoveredEditsOutputSink = new LogRecoveredEditsOutputSink(controller,
1888 entryBuffers, numWriters);
1889 this.logRecoveredEditsOutputSink.setReporter(reporter);
1890 }
1891
1892 @Override
1893 public void append(RegionEntryBuffer buffer) throws IOException {
1894 List<Entry> entries = buffer.entryBuffer;
1895 if (entries.isEmpty()) {
1896 LOG.warn("got an empty buffer, skipping");
1897 return;
1898 }
1899
1900
1901 if (disablingOrDisabledTables.contains(buffer.tableName)) {
1902
1903 logRecoveredEditsOutputSink.append(buffer);
1904 hasEditsInDisablingOrDisabledTables = true;
1905
1906 addToRecoveredRegions(Bytes.toString(buffer.encodedRegionName));
1907 return;
1908 }
1909
1910
1911 groupEditsByServer(entries);
1912
1913
1914 String maxLocKey = null;
1915 int maxSize = 0;
1916 List<Pair<HRegionLocation, Entry>> maxQueue = null;
1917 synchronized (this.serverToBufferQueueMap) {
1918 for (Map.Entry<String, List<Pair<HRegionLocation, Entry>>> entry:
1919 serverToBufferQueueMap.entrySet()) {
1920 List<Pair<HRegionLocation, Entry>> curQueue = entry.getValue();
1921 if (curQueue.size() > maxSize) {
1922 maxSize = curQueue.size();
1923 maxQueue = curQueue;
1924 maxLocKey = entry.getKey();
1925 }
1926 }
1927 if (maxSize < minBatchSize
1928 && entryBuffers.totalBuffered < BUFFER_THRESHOLD * entryBuffers.maxHeapUsage) {
1929
1930 return;
1931 } else if (maxSize > 0) {
1932 this.serverToBufferQueueMap.remove(maxLocKey);
1933 }
1934 }
1935
1936 if (maxSize > 0) {
1937 processWorkItems(maxLocKey, maxQueue);
1938 }
1939 }
1940
1941 private void addToRecoveredRegions(String encodedRegionName) {
1942 if (!recoveredRegions.contains(encodedRegionName)) {
1943 recoveredRegions.add(encodedRegionName);
1944 }
1945 }
1946
1947
1948
1949
1950
1951 private void groupEditsByServer(List<Entry> entries) throws IOException {
1952 Set<TableName> nonExistentTables = null;
1953 Long cachedLastFlushedSequenceId = -1l;
1954 for (Entry entry : entries) {
1955 WALEdit edit = entry.getEdit();
1956 TableName table = entry.getKey().getTablename();
1957
1958 entry.getKey().setScopes(null);
1959 String encodeRegionNameStr = Bytes.toString(entry.getKey().getEncodedRegionName());
1960
1961 if (nonExistentTables != null && nonExistentTables.contains(table)) {
1962 this.skippedEdits.incrementAndGet();
1963 continue;
1964 }
1965
1966 Map<byte[], Long> maxStoreSequenceIds = null;
1967 boolean needSkip = false;
1968 HRegionLocation loc = null;
1969 String locKey = null;
1970 List<Cell> cells = edit.getCells();
1971 List<Cell> skippedCells = new ArrayList<Cell>();
1972 HConnection hconn = this.getConnectionByTableName(table);
1973
1974 for (Cell cell : cells) {
1975 byte[] row = CellUtil.cloneRow(cell);
1976 byte[] family = CellUtil.cloneFamily(cell);
1977 boolean isCompactionEntry = false;
1978 if (CellUtil.matchingFamily(cell, WALEdit.METAFAMILY)) {
1979 CompactionDescriptor compaction = WALEdit.getCompaction(cell);
1980 if (compaction != null && compaction.hasRegionName()) {
1981 try {
1982 byte[][] regionName = HRegionInfo.parseRegionName(compaction.getRegionName()
1983 .toByteArray());
1984 row = regionName[1];
1985 family = compaction.getFamilyName().toByteArray();
1986 isCompactionEntry = true;
1987 } catch (Exception ex) {
1988 LOG.warn("Unexpected exception received, ignoring " + ex);
1989 skippedCells.add(cell);
1990 continue;
1991 }
1992 } else {
1993 skippedCells.add(cell);
1994 continue;
1995 }
1996 }
1997
1998 try {
1999 loc =
2000 locateRegionAndRefreshLastFlushedSequenceId(hconn, table, row,
2001 encodeRegionNameStr);
2002
2003 if (isCompactionEntry && !encodeRegionNameStr.equalsIgnoreCase(
2004 loc.getRegionInfo().getEncodedName())) {
2005 LOG.info("Not replaying a compaction marker for an older region: "
2006 + encodeRegionNameStr);
2007 needSkip = true;
2008 }
2009 } catch (TableNotFoundException ex) {
2010
2011 LOG.info("Table " + table + " doesn't exist. Skip log replay for region "
2012 + encodeRegionNameStr);
2013 lastFlushedSequenceIds.put(encodeRegionNameStr, Long.MAX_VALUE);
2014 if (nonExistentTables == null) {
2015 nonExistentTables = new TreeSet<TableName>();
2016 }
2017 nonExistentTables.add(table);
2018 this.skippedEdits.incrementAndGet();
2019 needSkip = true;
2020 break;
2021 }
2022
2023 cachedLastFlushedSequenceId =
2024 lastFlushedSequenceIds.get(loc.getRegionInfo().getEncodedName());
2025 if (cachedLastFlushedSequenceId != null
2026 && cachedLastFlushedSequenceId >= entry.getKey().getLogSeqNum()) {
2027
2028 this.skippedEdits.incrementAndGet();
2029 needSkip = true;
2030 break;
2031 } else {
2032 if (maxStoreSequenceIds == null) {
2033 maxStoreSequenceIds =
2034 regionMaxSeqIdInStores.get(loc.getRegionInfo().getEncodedName());
2035 }
2036 if (maxStoreSequenceIds != null) {
2037 Long maxStoreSeqId = maxStoreSequenceIds.get(family);
2038 if (maxStoreSeqId == null || maxStoreSeqId >= entry.getKey().getLogSeqNum()) {
2039
2040 skippedCells.add(cell);
2041 continue;
2042 }
2043 }
2044 }
2045 }
2046
2047
2048 if (loc == null || needSkip) continue;
2049
2050 if (!skippedCells.isEmpty()) {
2051 cells.removeAll(skippedCells);
2052 }
2053
2054 synchronized (serverToBufferQueueMap) {
2055 locKey = loc.getHostnamePort() + KEY_DELIMITER + table;
2056 List<Pair<HRegionLocation, Entry>> queue = serverToBufferQueueMap.get(locKey);
2057 if (queue == null) {
2058 queue =
2059 Collections.synchronizedList(new ArrayList<Pair<HRegionLocation, Entry>>());
2060 serverToBufferQueueMap.put(locKey, queue);
2061 }
2062 queue.add(new Pair<HRegionLocation, Entry>(loc, entry));
2063 }
2064
2065 addToRecoveredRegions(loc.getRegionInfo().getEncodedName());
2066 }
2067 }
2068
2069
2070
2071
2072
2073
2074 private HRegionLocation locateRegionAndRefreshLastFlushedSequenceId(HConnection hconn,
2075 TableName table, byte[] row, String originalEncodedRegionName) throws IOException {
2076
2077 HRegionLocation loc = onlineRegions.get(originalEncodedRegionName);
2078 if(loc != null) return loc;
2079
2080 loc = hconn.getRegionLocation(table, row, true);
2081 if (loc == null) {
2082 throw new IOException("Can't locate location for row:" + Bytes.toString(row)
2083 + " of table:" + table);
2084 }
2085
2086 if (!originalEncodedRegionName.equalsIgnoreCase(loc.getRegionInfo().getEncodedName())) {
2087
2088 lastFlushedSequenceIds.put(originalEncodedRegionName, Long.MAX_VALUE);
2089 HRegionLocation tmpLoc = onlineRegions.get(loc.getRegionInfo().getEncodedName());
2090 if (tmpLoc != null) return tmpLoc;
2091 }
2092
2093 Long lastFlushedSequenceId = -1l;
2094 AtomicBoolean isRecovering = new AtomicBoolean(true);
2095 loc = waitUntilRegionOnline(loc, row, this.waitRegionOnlineTimeOut, isRecovering);
2096 if (!isRecovering.get()) {
2097
2098
2099 lastFlushedSequenceIds.put(loc.getRegionInfo().getEncodedName(), Long.MAX_VALUE);
2100 LOG.info("logReplay skip region: " + loc.getRegionInfo().getEncodedName()
2101 + " because it's not in recovering.");
2102 } else {
2103 Long cachedLastFlushedSequenceId =
2104 lastFlushedSequenceIds.get(loc.getRegionInfo().getEncodedName());
2105
2106
2107
2108 RegionStoreSequenceIds ids =
2109 csm.getSplitLogWorkerCoordination().getRegionFlushedSequenceId(failedServerName,
2110 loc.getRegionInfo().getEncodedName());
2111 if (ids != null) {
2112 lastFlushedSequenceId = ids.getLastFlushedSequenceId();
2113 Map<byte[], Long> storeIds = new TreeMap<byte[], Long>(Bytes.BYTES_COMPARATOR);
2114 List<StoreSequenceId> maxSeqIdInStores = ids.getStoreSequenceIdList();
2115 for (StoreSequenceId id : maxSeqIdInStores) {
2116 storeIds.put(id.getFamilyName().toByteArray(), id.getSequenceId());
2117 }
2118 regionMaxSeqIdInStores.put(loc.getRegionInfo().getEncodedName(), storeIds);
2119 }
2120
2121 if (cachedLastFlushedSequenceId == null
2122 || lastFlushedSequenceId > cachedLastFlushedSequenceId) {
2123 lastFlushedSequenceIds.put(loc.getRegionInfo().getEncodedName(), lastFlushedSequenceId);
2124 }
2125 }
2126
2127 onlineRegions.put(loc.getRegionInfo().getEncodedName(), loc);
2128 return loc;
2129 }
2130
2131 private void processWorkItems(String key, List<Pair<HRegionLocation, Entry>> actions)
2132 throws IOException {
2133 RegionServerWriter rsw = null;
2134
2135 long startTime = System.nanoTime();
2136 try {
2137 rsw = getRegionServerWriter(key);
2138 rsw.sink.replayEntries(actions);
2139
2140
2141 rsw.incrementEdits(actions.size());
2142 rsw.incrementNanoTime(System.nanoTime() - startTime);
2143 } catch (IOException e) {
2144 e = RemoteExceptionHandler.checkIOException(e);
2145 LOG.fatal(" Got while writing log entry to log", e);
2146 throw e;
2147 }
2148 }
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159 private HRegionLocation waitUntilRegionOnline(HRegionLocation loc, byte[] row,
2160 final long timeout, AtomicBoolean isRecovering)
2161 throws IOException {
2162 final long endTime = EnvironmentEdgeManager.currentTime() + timeout;
2163 final long pause = conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
2164 HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
2165 boolean reloadLocation = false;
2166 TableName tableName = loc.getRegionInfo().getTable();
2167 int tries = 0;
2168 Throwable cause = null;
2169 while (endTime > EnvironmentEdgeManager.currentTime()) {
2170 try {
2171
2172 HConnection hconn = getConnectionByTableName(tableName);
2173 if(reloadLocation) {
2174 loc = hconn.getRegionLocation(tableName, row, true);
2175 }
2176 BlockingInterface remoteSvr = hconn.getAdmin(loc.getServerName());
2177 HRegionInfo region = loc.getRegionInfo();
2178 try {
2179 GetRegionInfoRequest request =
2180 RequestConverter.buildGetRegionInfoRequest(region.getRegionName());
2181 GetRegionInfoResponse response = remoteSvr.getRegionInfo(null, request);
2182 if (HRegionInfo.convert(response.getRegionInfo()) != null) {
2183 isRecovering.set((response.hasIsRecovering()) ? response.getIsRecovering() : true);
2184 return loc;
2185 }
2186 } catch (ServiceException se) {
2187 throw ProtobufUtil.getRemoteException(se);
2188 }
2189 } catch (IOException e) {
2190 cause = e.getCause();
2191 if(!(cause instanceof RegionOpeningException)) {
2192 reloadLocation = true;
2193 }
2194 }
2195 long expectedSleep = ConnectionUtils.getPauseTime(pause, tries);
2196 try {
2197 Thread.sleep(expectedSleep);
2198 } catch (InterruptedException e) {
2199 throw new IOException("Interrupted when waiting region " +
2200 loc.getRegionInfo().getEncodedName() + " online.", e);
2201 }
2202 tries++;
2203 }
2204
2205 throw new IOException("Timeout when waiting region " + loc.getRegionInfo().getEncodedName() +
2206 " online for " + timeout + " milliseconds.", cause);
2207 }
2208
2209 @Override
2210 public boolean flush() throws IOException {
2211 String curLoc = null;
2212 int curSize = 0;
2213 List<Pair<HRegionLocation, Entry>> curQueue = null;
2214 synchronized (this.serverToBufferQueueMap) {
2215 for (Map.Entry<String, List<Pair<HRegionLocation, Entry>>> entry :
2216 serverToBufferQueueMap.entrySet()) {
2217 String locationKey = entry.getKey();
2218 curQueue = entry.getValue();
2219 if (!curQueue.isEmpty()) {
2220 curSize = curQueue.size();
2221 curLoc = locationKey;
2222 break;
2223 }
2224 }
2225 if (curSize > 0) {
2226 this.serverToBufferQueueMap.remove(curLoc);
2227 }
2228 }
2229
2230 if (curSize > 0) {
2231 this.processWorkItems(curLoc, curQueue);
2232
2233 synchronized(controller.dataAvailable) {
2234 controller.dataAvailable.notifyAll();
2235 }
2236 return true;
2237 }
2238 return false;
2239 }
2240
2241 @Override
2242 public boolean keepRegionEvent(Entry entry) {
2243 return true;
2244 }
2245
2246 void addWriterError(Throwable t) {
2247 thrown.add(t);
2248 }
2249
2250 @Override
2251 public List<Path> finishWritingAndClose() throws IOException {
2252 try {
2253 if (!finishWriting(false)) {
2254 return null;
2255 }
2256 if (hasEditsInDisablingOrDisabledTables) {
2257 splits = logRecoveredEditsOutputSink.finishWritingAndClose();
2258 } else {
2259 splits = new ArrayList<Path>();
2260 }
2261
2262 return splits;
2263 } finally {
2264 List<IOException> thrown = closeRegionServerWriters();
2265 if (thrown != null && !thrown.isEmpty()) {
2266 throw MultipleIOException.createIOException(thrown);
2267 }
2268 }
2269 }
2270
2271 @Override
2272 int getNumOpenWriters() {
2273 return this.writers.size() + this.logRecoveredEditsOutputSink.getNumOpenWriters();
2274 }
2275
2276 private List<IOException> closeRegionServerWriters() throws IOException {
2277 List<IOException> result = null;
2278 if (!writersClosed) {
2279 result = Lists.newArrayList();
2280 try {
2281 for (WriterThread t : writerThreads) {
2282 while (t.isAlive()) {
2283 t.shouldStop = true;
2284 t.interrupt();
2285 try {
2286 t.join(10);
2287 } catch (InterruptedException e) {
2288 IOException iie = new InterruptedIOException();
2289 iie.initCause(e);
2290 throw iie;
2291 }
2292 }
2293 }
2294 } finally {
2295 synchronized (writers) {
2296 for (Map.Entry<String, RegionServerWriter> entry : writers.entrySet()) {
2297 String locationKey = entry.getKey();
2298 RegionServerWriter tmpW = entry.getValue();
2299 try {
2300 tmpW.close();
2301 } catch (IOException ioe) {
2302 LOG.error("Couldn't close writer for region server:" + locationKey, ioe);
2303 result.add(ioe);
2304 }
2305 }
2306 }
2307
2308
2309 synchronized (this.tableNameToHConnectionMap) {
2310 for (Map.Entry<TableName, HConnection> entry :
2311 tableNameToHConnectionMap.entrySet()) {
2312 TableName tableName = entry.getKey();
2313 HConnection hconn = entry.getValue();
2314 try {
2315 hconn.clearRegionCache();
2316 hconn.close();
2317 } catch (IOException ioe) {
2318 result.add(ioe);
2319 }
2320 }
2321 }
2322 writersClosed = true;
2323 }
2324 }
2325 return result;
2326 }
2327
2328 @Override
2329 public Map<byte[], Long> getOutputCounts() {
2330 TreeMap<byte[], Long> ret = new TreeMap<byte[], Long>(Bytes.BYTES_COMPARATOR);
2331 synchronized (writers) {
2332 for (Map.Entry<String, RegionServerWriter> entry : writers.entrySet()) {
2333 ret.put(Bytes.toBytes(entry.getKey()), entry.getValue().editsWritten);
2334 }
2335 }
2336 return ret;
2337 }
2338
2339 @Override
2340 public int getNumberOfRecoveredRegions() {
2341 return this.recoveredRegions.size();
2342 }
2343
2344
2345
2346
2347
2348
2349 private RegionServerWriter getRegionServerWriter(String loc) throws IOException {
2350 RegionServerWriter ret = writers.get(loc);
2351 if (ret != null) {
2352 return ret;
2353 }
2354
2355 TableName tableName = getTableFromLocationStr(loc);
2356 if(tableName == null){
2357 throw new IOException("Invalid location string:" + loc + " found. Replay aborted.");
2358 }
2359
2360 HConnection hconn = getConnectionByTableName(tableName);
2361 synchronized (writers) {
2362 ret = writers.get(loc);
2363 if (ret == null) {
2364 ret = new RegionServerWriter(conf, tableName, hconn);
2365 writers.put(loc, ret);
2366 }
2367 }
2368 return ret;
2369 }
2370
2371 private HConnection getConnectionByTableName(final TableName tableName) throws IOException {
2372 HConnection hconn = this.tableNameToHConnectionMap.get(tableName);
2373 if (hconn == null) {
2374 synchronized (this.tableNameToHConnectionMap) {
2375 hconn = this.tableNameToHConnectionMap.get(tableName);
2376 if (hconn == null) {
2377 hconn = HConnectionManager.getConnection(conf);
2378 this.tableNameToHConnectionMap.put(tableName, hconn);
2379 }
2380 }
2381 }
2382 return hconn;
2383 }
2384 private TableName getTableFromLocationStr(String loc) {
2385
2386
2387
2388 String[] splits = loc.split(KEY_DELIMITER);
2389 if (splits.length != 2) {
2390 return null;
2391 }
2392 return TableName.valueOf(splits[1]);
2393 }
2394 }
2395
2396
2397
2398
2399
2400 private final static class RegionServerWriter extends SinkWriter {
2401 final WALEditsReplaySink sink;
2402
2403 RegionServerWriter(final Configuration conf, final TableName tableName, final HConnection conn)
2404 throws IOException {
2405 this.sink = new WALEditsReplaySink(conf, tableName, conn);
2406 }
2407
2408 void close() throws IOException {
2409 }
2410 }
2411
2412 static class CorruptedLogFileException extends Exception {
2413 private static final long serialVersionUID = 1L;
2414
2415 CorruptedLogFileException(String s) {
2416 super(s);
2417 }
2418 }
2419
2420
2421 public static class MutationReplay implements Comparable<MutationReplay> {
2422 public MutationReplay(MutationType type, Mutation mutation, long nonceGroup, long nonce) {
2423 this.type = type;
2424 this.mutation = mutation;
2425 if(this.mutation.getDurability() != Durability.SKIP_WAL) {
2426
2427 this.mutation.setDurability(Durability.ASYNC_WAL);
2428 }
2429 this.nonceGroup = nonceGroup;
2430 this.nonce = nonce;
2431 }
2432
2433 public final MutationType type;
2434 public final Mutation mutation;
2435 public final long nonceGroup;
2436 public final long nonce;
2437
2438 @Override
2439 public int compareTo(final MutationReplay d) {
2440 return this.mutation.compareTo(d.mutation);
2441 }
2442
2443 @Override
2444 public boolean equals(Object obj) {
2445 if(!(obj instanceof MutationReplay)) {
2446 return false;
2447 } else {
2448 return this.compareTo((MutationReplay)obj) == 0;
2449 }
2450 }
2451
2452 @Override
2453 public int hashCode() {
2454 return this.mutation.hashCode();
2455 }
2456 }
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469 public static List<MutationReplay> getMutationsFromWALEntry(WALEntry entry, CellScanner cells,
2470 Pair<WALKey, WALEdit> logEntry, Durability durability)
2471 throws IOException {
2472 if (entry == null) {
2473
2474 return new ArrayList<MutationReplay>();
2475 }
2476
2477 long replaySeqId = (entry.getKey().hasOrigSequenceNumber()) ?
2478 entry.getKey().getOrigSequenceNumber() : entry.getKey().getLogSequenceNumber();
2479 int count = entry.getAssociatedCellCount();
2480 List<MutationReplay> mutations = new ArrayList<MutationReplay>();
2481 Cell previousCell = null;
2482 Mutation m = null;
2483 WALKey key = null;
2484 WALEdit val = null;
2485 if (logEntry != null) val = new WALEdit();
2486
2487 for (int i = 0; i < count; i++) {
2488
2489 if (!cells.advance()) {
2490 throw new ArrayIndexOutOfBoundsException("Expected=" + count + ", index=" + i);
2491 }
2492 Cell cell = cells.current();
2493 if (val != null) val.add(cell);
2494
2495 boolean isNewRowOrType =
2496 previousCell == null || previousCell.getTypeByte() != cell.getTypeByte()
2497 || !CellUtil.matchingRow(previousCell, cell);
2498 if (isNewRowOrType) {
2499
2500 if (CellUtil.isDelete(cell)) {
2501 m = new Delete(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
2502
2503 mutations.add(new MutationReplay(
2504 MutationType.DELETE, m, HConstants.NO_NONCE, HConstants.NO_NONCE));
2505 } else {
2506 m = new Put(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
2507
2508 long nonceGroup = entry.getKey().hasNonceGroup()
2509 ? entry.getKey().getNonceGroup() : HConstants.NO_NONCE;
2510 long nonce = entry.getKey().hasNonce() ? entry.getKey().getNonce() : HConstants.NO_NONCE;
2511 mutations.add(new MutationReplay(MutationType.PUT, m, nonceGroup, nonce));
2512 }
2513 }
2514 if (CellUtil.isDelete(cell)) {
2515 ((Delete) m).addDeleteMarker(cell);
2516 } else {
2517 ((Put) m).add(cell);
2518 }
2519 m.setDurability(durability);
2520 previousCell = cell;
2521 }
2522
2523
2524 if (logEntry != null) {
2525 org.apache.hadoop.hbase.protobuf.generated.WALProtos.WALKey walKeyProto = entry.getKey();
2526 List<UUID> clusterIds = new ArrayList<UUID>(walKeyProto.getClusterIdsCount());
2527 for (HBaseProtos.UUID uuid : entry.getKey().getClusterIdsList()) {
2528 clusterIds.add(new UUID(uuid.getMostSigBits(), uuid.getLeastSigBits()));
2529 }
2530
2531 key = new HLogKey(walKeyProto.getEncodedRegionName().toByteArray(), TableName.valueOf(
2532 walKeyProto.getTableName().toByteArray()), replaySeqId, walKeyProto.getWriteTime(),
2533 clusterIds, walKeyProto.getNonceGroup(), walKeyProto.getNonce(), null);
2534 logEntry.setFirst(key);
2535 logEntry.setSecond(val);
2536 }
2537
2538 return mutations;
2539 }
2540 }