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