Index: hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestBlocksRead.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestBlocksRead.java (revision 1453730) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestBlocksRead.java (working copy) @@ -251,13 +251,13 @@ // The first 2 seeks is to find out col2. [HBASE-4443] // One additional seek for col3 // So 3 seeks are needed. - kvs = getData(FAMILY, "row", Arrays.asList("col2", "col3"), 3); + kvs = getData(FAMILY, "row", Arrays.asList("col2", "col3"), 2); assertEquals(2, kvs.length); verifyData(kvs[0], "row", "col2", 2); verifyData(kvs[1], "row", "col3", 3); - // Expected block reads: 2. [HBASE-4443] - kvs = getData(FAMILY, "row", Arrays.asList("col5"), 2); + // Expected block reads: 1. [HBASE-4443]&[HBASE-7845] + kvs = getData(FAMILY, "row", Arrays.asList("col5"), 1); assertEquals(1, kvs.length); verifyData(kvs[0], "row", "col5", 5); } finally { @@ -334,7 +334,7 @@ // For NONE Bloom filter: Expected blocks read: 3. kvs = getData(FAMILY, "row", "col1", 2, 3, 3); assertEquals(0, kvs.length); - kvs = getData(FAMILY, "row", "col2", 3, 4, 4); + kvs = getData(FAMILY, "row", "col2", 2, 3, 3); assertEquals(0, kvs.length); kvs = getData(FAMILY, "row", "col3", 2); assertEquals(0, kvs.length); Index: hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestHFileWriterV2.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestHFileWriterV2.java (revision 1453730) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestHFileWriterV2.java (working copy) @@ -102,19 +102,16 @@ .create(); Random rand = new Random(9713312); // Just a fixed seed. + List keyValues = new ArrayList(entryCount); - List keys = new ArrayList(); - List values = new ArrayList(); - for (int i = 0; i < entryCount; ++i) { byte[] keyBytes = randomOrderedKey(rand, i); // A random-length random value. byte[] valueBytes = randomValue(rand); - writer.append(keyBytes, valueBytes); - - keys.add(keyBytes); - values.add(valueBytes); + KeyValue keyValue = new KeyValue(keyBytes, null, null, valueBytes); + writer.append(keyValue); + keyValues.add(keyValue); } // Add in an arbitrary order. They will be sorted lexicographically by @@ -207,8 +204,8 @@ } // A brute-force check to see that all keys and values are correct. - assertTrue(Bytes.compareTo(key, keys.get(entriesRead)) == 0); - assertTrue(Bytes.compareTo(value, values.get(entriesRead)) == 0); + assertTrue(Bytes.compareTo(key, keyValues.get(entriesRead).getKey()) == 0); + assertTrue(Bytes.compareTo(value, keyValues.get(entriesRead).getValue()) == 0); ++entriesRead; } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (revision 1453730) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (working copy) @@ -226,6 +226,10 @@ throws IOException { int result = s.seekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength()); if(result < 0) { + if (result == -2) { + // using faked key + return true; + } // Passed KV is smaller than first KV in file, work from start of file return s.seekTo(); } else if(result > 0) { @@ -243,11 +247,10 @@ int result = s.reseekTo(k.getBuffer(), k.getKeyOffset(), k.getKeyLength()); if (result <= 0) { return true; - } else { - // passed KV is larger than current KV in file, if there is a next - // it is after, if not then this scanner is done. - return s.next(); } + // passed KV is larger than current KV in file, if there is a next + // it is after, if not then this scanner is done. + return s.next(); } @Override Index: hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileReaderV2.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileReaderV2.java (revision 1453730) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileReaderV2.java (working copy) @@ -454,7 +454,9 @@ * doing the seek. If this is false, we are assuming we never go * back, otherwise the result is undefined. * @return -1 if the key is earlier than the first key of the file, - * 0 if we are at the given key, and 1 if we are past the given key + * 0 if we are at the given key, 1 if we are past the given key + * -2 if the key is earlier than the first key of the file while + * using a faked index key * @throws IOException */ protected int seekTo(byte[] key, int offset, int length, boolean rewind) @@ -802,7 +804,9 @@ * @param key the key to find * @param seekBefore find the key before the given key in case of exact * match. - * @return 0 in case of an exact key match, 1 in case of an inexact match + * @return 0 in case of an exact key match, 1 in case of an inexact match, + * -2 in case of an inexact match and furthermore, the input key less + * than the first key of current block(e.g. using a faked index key) */ private int blockSeek(byte[] key, int offset, int length, boolean seekBefore) { @@ -852,12 +856,13 @@ currMemstoreTSLen = memstoreTSLen; } return 0; // indicate exact match - } - - if (comp < 0) { + } else if (comp < 0) { if (lastKeyValueSize > 0) blockBuffer.position(blockBuffer.position() - lastKeyValueSize); readKeyValueLen(); + if (lastKeyValueSize == -1) { + return -2; + } return 1; } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileWriterV2.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileWriterV2.java (revision 1453730) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileWriterV2.java (working copy) @@ -34,6 +34,7 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.KeyValue.KeyComparator; +import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; import org.apache.hadoop.hbase.io.compress.Compression; import org.apache.hadoop.hbase.io.hfile.HFile.Writer; import org.apache.hadoop.hbase.io.hfile.HFileBlock.BlockWritable; @@ -77,6 +78,9 @@ /** The offset of the last data block or 0 if the file is empty. */ private long lastDataBlockOffset; + /** The last(stop) Key of the previous data block. */ + private byte[] lastKeyOfPreviousBlock = null; + /** Additional data items to be written to the "load-on-open" section. */ private List additionalLoadOnOpenData = new ArrayList(); @@ -161,24 +165,38 @@ return; long startTimeNs = System.nanoTime(); - // Update the first data block offset for scanning. if (firstDataBlockOffset == -1) { firstDataBlockOffset = outputStream.getPos(); } - // Update the last data block offset lastDataBlockOffset = outputStream.getPos(); - fsBlockWriter.writeHeaderAndData(outputStream); - int onDiskSize = fsBlockWriter.getOnDiskSizeWithHeader(); - dataBlockIndexWriter.addEntry(firstKeyInBlock, lastDataBlockOffset, - onDiskSize); + // Generate a shorter faked key into index block. For example, consider a block boundary + // between the keys "the quick brown fox" and "the who test text". We can use "the r" as the + // key for the index block entry since it is > all entries in the previous block and <= all + // entries in subsequent blocks. + if (comparator instanceof KeyComparator && blockEncoder.getEncodingOnDisk() == + DataBlockEncoding.NONE && blockEncoder.getEncodingInCache() == DataBlockEncoding.NONE) { + byte[] shortestSeparator = ((KeyComparator) comparator).getFakedKey( + lastKeyOfPreviousBlock, firstKeyInBlock); + if (comparator.compare(shortestSeparator, firstKeyInBlock) > 0) { + throw new IOException("Unexpected getFakedKey result, shortestSeparator:" + + shortestSeparator + ", firstKeyInBlock:" + firstKeyInBlock); + } + if (lastKeyOfPreviousBlock != null && comparator.compare(lastKeyOfPreviousBlock, + shortestSeparator) >= 0) { + throw new IOException("Unexpected getFakedKey result, lastKeyOfPreviousBlock:" + + Bytes.toString(lastKeyOfPreviousBlock) + ", shortestSeparator:" + + Bytes.toString(shortestSeparator)); + } + dataBlockIndexWriter.addEntry(shortestSeparator, lastDataBlockOffset,onDiskSize); + } else { + dataBlockIndexWriter.addEntry(firstKeyInBlock, lastDataBlockOffset,onDiskSize); + } totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader(); - HFile.offerWriteLatency(System.nanoTime() - startTimeNs); - if (cacheConf.shouldCacheDataOnWrite()) { doCacheOnWrite(lastDataBlockOffset); } @@ -229,6 +247,10 @@ // This is where the next block begins. fsBlockWriter.startWriting(BlockType.DATA); firstKeyInBlock = null; + if (lastKeyLength > 0) { + lastKeyOfPreviousBlock = new byte[lastKeyLength]; + System.arraycopy(lastKeyBuffer, lastKeyOffset, lastKeyOfPreviousBlock, 0, lastKeyLength); + } } /** Index: hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlockIndex.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlockIndex.java (revision 1453730) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/HFileBlockIndex.java (working copy) @@ -316,9 +316,10 @@ if (rootCount == 0) throw new IOException("HFile empty"); - byte[] midKey = this.midKey.get(); - if (midKey != null) - return midKey; + byte[] targetMidKey = this.midKey.get(); + if (targetMidKey != null) { + return targetMidKey; + } if (midLeafBlockOffset >= 0) { if (cachingBlockReader == null) { @@ -339,14 +340,14 @@ int keyOffset = b.arrayOffset() + Bytes.SIZEOF_INT * (numDataBlocks + 2) + keyRelOffset + SECONDARY_INDEX_ENTRY_OVERHEAD; - midKey = Arrays.copyOfRange(b.array(), keyOffset, keyOffset + keyLen); + targetMidKey = Arrays.copyOfRange(b.array(), keyOffset, keyOffset + keyLen); } else { // The middle of the root-level index. - midKey = blockKeys[rootCount / 2]; + targetMidKey = blockKeys[rootCount / 2]; } - this.midKey.set(midKey); - return midKey; + this.midKey.set(targetMidKey); + return targetMidKey; } /** Index: hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyValue.java =================================================================== --- hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyValue.java (revision 1453730) +++ hbase-common/src/test/java/org/apache/hadoop/hbase/TestKeyValue.java (working copy) @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase; import java.io.IOException; +import java.util.Arrays; import java.util.Set; import java.util.TreeSet; @@ -27,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.KeyValue.KVComparator; +import org.apache.hadoop.hbase.KeyValue.KeyComparator; import org.apache.hadoop.hbase.KeyValue.MetaComparator; import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.util.Bytes; @@ -78,11 +80,11 @@ check(HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes(getName()), null, 1, null); } - private void check(final byte [] row, final byte [] family, byte [] qualifier, + private void check(final byte [] row, final byte [] inputFamily, byte [] qualifier, final long timestamp, final byte [] value) { - KeyValue kv = new KeyValue(row, family, qualifier, timestamp, value); + KeyValue kv = new KeyValue(row, inputFamily, qualifier, timestamp, value); assertTrue(Bytes.compareTo(kv.getRow(), row) == 0); - assertTrue(kv.matchingColumn(family, qualifier)); + assertTrue(kv.matchingColumn(inputFamily, qualifier)); // Call toString to make sure it works. LOG.info(kv.toString()); } @@ -199,23 +201,23 @@ public void testKeyValueBorderCases() throws IOException { // % sorts before , so if we don't do special comparator, rowB would // come before rowA. - KeyValue rowA = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/,1234"), + KeyValue row1 = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/,1234"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - KeyValue rowB = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/%20,99999"), + KeyValue row2 = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/%20,99999"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - assertTrue(KeyValue.META_COMPARATOR.compare(rowA, rowB) < 0); + assertTrue(KeyValue.META_COMPARATOR.compare(row1, row2) < 0); - rowA = new KeyValue(Bytes.toBytes("testtable,,1234"), Bytes.toBytes("fam"), + row1 = new KeyValue(Bytes.toBytes("testtable,,1234"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - rowB = new KeyValue(Bytes.toBytes("testtable,$www.hbase.org/,99999"), + row2 = new KeyValue(Bytes.toBytes("testtable,$www.hbase.org/,99999"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - assertTrue(KeyValue.META_COMPARATOR.compare(rowA, rowB) < 0); + assertTrue(KeyValue.META_COMPARATOR.compare(row1, row2) < 0); - rowA = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/,1234,4321"), + row1 = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/,1234,4321"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - rowB = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/%20,99999,99999"), + row2 = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/%20,99999,99999"), Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null); - assertTrue(KeyValue.ROOT_COMPARATOR.compare(rowA, rowB) < 0); + assertTrue(KeyValue.ROOT_COMPARATOR.compare(row1, row2) < 0); } private void metacomparisons(final KeyValue.MetaComparator c) { @@ -325,6 +327,7 @@ private final byte[] family = Bytes.toBytes("family"); private final byte[] qualA = Bytes.toBytes("qfA"); + private final byte[] qualB = Bytes.toBytes("qfB"); private void assertKVLess(KeyValue.KVComparator c, KeyValue less, @@ -493,4 +496,65 @@ assertEquals(HConstants.LATEST_TIMESTAMP, time1); assertEquals(12345L, time2); } + + /** + * See HBASE-7845 + */ + public void testGetFakedKey() { + final KeyComparator keyComparator = new KeyValue.KeyComparator(); + //verify that faked shorter rowkey could be generated + long ts = 5; + KeyValue kv1 = new KeyValue(Bytes.toBytes("the quick brown fox"), family, qualA, ts, Type.Put); + KeyValue kv2 = new KeyValue(Bytes.toBytes("the who test text"), family, qualA, ts, Type.Put); + byte[] newKey = keyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(keyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(keyComparator.compare(newKey, kv2.getKey()) < 0); + short newRowLength = Bytes.toShort(newKey, 0); + byte[] expectedArray = Bytes.toBytes("the r"); + Bytes.equals(newKey, KeyValue.ROW_LENGTH_SIZE, newRowLength, expectedArray, 0, + expectedArray.length); + //verify: same with "row + family + qualifier", return rightKey directly + kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 5, Type.Put); + kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 0, Type.Put); + assertTrue(keyComparator.compare(kv1.getKey(), kv2.getKey()) < 0); + newKey = keyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(keyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(keyComparator.compare(newKey, kv2.getKey()) == 0); + kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, -5, Type.Put); + kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, -10, Type.Put); + assertTrue(keyComparator.compare(kv1.getKey(), kv2.getKey()) < 0); + newKey = keyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(keyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(keyComparator.compare(newKey, kv2.getKey()) == 0); + // verify: same with row, different with qualifier + kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, 5, Type.Put); + kv2 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualB, 5, Type.Put); + assertTrue(keyComparator.compare(kv1.getKey(), kv2.getKey()) < 0); + newKey = keyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(keyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(keyComparator.compare(newKey, kv2.getKey()) < 0); + KeyValue newKeyValue = KeyValue.createKeyValueFromKey(newKey); + assertTrue(Arrays.equals(newKeyValue.getFamily(),family)); + assertTrue(Arrays.equals(newKeyValue.getQualifier(),qualB)); + assertTrue(newKeyValue.getTimestamp() == HConstants.LATEST_TIMESTAMP); + assertTrue(newKeyValue.getType() == Type.Maximum.getCode()); + //verify root/metaKeyComparator's getFakedKey output + final KeyComparator rootKeyComparator = new KeyValue.RootKeyComparator(); + final KeyComparator metaKeyComparator = new KeyValue.MetaKeyComparator(); + kv1 = new KeyValue(Bytes.toBytes("ilovehbase123"), family, qualA, 5, Type.Put); + kv2 = new KeyValue(Bytes.toBytes("ilovehbase234"), family, qualA, 0, Type.Put); + newKey = rootKeyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(rootKeyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(rootKeyComparator.compare(newKey, kv2.getKey()) == 0); + newKey = metaKeyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(metaKeyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(metaKeyComparator.compare(newKey, kv2.getKey()) == 0); + //verify common fix scenario + kv1 = new KeyValue(Bytes.toBytes("ilovehbase"), family, qualA, ts, Type.Put); + kv2 = new KeyValue(Bytes.toBytes("ilovehbaseandhdfs"), family, qualA, ts, Type.Put); + assertTrue(keyComparator.compare(kv1.getKey(), kv2.getKey()) < 0); + newKey = keyComparator.getFakedKey(kv1.getKey(), kv2.getKey()); + assertTrue(keyComparator.compare(kv1.getKey(), newKey) < 0); + assertTrue(keyComparator.compare(newKey, kv2.getKey()) == 0); + } } \ No newline at end of file Index: hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java =================================================================== --- hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java (revision 1453730) +++ hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java (working copy) @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; @@ -2491,6 +2492,10 @@ return result; } + public byte[] getFakedKey(final byte[] leftKey, final byte[] rightKey) { + return Arrays.copyOf(rightKey, rightKey.length); + } + protected int compareRowid(byte[] left, int loffset, int llength, byte[] right, int roffset, int rlength) { return Bytes.compareTo(left, loffset, llength, right, roffset, rlength); @@ -2686,6 +2691,85 @@ return Bytes.compareTo(left, loffset, llength, right, roffset, rlength); } + /** + * Generate a faked byte array if possible, see HBASE-7845 + * we need to ensure: leftKey < newKey <= rightKey + * @return newKey: the new generated faked key + */ + public byte[] getFakedKey(final byte[] leftKey, final byte[] rightKey) { + if (rightKey == null) { + throw new IllegalArgumentException("rightKey can not be null"); + } + if (leftKey == null) { + return Arrays.copyOf(rightKey, rightKey.length); + } + if (compare(leftKey, rightKey) >= 0) { + throw new IllegalArgumentException("Unexpected input, leftKey:" + Bytes.toString(leftKey) + + ", rightKey:" + Bytes.toString(rightKey)); + } + + short leftRowLength = Bytes.toShort(leftKey, 0); + short rightRowLength = Bytes.toShort(rightKey, 0); + int leftCommonLength = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + leftRowLength; + int rightCommonLength = ROW_LENGTH_SIZE + FAMILY_LENGTH_SIZE + rightRowLength; + int leftCommonLengthWithTSAndType = TIMESTAMP_TYPE_SIZE + leftCommonLength; + int rightCommonLengthWithTSAndType = TIMESTAMP_TYPE_SIZE + rightCommonLength; + int leftColumnLength = leftKey.length - leftCommonLengthWithTSAndType; + int rightColumnLength = rightKey.length - rightCommonLengthWithTSAndType; + // rows are equal + if (leftRowLength == rightRowLength && compareRows(leftKey, ROW_LENGTH_SIZE, leftRowLength, + rightKey, ROW_LENGTH_SIZE, rightRowLength) == 0) { + // Compare family & qualifier together. + int comparison = Bytes.compareTo(leftKey, leftCommonLength, leftColumnLength, rightKey, + rightCommonLength, rightColumnLength); + // same with "row + family + qualifier", return rightKey directly + if (comparison == 0) { + return Arrays.copyOf(rightKey, rightKey.length); + } + // "family + qualifier" are different, generate a faked key per rightKey + byte[] newKey = Arrays.copyOf(rightKey, rightKey.length); + Bytes.putLong(newKey, rightKey.length - TIMESTAMP_TYPE_SIZE, HConstants.LATEST_TIMESTAMP); + Bytes.putByte(newKey, rightKey.length - TYPE_SIZE, Type.Maximum.getCode()); + return newKey; + } + // rows are different + short minLength = leftRowLength < rightRowLength ? leftRowLength : rightRowLength; + short diffIdx = 0; + while (diffIdx < minLength + && leftKey[ROW_LENGTH_SIZE + diffIdx] == rightKey[ROW_LENGTH_SIZE + diffIdx]) { + diffIdx++; + } + if (diffIdx >= minLength) { + // leftKey's row is prefix of rightKey's. we can optimize it in future + return Arrays.copyOf(rightKey, rightKey.length); + } + int diffByte = leftKey[ROW_LENGTH_SIZE + diffIdx]; + if ((0xff & diffByte) < 0xff && (diffByte + 1) < + (rightKey[ROW_LENGTH_SIZE + diffIdx] & 0xff)) { + byte[] newRowKey = new byte[diffIdx + 1]; + System.arraycopy(leftKey, ROW_LENGTH_SIZE, newRowKey, 0, diffIdx); + newRowKey[diffIdx] = (byte) (diffByte + 1); + int rightFamilyLength = rightKey[rightCommonLength - 1]; + byte[] family = null; + if (rightFamilyLength > 0) { + family = new byte[rightFamilyLength]; + System.arraycopy(rightKey, rightCommonLength, family, 0, rightFamilyLength); + } + //return KeyValue.createFirstOnRow(newRowKey, family, null).getKey(); + int rightQualifierLength = rightColumnLength - rightFamilyLength; + byte[] qualifier = null; + if (rightQualifierLength > 0) { + qualifier = new byte[rightQualifierLength]; + System.arraycopy(rightKey, rightCommonLength + rightFamilyLength, qualifier, 0, + rightQualifierLength); + } + return new KeyValue(newRowKey, null, null, HConstants.LATEST_TIMESTAMP, + Type.Maximum).getKey(); + } + // the following is optimizable in future + return Arrays.copyOf(rightKey, rightKey.length); + } + protected int compareColumns( byte [] left, int loffset, int llength, final int lfamilylength, byte [] right, int roffset, int rlength, final int rfamilylength) {