1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.mapreduce;
20
21 import static java.lang.String.format;
22
23 import com.google.common.collect.HashMultimap;
24 import com.google.common.collect.Multimap;
25 import com.google.common.collect.Multimaps;
26 import com.google.common.util.concurrent.ThreadFactoryBuilder;
27
28 import org.apache.commons.lang.mutable.MutableInt;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.conf.Configured;
33 import org.apache.hadoop.fs.FileStatus;
34 import org.apache.hadoop.fs.FileSystem;
35 import org.apache.hadoop.fs.Path;
36 import org.apache.hadoop.fs.permission.FsPermission;
37 import org.apache.hadoop.hbase.HBaseConfiguration;
38 import org.apache.hadoop.hbase.HColumnDescriptor;
39 import org.apache.hadoop.hbase.HConstants;
40 import org.apache.hadoop.hbase.HTableDescriptor;
41 import org.apache.hadoop.hbase.KeyValue;
42 import org.apache.hadoop.hbase.KeyValueUtil;
43 import org.apache.hadoop.hbase.TableName;
44 import org.apache.hadoop.hbase.TableNotFoundException;
45 import org.apache.hadoop.hbase.classification.InterfaceAudience;
46 import org.apache.hadoop.hbase.classification.InterfaceStability;
47 import org.apache.hadoop.hbase.client.Admin;
48 import org.apache.hadoop.hbase.client.ClusterConnection;
49 import org.apache.hadoop.hbase.client.Connection;
50 import org.apache.hadoop.hbase.client.ConnectionFactory;
51 import org.apache.hadoop.hbase.client.HBaseAdmin;
52 import org.apache.hadoop.hbase.client.HConnection;
53 import org.apache.hadoop.hbase.client.HTable;
54 import org.apache.hadoop.hbase.client.NeedUnmanagedConnectionException;
55 import org.apache.hadoop.hbase.client.RegionLocator;
56 import org.apache.hadoop.hbase.client.RegionServerCallable;
57 import org.apache.hadoop.hbase.client.RpcRetryingCallerFactory;
58 import org.apache.hadoop.hbase.client.Table;
59 import org.apache.hadoop.hbase.client.coprocessor.SecureBulkLoadClient;
60 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
61 import org.apache.hadoop.hbase.io.HFileLink;
62 import org.apache.hadoop.hbase.io.HalfStoreFileReader;
63 import org.apache.hadoop.hbase.io.Reference;
64 import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
65 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
66 import org.apache.hadoop.hbase.io.hfile.HFile;
67 import org.apache.hadoop.hbase.io.hfile.HFileContext;
68 import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder;
69 import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoder;
70 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
71 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
72 import org.apache.hadoop.hbase.regionserver.BloomType;
73 import org.apache.hadoop.hbase.regionserver.HStore;
74 import org.apache.hadoop.hbase.regionserver.StoreFile;
75 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
76 import org.apache.hadoop.hbase.security.UserProvider;
77 import org.apache.hadoop.hbase.security.token.FsDelegationToken;
78 import org.apache.hadoop.hbase.util.Bytes;
79 import org.apache.hadoop.hbase.util.FSHDFSUtils;
80 import org.apache.hadoop.hbase.util.Pair;
81 import org.apache.hadoop.util.Tool;
82 import org.apache.hadoop.util.ToolRunner;
83
84 import java.io.FileNotFoundException;
85 import java.io.IOException;
86 import java.io.InterruptedIOException;
87 import java.nio.ByteBuffer;
88 import java.util.ArrayList;
89 import java.util.Arrays;
90 import java.util.Collection;
91 import java.util.Deque;
92 import java.util.HashMap;
93 import java.util.HashSet;
94 import java.util.Iterator;
95 import java.util.LinkedList;
96 import java.util.List;
97 import java.util.Map;
98 import java.util.Map.Entry;
99 import java.util.Set;
100 import java.util.TreeMap;
101 import java.util.UUID;
102 import java.util.concurrent.Callable;
103 import java.util.concurrent.ExecutionException;
104 import java.util.concurrent.ExecutorService;
105 import java.util.concurrent.Future;
106 import java.util.concurrent.LinkedBlockingQueue;
107 import java.util.concurrent.ThreadPoolExecutor;
108 import java.util.concurrent.TimeUnit;
109
110
111
112
113
114 @InterfaceAudience.Public
115 @InterfaceStability.Stable
116 public class LoadIncrementalHFiles extends Configured implements Tool {
117 private static final Log LOG = LogFactory.getLog(LoadIncrementalHFiles.class);
118 private Admin hbAdmin;
119
120 public static final String NAME = "completebulkload";
121 public static final String MAX_FILES_PER_REGION_PER_FAMILY
122 = "hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily";
123 private static final String ASSIGN_SEQ_IDS = "hbase.mapreduce.bulkload.assign.sequenceNumbers";
124 public final static String CREATE_TABLE_CONF_KEY = "create.table";
125
126 private int maxFilesPerRegionPerFamily;
127 private boolean assignSeqIds;
128
129
130 private FileSystem fs;
131
132 private FsDelegationToken fsDelegationToken;
133 private String bulkToken;
134 private UserProvider userProvider;
135
136 private LoadIncrementalHFiles() {}
137
138 public LoadIncrementalHFiles(Configuration conf) throws Exception {
139 super(conf);
140 initialize();
141 }
142
143 private void initialize() throws Exception {
144 if (hbAdmin == null) {
145
146 setConf(HBaseConfiguration.create(getConf()));
147 Configuration conf = getConf();
148
149 conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0);
150 this.hbAdmin = new HBaseAdmin(conf);
151 this.userProvider = UserProvider.instantiate(conf);
152 this.fsDelegationToken = new FsDelegationToken(userProvider, "renewer");
153 assignSeqIds = conf.getBoolean(ASSIGN_SEQ_IDS, true);
154 maxFilesPerRegionPerFamily = conf.getInt(MAX_FILES_PER_REGION_PER_FAMILY, 32);
155 }
156 }
157
158 private void usage() {
159 System.err.println("usage: " + NAME + " /path/to/hfileoutputformat-output tablename" + "\n -D"
160 + CREATE_TABLE_CONF_KEY + "=no - can be used to avoid creation of table by this tool\n"
161 + " Note: if you set this to 'no', then the target table must already exist in HBase\n"
162 + "\n");
163 }
164
165 private static interface BulkHFileVisitor<TFamily> {
166 TFamily bulkFamily(final byte[] familyName)
167 throws IOException;
168 void bulkHFile(final TFamily family, final FileStatus hfileStatus)
169 throws IOException;
170 }
171
172
173
174
175
176 private static <TFamily> void visitBulkHFiles(final FileSystem fs, final Path bulkDir,
177 final BulkHFileVisitor<TFamily> visitor) throws IOException {
178 if (!fs.exists(bulkDir)) {
179 throw new FileNotFoundException("Bulkload dir " + bulkDir + " not found");
180 }
181
182 FileStatus[] familyDirStatuses = fs.listStatus(bulkDir);
183 if (familyDirStatuses == null) {
184 throw new FileNotFoundException("No families found in " + bulkDir);
185 }
186
187 for (FileStatus familyStat : familyDirStatuses) {
188 if (!familyStat.isDirectory()) {
189 LOG.warn("Skipping non-directory " + familyStat.getPath());
190 continue;
191 }
192 Path familyDir = familyStat.getPath();
193 byte[] familyName = familyDir.getName().getBytes();
194 TFamily family = visitor.bulkFamily(familyName);
195
196 FileStatus[] hfileStatuses = fs.listStatus(familyDir);
197 for (FileStatus hfileStatus : hfileStatuses) {
198 if (!fs.isFile(hfileStatus.getPath())) {
199 LOG.warn("Skipping non-file " + hfileStatus);
200 continue;
201 }
202
203 Path hfile = hfileStatus.getPath();
204
205 String fileName = hfile.getName();
206 if (fileName.startsWith("_")) {
207 continue;
208 }
209 if (StoreFileInfo.isReference(fileName)) {
210 LOG.warn("Skipping reference " + fileName);
211 continue;
212 }
213 if (HFileLink.isHFileLink(fileName)) {
214 LOG.warn("Skipping HFileLink " + fileName);
215 continue;
216 }
217
218
219 try {
220 if (!HFile.isHFileFormat(fs, hfile)) {
221 LOG.warn("the file " + hfile + " doesn't seems to be an hfile. skipping");
222 continue;
223 }
224 } catch (FileNotFoundException e) {
225 LOG.warn("the file " + hfile + " was removed");
226 continue;
227 }
228
229 visitor.bulkHFile(family, hfileStatus);
230 }
231 }
232 }
233
234
235
236
237
238
239
240
241
242 static class LoadQueueItem {
243 final byte[] family;
244 final Path hfilePath;
245
246 public LoadQueueItem(byte[] family, Path hfilePath) {
247 this.family = family;
248 this.hfilePath = hfilePath;
249 }
250
251 @Override
252 public String toString() {
253 return "family:"+ Bytes.toString(family) + " path:" + hfilePath.toString();
254 }
255 }
256
257
258
259
260
261 private void discoverLoadQueue(final Deque<LoadQueueItem> ret, final Path hfofDir)
262 throws IOException {
263 fs = hfofDir.getFileSystem(getConf());
264 visitBulkHFiles(fs, hfofDir, new BulkHFileVisitor<byte[]>() {
265 @Override
266 public byte[] bulkFamily(final byte[] familyName) {
267 return familyName;
268 }
269 @Override
270 public void bulkHFile(final byte[] family, final FileStatus hfile) throws IOException {
271 long length = hfile.getLen();
272 if (length > getConf().getLong(HConstants.HREGION_MAX_FILESIZE,
273 HConstants.DEFAULT_MAX_FILE_SIZE)) {
274 LOG.warn("Trying to bulk load hfile " + hfile.getPath() + " with size: " +
275 length + " bytes can be problematic as it may lead to oversplitting.");
276 }
277 ret.add(new LoadQueueItem(family, hfile.getPath()));
278 }
279 });
280 }
281
282
283
284
285
286
287
288
289
290
291 @SuppressWarnings("deprecation")
292 public void doBulkLoad(Path hfofDir, final HTable table)
293 throws TableNotFoundException, IOException
294 {
295 Admin admin = null;
296 Table t = table;
297 Connection conn = table.getConnection();
298 boolean closeConnWhenFinished = false;
299 try {
300 if (conn instanceof ClusterConnection && ((ClusterConnection) conn).isManaged()) {
301 LOG.warn("managed connection cannot be used for bulkload. Creating unmanaged connection.");
302
303 conn = ConnectionFactory.createConnection(table.getConfiguration());
304 t = conn.getTable(table.getName());
305 closeConnWhenFinished = true;
306 if (conn instanceof ClusterConnection && ((ClusterConnection) conn).isManaged()) {
307 throw new RuntimeException("Failed to create unmanaged connection.");
308 }
309 admin = conn.getAdmin();
310 } else {
311 admin = conn.getAdmin();
312 }
313 try (RegionLocator rl = conn.getRegionLocator(t.getName())) {
314 doBulkLoad(hfofDir, admin, t, rl);
315 }
316 } finally {
317 if (admin != null) admin.close();
318 if (closeConnWhenFinished) {
319 t.close();
320 conn.close();
321 }
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334 @SuppressWarnings("deprecation")
335 public void doBulkLoad(Path hfofDir, final Admin admin, Table table,
336 RegionLocator regionLocator) throws TableNotFoundException, IOException {
337
338 if (!admin.isTableAvailable(regionLocator.getName())) {
339 throw new TableNotFoundException("Table " + table.getName() + " is not currently available.");
340 }
341
342
343 int nrThreads = getConf().getInt("hbase.loadincremental.threads.max",
344 Runtime.getRuntime().availableProcessors());
345 ThreadFactoryBuilder builder = new ThreadFactoryBuilder();
346 builder.setNameFormat("LoadIncrementalHFiles-%1$d");
347 ExecutorService pool = new ThreadPoolExecutor(nrThreads, nrThreads,
348 60, TimeUnit.SECONDS,
349 new LinkedBlockingQueue<Runnable>(),
350 builder.build());
351 ((ThreadPoolExecutor)pool).allowCoreThreadTimeOut(true);
352
353
354
355 Deque<LoadQueueItem> queue = new LinkedList<LoadQueueItem>();
356 try {
357 discoverLoadQueue(queue, hfofDir);
358
359 Collection<HColumnDescriptor> families = table.getTableDescriptor().getFamilies();
360 ArrayList<String> familyNames = new ArrayList<String>(families.size());
361 for (HColumnDescriptor family : families) {
362 familyNames.add(family.getNameAsString());
363 }
364 ArrayList<String> unmatchedFamilies = new ArrayList<String>();
365 Iterator<LoadQueueItem> queueIter = queue.iterator();
366 while (queueIter.hasNext()) {
367 LoadQueueItem lqi = queueIter.next();
368 String familyNameInHFile = Bytes.toString(lqi.family);
369 if (!familyNames.contains(familyNameInHFile)) {
370 unmatchedFamilies.add(familyNameInHFile);
371 }
372 }
373 if (unmatchedFamilies.size() > 0) {
374 String msg =
375 "Unmatched family names found: unmatched family names in HFiles to be bulkloaded: "
376 + unmatchedFamilies + "; valid family names of table "
377 + table.getName() + " are: " + familyNames;
378 LOG.error(msg);
379 throw new IOException(msg);
380 }
381 int count = 0;
382
383 if (queue.isEmpty()) {
384 LOG.warn("Bulk load operation did not find any files to load in " +
385 "directory " + hfofDir.toUri() + ". Does it contain files in " +
386 "subdirectories that correspond to column family names?");
387 return;
388 }
389
390
391
392
393 fsDelegationToken.acquireDelegationToken(fs);
394 if(isSecureBulkLoadEndpointAvailable()) {
395 bulkToken = new SecureBulkLoadClient(table).prepareBulkLoad(table.getName());
396 }
397
398
399 while (!queue.isEmpty()) {
400
401 final Pair<byte[][], byte[][]> startEndKeys = regionLocator.getStartEndKeys();
402 if (count != 0) {
403 LOG.info("Split occured while grouping HFiles, retry attempt " +
404 + count + " with " + queue.size() + " files remaining to group or split");
405 }
406
407 int maxRetries = getConf().getInt("hbase.bulkload.retries.number", 10);
408 if (maxRetries != 0 && count >= maxRetries) {
409 throw new IOException("Retry attempted " + count +
410 " times without completing, bailing out");
411 }
412 count++;
413
414
415 Multimap<ByteBuffer, LoadQueueItem> regionGroups = groupOrSplitPhase(table,
416 pool, queue, startEndKeys);
417
418 if (!checkHFilesCountPerRegionPerFamily(regionGroups)) {
419
420 throw new IOException("Trying to load more than " + maxFilesPerRegionPerFamily
421 + " hfiles to one family of one region");
422 }
423
424 bulkLoadPhase(table, admin.getConnection(), pool, queue, regionGroups);
425
426
427
428
429 }
430
431 } finally {
432 fsDelegationToken.releaseDelegationToken();
433 if(bulkToken != null) {
434 new SecureBulkLoadClient(table).cleanupBulkLoad(bulkToken);
435 }
436 pool.shutdown();
437 if (queue != null && !queue.isEmpty()) {
438 StringBuilder err = new StringBuilder();
439 err.append("-------------------------------------------------\n");
440 err.append("Bulk load aborted with some files not yet loaded:\n");
441 err.append("-------------------------------------------------\n");
442 for (LoadQueueItem q : queue) {
443 err.append(" ").append(q.hfilePath).append('\n');
444 }
445 LOG.error(err);
446 }
447 }
448
449 if (queue != null && !queue.isEmpty()) {
450 throw new RuntimeException("Bulk load aborted with some files not yet loaded."
451 + "Please check log for more details.");
452 }
453 }
454
455
456
457
458
459
460 protected void bulkLoadPhase(final Table table, final Connection conn,
461 ExecutorService pool, Deque<LoadQueueItem> queue,
462 final Multimap<ByteBuffer, LoadQueueItem> regionGroups) throws IOException {
463
464 Set<Future<List<LoadQueueItem>>> loadingFutures = new HashSet<Future<List<LoadQueueItem>>>();
465 for (Entry<ByteBuffer, ? extends Collection<LoadQueueItem>> e: regionGroups.asMap().entrySet()) {
466 final byte[] first = e.getKey().array();
467 final Collection<LoadQueueItem> lqis = e.getValue();
468
469 final Callable<List<LoadQueueItem>> call = new Callable<List<LoadQueueItem>>() {
470 @Override
471 public List<LoadQueueItem> call() throws Exception {
472 List<LoadQueueItem> toRetry =
473 tryAtomicRegionLoad(conn, table.getName(), first, lqis);
474 return toRetry;
475 }
476 };
477 loadingFutures.add(pool.submit(call));
478 }
479
480
481 for (Future<List<LoadQueueItem>> future : loadingFutures) {
482 try {
483 List<LoadQueueItem> toRetry = future.get();
484
485
486 queue.addAll(toRetry);
487
488 } catch (ExecutionException e1) {
489 Throwable t = e1.getCause();
490 if (t instanceof IOException) {
491
492
493 throw new IOException("BulkLoad encountered an unrecoverable problem", t);
494 }
495 LOG.error("Unexpected execution exception during bulk load", e1);
496 throw new IllegalStateException(t);
497 } catch (InterruptedException e1) {
498 LOG.error("Unexpected interrupted exception during bulk load", e1);
499 throw (InterruptedIOException)new InterruptedIOException().initCause(e1);
500 }
501 }
502 }
503
504 private boolean checkHFilesCountPerRegionPerFamily(
505 final Multimap<ByteBuffer, LoadQueueItem> regionGroups) {
506 for (Entry<ByteBuffer,
507 ? extends Collection<LoadQueueItem>> e: regionGroups.asMap().entrySet()) {
508 final Collection<LoadQueueItem> lqis = e.getValue();
509 HashMap<byte[], MutableInt> filesMap = new HashMap<byte[], MutableInt>();
510 for (LoadQueueItem lqi: lqis) {
511 MutableInt count = filesMap.get(lqi.family);
512 if (count == null) {
513 count = new MutableInt();
514 filesMap.put(lqi.family, count);
515 }
516 count.increment();
517 if (count.intValue() > maxFilesPerRegionPerFamily) {
518 LOG.error("Trying to load more than " + maxFilesPerRegionPerFamily
519 + " hfiles to family " + Bytes.toStringBinary(lqi.family)
520 + " of region with start key "
521 + Bytes.toStringBinary(e.getKey()));
522 return false;
523 }
524 }
525 }
526 return true;
527 }
528
529
530
531
532
533 private Multimap<ByteBuffer, LoadQueueItem> groupOrSplitPhase(final Table table,
534 ExecutorService pool, Deque<LoadQueueItem> queue,
535 final Pair<byte[][], byte[][]> startEndKeys) throws IOException {
536
537
538 Multimap<ByteBuffer, LoadQueueItem> rgs = HashMultimap.create();
539 final Multimap<ByteBuffer, LoadQueueItem> regionGroups = Multimaps.synchronizedMultimap(rgs);
540
541
542 Set<Future<List<LoadQueueItem>>> splittingFutures = new HashSet<Future<List<LoadQueueItem>>>();
543 while (!queue.isEmpty()) {
544 final LoadQueueItem item = queue.remove();
545
546 final Callable<List<LoadQueueItem>> call = new Callable<List<LoadQueueItem>>() {
547 @Override
548 public List<LoadQueueItem> call() throws Exception {
549 List<LoadQueueItem> splits = groupOrSplit(regionGroups, item, table, startEndKeys);
550 return splits;
551 }
552 };
553 splittingFutures.add(pool.submit(call));
554 }
555
556
557 for (Future<List<LoadQueueItem>> lqis : splittingFutures) {
558 try {
559 List<LoadQueueItem> splits = lqis.get();
560 if (splits != null) {
561 queue.addAll(splits);
562 }
563 } catch (ExecutionException e1) {
564 Throwable t = e1.getCause();
565 if (t instanceof IOException) {
566 LOG.error("IOException during splitting", e1);
567 throw (IOException)t;
568 }
569 LOG.error("Unexpected execution exception during splitting", e1);
570 throw new IllegalStateException(t);
571 } catch (InterruptedException e1) {
572 LOG.error("Unexpected interrupted exception during splitting", e1);
573 throw (InterruptedIOException)new InterruptedIOException().initCause(e1);
574 }
575 }
576 return regionGroups;
577 }
578
579
580 private String getUniqueName() {
581 return UUID.randomUUID().toString().replaceAll("-", "");
582 }
583
584 protected List<LoadQueueItem> splitStoreFile(final LoadQueueItem item,
585 final Table table, byte[] startKey,
586 byte[] splitKey) throws IOException {
587 final Path hfilePath = item.hfilePath;
588
589
590
591 final Path tmpDir = new Path(item.hfilePath.getParent(), "_tmp");
592
593 LOG.info("HFile at " + hfilePath + " no longer fits inside a single " +
594 "region. Splitting...");
595
596 String uniqueName = getUniqueName();
597 HColumnDescriptor familyDesc = table.getTableDescriptor().getFamily(item.family);
598 Path botOut = new Path(tmpDir, uniqueName + ".bottom");
599 Path topOut = new Path(tmpDir, uniqueName + ".top");
600 splitStoreFile(getConf(), hfilePath, familyDesc, splitKey,
601 botOut, topOut);
602
603 FileSystem fs = tmpDir.getFileSystem(getConf());
604 fs.setPermission(tmpDir, FsPermission.valueOf("-rwxrwxrwx"));
605 fs.setPermission(botOut, FsPermission.valueOf("-rwxrwxrwx"));
606 fs.setPermission(topOut, FsPermission.valueOf("-rwxrwxrwx"));
607
608
609
610 List<LoadQueueItem> lqis = new ArrayList<LoadQueueItem>(2);
611 lqis.add(new LoadQueueItem(item.family, botOut));
612 lqis.add(new LoadQueueItem(item.family, topOut));
613
614 LOG.info("Successfully split into new HFiles " + botOut + " and " + topOut);
615 return lqis;
616 }
617
618
619
620
621
622
623
624
625
626
627 protected List<LoadQueueItem> groupOrSplit(Multimap<ByteBuffer, LoadQueueItem> regionGroups,
628 final LoadQueueItem item, final Table table,
629 final Pair<byte[][], byte[][]> startEndKeys)
630 throws IOException {
631 final Path hfilePath = item.hfilePath;
632 HFile.Reader hfr = HFile.createReader(fs, hfilePath,
633 new CacheConfig(getConf()), getConf());
634 final byte[] first, last;
635 try {
636 hfr.loadFileInfo();
637 first = hfr.getFirstRowKey();
638 last = hfr.getLastRowKey();
639 } finally {
640 hfr.close();
641 }
642
643 LOG.info("Trying to load hfile=" + hfilePath +
644 " first=" + Bytes.toStringBinary(first) +
645 " last=" + Bytes.toStringBinary(last));
646 if (first == null || last == null) {
647 assert first == null && last == null;
648
649 LOG.info("hfile " + hfilePath + " has no entries, skipping");
650 return null;
651 }
652 if (Bytes.compareTo(first, last) > 0) {
653 throw new IllegalArgumentException(
654 "Invalid range: " + Bytes.toStringBinary(first) +
655 " > " + Bytes.toStringBinary(last));
656 }
657 int idx = Arrays.binarySearch(startEndKeys.getFirst(), first,
658 Bytes.BYTES_COMPARATOR);
659 if (idx < 0) {
660
661
662 idx = -(idx + 1) - 1;
663 }
664 final int indexForCallable = idx;
665
666
667
668
669
670
671 if (indexForCallable < 0) {
672 throw new IOException("The first region info for table "
673 + table.getName()
674 + " cann't be found in hbase:meta.Please use hbck tool to fix it first.");
675 } else if ((indexForCallable == startEndKeys.getFirst().length - 1)
676 && !Bytes.equals(startEndKeys.getSecond()[indexForCallable], HConstants.EMPTY_BYTE_ARRAY)) {
677 throw new IOException("The last region info for table "
678 + table.getName()
679 + " cann't be found in hbase:meta.Please use hbck tool to fix it first.");
680 } else if (indexForCallable + 1 < startEndKeys.getFirst().length
681 && !(Bytes.compareTo(startEndKeys.getSecond()[indexForCallable],
682 startEndKeys.getFirst()[indexForCallable + 1]) == 0)) {
683 throw new IOException("The endkey of one region for table "
684 + table.getName()
685 + " is not equal to the startkey of the next region in hbase:meta."
686 + "Please use hbck tool to fix it first.");
687 }
688
689 boolean lastKeyInRange =
690 Bytes.compareTo(last, startEndKeys.getSecond()[idx]) < 0 ||
691 Bytes.equals(startEndKeys.getSecond()[idx], HConstants.EMPTY_BYTE_ARRAY);
692 if (!lastKeyInRange) {
693 List<LoadQueueItem> lqis = splitStoreFile(item, table,
694 startEndKeys.getFirst()[indexForCallable],
695 startEndKeys.getSecond()[indexForCallable]);
696 return lqis;
697 }
698
699
700 regionGroups.put(ByteBuffer.wrap(startEndKeys.getFirst()[idx]), item);
701 return null;
702 }
703
704
705
706
707
708
709
710 @Deprecated
711 protected List<LoadQueueItem> tryAtomicRegionLoad(final HConnection conn,
712 final byte [] tableName, final byte[] first, Collection<LoadQueueItem> lqis)
713 throws IOException {
714 return tryAtomicRegionLoad(conn, TableName.valueOf(tableName), first, lqis);
715 }
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730 protected List<LoadQueueItem> tryAtomicRegionLoad(final Connection conn,
731 final TableName tableName, final byte[] first, Collection<LoadQueueItem> lqis)
732 throws IOException {
733 final List<Pair<byte[], String>> famPaths =
734 new ArrayList<Pair<byte[], String>>(lqis.size());
735 for (LoadQueueItem lqi : lqis) {
736 famPaths.add(Pair.newPair(lqi.family, lqi.hfilePath.toString()));
737 }
738
739 final RegionServerCallable<Boolean> svrCallable =
740 new RegionServerCallable<Boolean>(conn, tableName, first) {
741 @Override
742 public Boolean call(int callTimeout) throws Exception {
743 SecureBulkLoadClient secureClient = null;
744 boolean success = false;
745
746 try {
747 LOG.debug("Going to connect to server " + getLocation() + " for row "
748 + Bytes.toStringBinary(getRow()) + " with hfile group " + famPaths);
749 byte[] regionName = getLocation().getRegionInfo().getRegionName();
750 if (!isSecureBulkLoadEndpointAvailable()) {
751 success = ProtobufUtil.bulkLoadHFile(getStub(), famPaths, regionName, assignSeqIds);
752 } else {
753 try (Table table = conn.getTable(getTableName())) {
754 secureClient = new SecureBulkLoadClient(table);
755 success = secureClient.bulkLoadHFiles(famPaths, fsDelegationToken.getUserToken(),
756 bulkToken, getLocation().getRegionInfo().getStartKey());
757 }
758 }
759 return success;
760 } finally {
761
762
763
764 if(secureClient != null && !success) {
765 FileSystem targetFs = FileSystem.get(getConf());
766
767
768
769 if (FSHDFSUtils.isSameHdfs(getConf(), fs, targetFs)) {
770 for(Pair<byte[], String> el : famPaths) {
771 Path hfileStagingPath = null;
772 Path hfileOrigPath = new Path(el.getSecond());
773 try {
774 hfileStagingPath= new Path(secureClient.getStagingPath(bulkToken, el.getFirst()),
775 hfileOrigPath.getName());
776 if(targetFs.rename(hfileStagingPath, hfileOrigPath)) {
777 LOG.debug("Moved back file " + hfileOrigPath + " from " +
778 hfileStagingPath);
779 } else if(targetFs.exists(hfileStagingPath)){
780 LOG.debug("Unable to move back file " + hfileOrigPath + " from " +
781 hfileStagingPath);
782 }
783 } catch(Exception ex) {
784 LOG.debug("Unable to move back file " + hfileOrigPath + " from " +
785 hfileStagingPath, ex);
786 }
787 }
788 }
789 }
790 }
791 }
792 };
793
794 try {
795 List<LoadQueueItem> toRetry = new ArrayList<LoadQueueItem>();
796 Configuration conf = getConf();
797 boolean success = RpcRetryingCallerFactory.instantiate(conf,
798 null).<Boolean> newCaller()
799 .callWithRetries(svrCallable, Integer.MAX_VALUE);
800 if (!success) {
801 LOG.warn("Attempt to bulk load region containing "
802 + Bytes.toStringBinary(first) + " into table "
803 + tableName + " with files " + lqis
804 + " failed. This is recoverable and they will be retried.");
805 toRetry.addAll(lqis);
806 }
807
808 return toRetry;
809 } catch (IOException e) {
810 LOG.error("Encountered unrecoverable error from region server, additional details: "
811 + svrCallable.getExceptionMessageAdditionalDetail(), e);
812 throw e;
813 }
814 }
815
816 private boolean isSecureBulkLoadEndpointAvailable() {
817 String classes = getConf().get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, "");
818 return classes.contains("org.apache.hadoop.hbase.security.access.SecureBulkLoadEndpoint");
819 }
820
821
822
823
824
825 static void splitStoreFile(
826 Configuration conf, Path inFile,
827 HColumnDescriptor familyDesc, byte[] splitKey,
828 Path bottomOut, Path topOut) throws IOException
829 {
830
831 Reference topReference = Reference.createTopReference(splitKey);
832 Reference bottomReference = Reference.createBottomReference(splitKey);
833
834 copyHFileHalf(conf, inFile, topOut, topReference, familyDesc);
835 copyHFileHalf(conf, inFile, bottomOut, bottomReference, familyDesc);
836 }
837
838
839
840
841 private static void copyHFileHalf(
842 Configuration conf, Path inFile, Path outFile, Reference reference,
843 HColumnDescriptor familyDescriptor)
844 throws IOException {
845 FileSystem fs = inFile.getFileSystem(conf);
846 CacheConfig cacheConf = new CacheConfig(conf);
847 HalfStoreFileReader halfReader = null;
848 StoreFile.Writer halfWriter = null;
849 try {
850 halfReader = new HalfStoreFileReader(fs, inFile, cacheConf, reference, conf);
851 Map<byte[], byte[]> fileInfo = halfReader.loadFileInfo();
852
853 int blocksize = familyDescriptor.getBlocksize();
854 Algorithm compression = familyDescriptor.getCompression();
855 BloomType bloomFilterType = familyDescriptor.getBloomFilterType();
856 HFileContext hFileContext = new HFileContextBuilder()
857 .withCompression(compression)
858 .withChecksumType(HStore.getChecksumType(conf))
859 .withBytesPerCheckSum(HStore.getBytesPerChecksum(conf))
860 .withBlockSize(blocksize)
861 .withDataBlockEncoding(familyDescriptor.getDataBlockEncoding())
862 .withIncludesTags(true)
863 .build();
864 halfWriter = new StoreFile.WriterBuilder(conf, cacheConf,
865 fs)
866 .withFilePath(outFile)
867 .withBloomType(bloomFilterType)
868 .withFileContext(hFileContext)
869 .build();
870 HFileScanner scanner = halfReader.getScanner(false, false, false);
871 scanner.seekTo();
872 do {
873 KeyValue kv = KeyValueUtil.ensureKeyValue(scanner.getKeyValue());
874 halfWriter.append(kv);
875 } while (scanner.next());
876
877 for (Map.Entry<byte[],byte[]> entry : fileInfo.entrySet()) {
878 if (shouldCopyHFileMetaKey(entry.getKey())) {
879 halfWriter.appendFileInfo(entry.getKey(), entry.getValue());
880 }
881 }
882 } finally {
883 if (halfWriter != null) halfWriter.close();
884 if (halfReader != null) halfReader.close(cacheConf.shouldEvictOnClose());
885 }
886 }
887
888 private static boolean shouldCopyHFileMetaKey(byte[] key) {
889
890 if (Bytes.equals(key, HFileDataBlockEncoder.DATA_BLOCK_ENCODING)) {
891 return false;
892 }
893
894 return !HFile.isReservedFileInfoKey(key);
895 }
896
897 private boolean doesTableExist(TableName tableName) throws Exception {
898 return hbAdmin.tableExists(tableName);
899 }
900
901
902
903
904
905
906
907
908
909
910
911
912
913 public static byte[][] inferBoundaries(TreeMap<byte[], Integer> bdryMap) {
914 ArrayList<byte[]> keysArray = new ArrayList<byte[]>();
915 int runningValue = 0;
916 byte[] currStartKey = null;
917 boolean firstBoundary = true;
918
919 for (Map.Entry<byte[], Integer> item: bdryMap.entrySet()) {
920 if (runningValue == 0) currStartKey = item.getKey();
921 runningValue += item.getValue();
922 if (runningValue == 0) {
923 if (!firstBoundary) keysArray.add(currStartKey);
924 firstBoundary = false;
925 }
926 }
927
928 return keysArray.toArray(new byte[0][0]);
929 }
930
931
932
933
934
935 private void createTable(TableName tableName, String dirPath) throws Exception {
936 final Path hfofDir = new Path(dirPath);
937 final FileSystem fs = hfofDir.getFileSystem(getConf());
938
939
940
941 final HTableDescriptor htd = new HTableDescriptor(tableName);
942 final TreeMap<byte[], Integer> map = new TreeMap<byte[], Integer>(Bytes.BYTES_COMPARATOR);
943 visitBulkHFiles(fs, hfofDir, new BulkHFileVisitor<HColumnDescriptor>() {
944 @Override
945 public HColumnDescriptor bulkFamily(final byte[] familyName) {
946 HColumnDescriptor hcd = new HColumnDescriptor(familyName);
947 htd.addFamily(hcd);
948 return hcd;
949 }
950 @Override
951 public void bulkHFile(final HColumnDescriptor hcd, final FileStatus hfileStatus)
952 throws IOException {
953 Path hfile = hfileStatus.getPath();
954 HFile.Reader reader = HFile.createReader(fs, hfile,
955 new CacheConfig(getConf()), getConf());
956 try {
957 if (hcd.getCompressionType() != reader.getFileContext().getCompression()) {
958 hcd.setCompressionType(reader.getFileContext().getCompression());
959 LOG.info("Setting compression " + hcd.getCompressionType().name() +
960 " for family " + hcd.toString());
961 }
962 reader.loadFileInfo();
963 byte[] first = reader.getFirstRowKey();
964 byte[] last = reader.getLastRowKey();
965
966 LOG.info("Trying to figure out region boundaries hfile=" + hfile +
967 " first=" + Bytes.toStringBinary(first) +
968 " last=" + Bytes.toStringBinary(last));
969
970
971 Integer value = map.containsKey(first)? map.get(first):0;
972 map.put(first, value+1);
973
974 value = map.containsKey(last)? map.get(last):0;
975 map.put(last, value-1);
976 } finally {
977 reader.close();
978 }
979 }
980 });
981
982 byte[][] keys = LoadIncrementalHFiles.inferBoundaries(map);
983 this.hbAdmin.createTable(htd,keys);
984
985 LOG.info("Table "+ tableName +" is available!!");
986 }
987
988 @Override
989 public int run(String[] args) throws Exception {
990 if (args.length != 2) {
991 usage();
992 return -1;
993 }
994
995 initialize();
996
997 String dirPath = args[0];
998 TableName tableName = TableName.valueOf(args[1]);
999
1000 boolean tableExists = this.doesTableExist(tableName);
1001 if (!tableExists) {
1002 if ("yes".equalsIgnoreCase(getConf().get(CREATE_TABLE_CONF_KEY, "yes"))) {
1003 this.createTable(tableName, dirPath);
1004 } else {
1005 String errorMsg = format("Table '%s' does not exist.", tableName);
1006 LOG.error(errorMsg);
1007 throw new TableNotFoundException(errorMsg);
1008 }
1009 }
1010
1011 Path hfofDir = new Path(dirPath);
1012
1013 try (Connection connection = ConnectionFactory.createConnection(getConf());
1014 HTable table = (HTable) connection.getTable(tableName);) {
1015 doBulkLoad(hfofDir, table);
1016 }
1017 return 0;
1018 }
1019
1020 public static void main(String[] args) throws Exception {
1021 Configuration conf = HBaseConfiguration.create();
1022 int ret = ToolRunner.run(conf, new LoadIncrementalHFiles(), args);
1023 System.exit(ret);
1024 }
1025
1026 }