001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.replication.regionserver;
019
020import static org.junit.Assert.assertEquals;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030import java.util.Random;
031import java.util.Set;
032import java.util.concurrent.ThreadLocalRandom;
033import java.util.concurrent.atomic.AtomicBoolean;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.fs.FileSystem;
036import org.apache.hadoop.fs.FileUtil;
037import org.apache.hadoop.fs.Path;
038import org.apache.hadoop.hbase.Cell;
039import org.apache.hadoop.hbase.CellUtil;
040import org.apache.hadoop.hbase.HBaseClassTestRule;
041import org.apache.hadoop.hbase.HBaseTestingUtility;
042import org.apache.hadoop.hbase.HConstants;
043import org.apache.hadoop.hbase.HRegionInfo;
044import org.apache.hadoop.hbase.KeyValue;
045import org.apache.hadoop.hbase.Stoppable;
046import org.apache.hadoop.hbase.TableName;
047import org.apache.hadoop.hbase.TableNotFoundException;
048import org.apache.hadoop.hbase.client.Admin;
049import org.apache.hadoop.hbase.client.Connection;
050import org.apache.hadoop.hbase.client.ConnectionFactory;
051import org.apache.hadoop.hbase.client.Get;
052import org.apache.hadoop.hbase.client.RegionLocator;
053import org.apache.hadoop.hbase.client.Result;
054import org.apache.hadoop.hbase.client.ResultScanner;
055import org.apache.hadoop.hbase.client.RetriesExhaustedException;
056import org.apache.hadoop.hbase.client.Scan;
057import org.apache.hadoop.hbase.client.Table;
058import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
059import org.apache.hadoop.hbase.testclassification.LargeTests;
060import org.apache.hadoop.hbase.testclassification.ReplicationTests;
061import org.apache.hadoop.hbase.util.Bytes;
062import org.apache.hadoop.hbase.util.CommonFSUtils;
063import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
064import org.apache.hadoop.hbase.util.HFileTestUtil;
065import org.junit.AfterClass;
066import org.junit.Assert;
067import org.junit.Before;
068import org.junit.BeforeClass;
069import org.junit.ClassRule;
070import org.junit.Test;
071import org.junit.experimental.categories.Category;
072import org.slf4j.Logger;
073import org.slf4j.LoggerFactory;
074
075import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations;
076
077import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
078import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.WALEntry;
079import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.UUID;
080import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos;
081import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.WALKey;
082
083@Category({ ReplicationTests.class, LargeTests.class })
084public class TestReplicationSink {
085
086  @ClassRule
087  public static final HBaseClassTestRule CLASS_RULE =
088    HBaseClassTestRule.forClass(TestReplicationSink.class);
089
090  private static final Logger LOG = LoggerFactory.getLogger(TestReplicationSink.class);
091  private static final int BATCH_SIZE = 10;
092
093  protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
094
095  protected static ReplicationSink SINK;
096
097  protected static final TableName TABLE_NAME1 = TableName.valueOf("table1");
098  protected static final TableName TABLE_NAME2 = TableName.valueOf("table2");
099
100  protected static final byte[] FAM_NAME1 = Bytes.toBytes("info1");
101  protected static final byte[] FAM_NAME2 = Bytes.toBytes("info2");
102
103  protected static Table table1;
104  protected static Stoppable STOPPABLE = new Stoppable() {
105    final AtomicBoolean stop = new AtomicBoolean(false);
106
107    @Override
108    public boolean isStopped() {
109      return this.stop.get();
110    }
111
112    @Override
113    public void stop(String why) {
114      LOG.info("STOPPING BECAUSE: " + why);
115      this.stop.set(true);
116    }
117
118  };
119
120  protected static Table table2;
121  protected static String baseNamespaceDir;
122  protected static String hfileArchiveDir;
123  protected static String replicationClusterId;
124
125  /**
126   * @throws java.lang.Exception
127   */
128  @BeforeClass
129  public static void setUpBeforeClass() throws Exception {
130    TEST_UTIL.getConfiguration().set("hbase.replication.source.fs.conf.provider",
131      TestSourceFSConfigurationProvider.class.getCanonicalName());
132    TEST_UTIL.startMiniCluster(3);
133    RegionServerCoprocessorHost rsCpHost =
134      TEST_UTIL.getMiniHBaseCluster().getRegionServer(0).getRegionServerCoprocessorHost();
135    SINK = new ReplicationSink(new Configuration(TEST_UTIL.getConfiguration()), rsCpHost);
136    table1 = TEST_UTIL.createTable(TABLE_NAME1, FAM_NAME1);
137    table2 = TEST_UTIL.createTable(TABLE_NAME2, FAM_NAME2);
138    Path rootDir = CommonFSUtils.getRootDir(TEST_UTIL.getConfiguration());
139    baseNamespaceDir = new Path(rootDir, new Path(HConstants.BASE_NAMESPACE_DIR)).toString();
140    hfileArchiveDir = new Path(rootDir, new Path(HConstants.HFILE_ARCHIVE_DIRECTORY)).toString();
141    replicationClusterId = "12345";
142  }
143
144  /**
145   * @throws java.lang.Exception
146   */
147  @AfterClass
148  public static void tearDownAfterClass() throws Exception {
149    STOPPABLE.stop("Shutting down");
150    TEST_UTIL.shutdownMiniCluster();
151  }
152
153  /**
154   * @throws java.lang.Exception
155   */
156  @Before
157  public void setUp() throws Exception {
158    table1 = TEST_UTIL.deleteTableData(TABLE_NAME1);
159    table2 = TEST_UTIL.deleteTableData(TABLE_NAME2);
160  }
161
162  /**
163   * Insert a whole batch of entries
164   */
165  @Test
166  public void testBatchSink() throws Exception {
167    List<WALEntry> entries = new ArrayList<>(BATCH_SIZE);
168    List<Cell> cells = new ArrayList<>();
169    for (int i = 0; i < BATCH_SIZE; i++) {
170      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
171    }
172    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
173      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
174    Scan scan = new Scan();
175    ResultScanner scanRes = table1.getScanner(scan);
176    assertEquals(BATCH_SIZE, scanRes.next(BATCH_SIZE).length);
177  }
178
179  /**
180   * Insert a mix of puts and deletes
181   */
182  @Test
183  public void testMixedPutDelete() throws Exception {
184    List<WALEntry> entries = new ArrayList<>(BATCH_SIZE / 2);
185    List<Cell> cells = new ArrayList<>();
186    for (int i = 0; i < BATCH_SIZE / 2; i++) {
187      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
188    }
189    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
190      baseNamespaceDir, hfileArchiveDir);
191
192    entries = new ArrayList<>(BATCH_SIZE);
193    cells = new ArrayList<>();
194    for (int i = 0; i < BATCH_SIZE; i++) {
195      entries.add(createEntry(TABLE_NAME1, i,
196        i % 2 != 0 ? KeyValue.Type.Put : KeyValue.Type.DeleteColumn, cells));
197    }
198
199    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
200      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
201    Scan scan = new Scan();
202    ResultScanner scanRes = table1.getScanner(scan);
203    assertEquals(BATCH_SIZE / 2, scanRes.next(BATCH_SIZE).length);
204  }
205
206  @Test
207  public void testLargeEditsPutDelete() throws Exception {
208    List<WALEntry> entries = new ArrayList<>();
209    List<Cell> cells = new ArrayList<>();
210    for (int i = 0; i < 5510; i++) {
211      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
212    }
213    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
214      baseNamespaceDir, hfileArchiveDir);
215
216    ResultScanner resultScanner = table1.getScanner(new Scan());
217    int totalRows = 0;
218    while (resultScanner.next() != null) {
219      totalRows++;
220    }
221    assertEquals(5510, totalRows);
222
223    entries = new ArrayList<>();
224    cells = new ArrayList<>();
225    for (int i = 0; i < 11000; i++) {
226      entries.add(createEntry(TABLE_NAME1, i,
227        i % 2 != 0 ? KeyValue.Type.Put : KeyValue.Type.DeleteColumn, cells));
228    }
229    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells), replicationClusterId,
230      baseNamespaceDir, hfileArchiveDir);
231    resultScanner = table1.getScanner(new Scan());
232    totalRows = 0;
233    while (resultScanner.next() != null) {
234      totalRows++;
235    }
236    assertEquals(5500, totalRows);
237  }
238
239  /**
240   * Insert to 2 different tables
241   */
242  @Test
243  public void testMixedPutTables() throws Exception {
244    List<WALEntry> entries = new ArrayList<>(BATCH_SIZE / 2);
245    List<Cell> cells = new ArrayList<>();
246    for (int i = 0; i < BATCH_SIZE; i++) {
247      entries.add(createEntry(i % 2 == 0 ? TABLE_NAME2 : TABLE_NAME1, i, KeyValue.Type.Put, cells));
248    }
249
250    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
251      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
252    Scan scan = new Scan();
253    ResultScanner scanRes = table2.getScanner(scan);
254    for (Result res : scanRes) {
255      assertEquals(0, Bytes.toInt(res.getRow()) % 2);
256    }
257    scanRes = table1.getScanner(scan);
258    for (Result res : scanRes) {
259      assertEquals(1, Bytes.toInt(res.getRow()) % 2);
260    }
261  }
262
263  /**
264   * Insert then do different types of deletes
265   */
266  @Test
267  public void testMixedDeletes() throws Exception {
268    List<WALEntry> entries = new ArrayList<>(3);
269    List<Cell> cells = new ArrayList<>();
270    for (int i = 0; i < 3; i++) {
271      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
272    }
273    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
274      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
275    entries = new ArrayList<>(3);
276    cells = new ArrayList<>();
277    entries.add(createEntry(TABLE_NAME1, 0, KeyValue.Type.DeleteColumn, cells));
278    entries.add(createEntry(TABLE_NAME1, 1, KeyValue.Type.DeleteFamily, cells));
279    entries.add(createEntry(TABLE_NAME1, 2, KeyValue.Type.DeleteColumn, cells));
280
281    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
282      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
283
284    Scan scan = new Scan();
285    ResultScanner scanRes = table1.getScanner(scan);
286    assertEquals(0, scanRes.next(3).length);
287  }
288
289  /**
290   * Puts are buffered, but this tests when a delete (not-buffered) is applied before the actual Put
291   * that creates it.
292   */
293  @Test
294  public void testApplyDeleteBeforePut() throws Exception {
295    List<WALEntry> entries = new ArrayList<>(5);
296    List<Cell> cells = new ArrayList<>();
297    for (int i = 0; i < 2; i++) {
298      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
299    }
300    entries.add(createEntry(TABLE_NAME1, 1, KeyValue.Type.DeleteFamily, cells));
301    for (int i = 3; i < 5; i++) {
302      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
303    }
304    SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
305      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
306    Get get = new Get(Bytes.toBytes(1));
307    Result res = table1.get(get);
308    assertEquals(0, res.size());
309  }
310
311  @Test
312  public void testRethrowRetriesExhaustedWithDetailsException() throws Exception {
313    TableName notExistTable = TableName.valueOf("notExistTable");
314    List<WALEntry> entries = new ArrayList<>();
315    List<Cell> cells = new ArrayList<>();
316    for (int i = 0; i < 10; i++) {
317      entries.add(createEntry(notExistTable, i, KeyValue.Type.Put, cells));
318    }
319    try {
320      SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
321        replicationClusterId, baseNamespaceDir, hfileArchiveDir);
322      Assert.fail("Should re-throw TableNotFoundException.");
323    } catch (TableNotFoundException e) {
324    }
325    entries.clear();
326    cells.clear();
327    for (int i = 0; i < 10; i++) {
328      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
329    }
330    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
331      try (Admin admin = conn.getAdmin()) {
332        admin.disableTable(TABLE_NAME1);
333        try {
334          SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
335            replicationClusterId, baseNamespaceDir, hfileArchiveDir);
336          Assert.fail("Should re-throw RetriesExhaustedException.");
337        } catch (RetriesExhaustedException e) {
338        } finally {
339          admin.enableTable(TABLE_NAME1);
340        }
341      }
342    }
343  }
344
345  /**
346   * Test replicateEntries with a bulk load entry for 25 HFiles
347   */
348  @Test
349  public void testReplicateEntriesForHFiles() throws Exception {
350    Path dir = TEST_UTIL.getDataTestDirOnTestFS("testReplicateEntries");
351    Path familyDir = new Path(dir, Bytes.toString(FAM_NAME1));
352    int numRows = 10;
353    List<Path> p = new ArrayList<>(1);
354    final String hfilePrefix = "hfile-";
355
356    // 1. Generate 25 hfile ranges
357    Random rand = ThreadLocalRandom.current();
358    Set<Integer> numbers = new HashSet<>();
359    while (numbers.size() < 50) {
360      numbers.add(rand.nextInt(1000));
361    }
362    List<Integer> numberList = new ArrayList<>(numbers);
363    Collections.sort(numberList);
364    Map<String, Long> storeFilesSize = new HashMap<>(1);
365
366    // 2. Create 25 hfiles
367    Configuration conf = TEST_UTIL.getConfiguration();
368    FileSystem fs = dir.getFileSystem(conf);
369    Iterator<Integer> numbersItr = numberList.iterator();
370    for (int i = 0; i < 25; i++) {
371      Path hfilePath = new Path(familyDir, hfilePrefix + i);
372      HFileTestUtil.createHFile(conf, fs, hfilePath, FAM_NAME1, FAM_NAME1,
373        Bytes.toBytes(numbersItr.next()), Bytes.toBytes(numbersItr.next()), numRows);
374      p.add(hfilePath);
375      storeFilesSize.put(hfilePath.getName(), fs.getFileStatus(hfilePath).getLen());
376    }
377
378    // 3. Create a BulkLoadDescriptor and a WALEdit
379    Map<byte[], List<Path>> storeFiles = new HashMap<>(1);
380    storeFiles.put(FAM_NAME1, p);
381    org.apache.hadoop.hbase.wal.WALEdit edit = null;
382    WALProtos.BulkLoadDescriptor loadDescriptor = null;
383
384    try (Connection c = ConnectionFactory.createConnection(conf);
385      RegionLocator l = c.getRegionLocator(TABLE_NAME1)) {
386      HRegionInfo regionInfo = l.getAllRegionLocations().get(0).getRegionInfo();
387      loadDescriptor = ProtobufUtil.toBulkLoadDescriptor(TABLE_NAME1,
388        UnsafeByteOperations.unsafeWrap(regionInfo.getEncodedNameAsBytes()), storeFiles,
389        storeFilesSize, 1);
390      edit = org.apache.hadoop.hbase.wal.WALEdit.createBulkLoadEvent(regionInfo, loadDescriptor);
391    }
392    List<WALEntry> entries = new ArrayList<>(1);
393
394    // 4. Create a WALEntryBuilder
395    WALEntry.Builder builder = createWALEntryBuilder(TABLE_NAME1);
396
397    // 5. Copy the hfile to the path as it is in reality
398    for (int i = 0; i < 25; i++) {
399      String pathToHfileFromNS = new StringBuilder(100).append(TABLE_NAME1.getNamespaceAsString())
400        .append(Path.SEPARATOR).append(Bytes.toString(TABLE_NAME1.getName())).append(Path.SEPARATOR)
401        .append(Bytes.toString(loadDescriptor.getEncodedRegionName().toByteArray()))
402        .append(Path.SEPARATOR).append(Bytes.toString(FAM_NAME1)).append(Path.SEPARATOR)
403        .append(hfilePrefix + i).toString();
404      String dst = baseNamespaceDir + Path.SEPARATOR + pathToHfileFromNS;
405      Path dstPath = new Path(dst);
406      FileUtil.copy(fs, p.get(0), fs, dstPath, false, conf);
407    }
408
409    entries.add(builder.build());
410    try (ResultScanner scanner = table1.getScanner(new Scan())) {
411      // 6. Assert no existing data in table
412      assertEquals(0, scanner.next(numRows).length);
413    }
414    // 7. Replicate the bulk loaded entry
415    SINK.replicateEntries(entries, CellUtil.createCellScanner(edit.getCells().iterator()),
416      replicationClusterId, baseNamespaceDir, hfileArchiveDir);
417    try (ResultScanner scanner = table1.getScanner(new Scan())) {
418      // 8. Assert data is replicated
419      assertEquals(numRows, scanner.next(numRows).length);
420    }
421    // Clean up the created hfiles or it will mess up subsequent tests
422  }
423
424  /**
425   * Test failure metrics produced for failed replication edits
426   */
427  @Test
428  public void testFailedReplicationSinkMetrics() throws IOException {
429    long initialFailedBatches = SINK.getSinkMetrics().getFailedBatches();
430    long errorCount = 0L;
431    List<WALEntry> entries = new ArrayList<>(BATCH_SIZE);
432    List<Cell> cells = new ArrayList<>();
433    for (int i = 0; i < BATCH_SIZE; i++) {
434      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
435    }
436    cells.clear(); // cause IndexOutOfBoundsException
437    try {
438      SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
439        replicationClusterId, baseNamespaceDir, hfileArchiveDir);
440      Assert.fail("Should re-throw ArrayIndexOutOfBoundsException.");
441    } catch (ArrayIndexOutOfBoundsException e) {
442      errorCount++;
443      assertEquals(initialFailedBatches + errorCount, SINK.getSinkMetrics().getFailedBatches());
444    }
445
446    entries.clear();
447    cells.clear();
448    TableName notExistTable = TableName.valueOf("notExistTable"); // cause TableNotFoundException
449    for (int i = 0; i < BATCH_SIZE; i++) {
450      entries.add(createEntry(notExistTable, i, KeyValue.Type.Put, cells));
451    }
452    try {
453      SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
454        replicationClusterId, baseNamespaceDir, hfileArchiveDir);
455      Assert.fail("Should re-throw TableNotFoundException.");
456    } catch (TableNotFoundException e) {
457      errorCount++;
458      assertEquals(initialFailedBatches + errorCount, SINK.getSinkMetrics().getFailedBatches());
459    }
460
461    entries.clear();
462    cells.clear();
463    for (int i = 0; i < BATCH_SIZE; i++) {
464      entries.add(createEntry(TABLE_NAME1, i, KeyValue.Type.Put, cells));
465    }
466    // cause IOException in batch()
467    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
468      try (Admin admin = conn.getAdmin()) {
469        admin.disableTable(TABLE_NAME1);
470        try {
471          SINK.replicateEntries(entries, CellUtil.createCellScanner(cells.iterator()),
472            replicationClusterId, baseNamespaceDir, hfileArchiveDir);
473          Assert.fail("Should re-throw IOException.");
474        } catch (IOException e) {
475          errorCount++;
476          assertEquals(initialFailedBatches + errorCount, SINK.getSinkMetrics().getFailedBatches());
477        } finally {
478          admin.enableTable(TABLE_NAME1);
479        }
480      }
481    }
482  }
483
484  private WALEntry createEntry(TableName table, int row, KeyValue.Type type, List<Cell> cells) {
485    byte[] fam = table.equals(TABLE_NAME1) ? FAM_NAME1 : FAM_NAME2;
486    byte[] rowBytes = Bytes.toBytes(row);
487    // Just make sure we don't get the same ts for two consecutive rows with
488    // same key
489    try {
490      Thread.sleep(1);
491    } catch (InterruptedException e) {
492      LOG.info("Was interrupted while sleep, meh", e);
493    }
494    final long now = EnvironmentEdgeManager.currentTime();
495    KeyValue kv = null;
496    if (type.getCode() == KeyValue.Type.Put.getCode()) {
497      kv = new KeyValue(rowBytes, fam, fam, now, KeyValue.Type.Put, Bytes.toBytes(row));
498    } else if (type.getCode() == KeyValue.Type.DeleteColumn.getCode()) {
499      kv = new KeyValue(rowBytes, fam, fam, now, KeyValue.Type.DeleteColumn);
500    } else if (type.getCode() == KeyValue.Type.DeleteFamily.getCode()) {
501      kv = new KeyValue(rowBytes, fam, null, now, KeyValue.Type.DeleteFamily);
502    }
503    WALEntry.Builder builder = createWALEntryBuilder(table);
504    cells.add(kv);
505
506    return builder.build();
507  }
508
509  private WALEntry.Builder createWALEntryBuilder(TableName table) {
510    WALEntry.Builder builder = WALEntry.newBuilder();
511    builder.setAssociatedCellCount(1);
512    WALKey.Builder keyBuilder = WALKey.newBuilder();
513    UUID.Builder uuidBuilder = UUID.newBuilder();
514    uuidBuilder.setLeastSigBits(HConstants.DEFAULT_CLUSTER_ID.getLeastSignificantBits());
515    uuidBuilder.setMostSigBits(HConstants.DEFAULT_CLUSTER_ID.getMostSignificantBits());
516    keyBuilder.setClusterId(uuidBuilder.build());
517    keyBuilder.setTableName(UnsafeByteOperations.unsafeWrap(table.getName()));
518    keyBuilder.setWriteTime(EnvironmentEdgeManager.currentTime());
519    keyBuilder.setEncodedRegionName(UnsafeByteOperations.unsafeWrap(HConstants.EMPTY_BYTE_ARRAY));
520    keyBuilder.setLogSequenceNumber(-1);
521    builder.setKey(keyBuilder.build());
522    return builder;
523  }
524}