diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java index 54c97d7..3e9e6a1 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java @@ -45,7 +45,22 @@ public abstract class AbstractClientScanner implements ResultScanner { } } - // TODO: should this be at ResultScanner? ScanMetrics is not public API it seems. + /** + * Scan metrics for advanced users. Returns null unless scan metrics enabled for a Scan. To + * enable metrics, set the following Scan attribute post Scan construction but before you + * get {@link ResultScanner} from {@link Table}: e.g. + * scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.TRUE)); + * Casting your ResultScanner to an instance of this class should allow you call the below: + * + * Scan scan = new Scan(....); + * scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.TRUE)); + * try (ResultScanner scanner = table.getScanner(scan)) { + * ... + * ScanMetrics metrics = ((AbstractClientScanner)scanner).getScanMetrics(); + * } + * @return Returns the running {@link ScanMetrics} instance or null if scan metrics not enabled. + */ + // TODO: should this be at ResultScanner? public ScanMetrics getScanMetrics() { return scanMetrics; } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java index b9d652d..364783f 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java @@ -137,9 +137,22 @@ public class Put extends Mutation implements HeapSize, Comparable { * @param qualifier column qualifier * @param value column value * @return this + * @deprecated Since 1.0.0. Use {@link #addColumn(byte[], byte[], byte[])} */ + @Deprecated public Put add(byte [] family, byte [] qualifier, byte [] value) { - return add(family, qualifier, this.ts, value); + return addColumn(family, qualifier, value); + } + + /** + * Add the specified column and value to this Put operation. + * @param family family name + * @param qualifier column qualifier + * @param value column value + * @return this + */ + public Put addColumn(byte [] family, byte [] qualifier, byte [] value) { + return addColumn(family, qualifier, this.ts, value); } /** @@ -167,8 +180,23 @@ public class Put extends Mutation implements HeapSize, Comparable { * @param ts version timestamp * @param value column value * @return this + * @deprecated Since 1.0.0. Use {@link #addColumn(byte[], byte[], long, byte[])} */ + @Deprecated public Put add(byte [] family, byte [] qualifier, long ts, byte [] value) { + return addColumn(family, qualifier, ts, value); + } + + /** + * Add the specified column and value, with the specified timestamp as + * its version to this Put operation. + * @param family family name + * @param qualifier column qualifier + * @param ts version timestamp + * @param value column value + * @return this + */ + public Put addColumn(byte [] family, byte [] qualifier, long ts, byte [] value) { if (ts < 0) { throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts); } @@ -199,7 +227,6 @@ public class Put extends Mutation implements HeapSize, Comparable { * This expects that the underlying arrays won't change. It's intended * for usage internal HBase to and for advanced client applications. */ - @SuppressWarnings("unchecked") public Put addImmutable(byte[] family, byte[] qualifier, long ts, byte[] value, Tag[] tag) { List list = getCellList(family); KeyValue kv = createPutKeyValue(family, qualifier, ts, value, tag); @@ -233,8 +260,23 @@ public class Put extends Mutation implements HeapSize, Comparable { * @param ts version timestamp * @param value column value * @return this + * @deprecated Since 1.0.0. Use {@link Put#addColumn(byte[], ByteBuffer, long, ByteBuffer)} */ + @Deprecated public Put add(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) { + return addColumn(family, qualifier, ts, value); + } + + /** + * Add the specified column and value, with the specified timestamp as + * its version to this Put operation. + * @param family family name + * @param qualifier column qualifier + * @param ts version timestamp + * @param value column value + * @return this + */ + public Put addColumn(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) { if (ts < 0) { throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts); } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java index faef0d3..c418e47 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java @@ -361,6 +361,9 @@ public class Result implements CellScannable, CellScanner { /** * Get the latest version of the specified column. + * Note: this call clones the value content of the hosting Cell. See + * {@link #getValueAsByteBuffer(byte[], byte[])}, etc., or {@link #listCells()} if you would + * avoid the cloning. * @param family family name * @param qualifier column qualifier * @return value of latest version of column, null if none found @@ -388,7 +391,8 @@ public class Result implements CellScannable, CellScanner { if (kv == null) { return null; } - return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()); + return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()). + asReadOnlyBuffer(); } /** @@ -411,7 +415,8 @@ public class Result implements CellScannable, CellScanner { if (kv == null) { return null; } - return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()); + return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()). + asReadOnlyBuffer(); } /** diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/metrics/ScanMetrics.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/metrics/ScanMetrics.java index 86bc120..a848a24 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/metrics/ScanMetrics.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/metrics/ScanMetrics.java @@ -22,15 +22,14 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; import com.google.common.collect.ImmutableMap; /** - * Provides client-side metrics related to scan operations + * Provides client-side metrics related to scan operations. * The data can be passed to mapreduce framework or other systems. * We use atomic longs so that one thread can increment, * while another atomically resets to zero after the values are reported @@ -40,12 +39,9 @@ import com.google.common.collect.ImmutableMap; * However, there is no need for this. So they are defined under scan operation * for now. */ -@InterfaceAudience.Private +@InterfaceAudience.Public +@InterfaceStability.Evolving public class ScanMetrics { - - - private static final Log LOG = LogFactory.getLog(ScanMetrics.class); - /** * Hash to hold the String -> Atomic Long mappings. */ diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java index fefe626..7b68eee 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java @@ -61,6 +61,10 @@ public final class CellUtil { cell.getQualifierLength()); } + public static ByteRange fillValueRange(Cell cell, ByteRange range) { + return range.set(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); + } + public static ByteRange fillTagRange(Cell cell, ByteRange range) { return range.set(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java index a6c1cfe..b1a1777 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestScannersFromClientSide.java @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -32,6 +33,7 @@ import org.apache.hadoop.hbase.HTestConst; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.metrics.ScanMetrics; import org.apache.hadoop.hbase.filter.ColumnPrefixFilter; import org.apache.hadoop.hbase.filter.ColumnRangeFilter; import org.apache.hadoop.hbase.master.HMaster; @@ -96,6 +98,27 @@ public class TestScannersFromClientSide { } /** + * Test ScanMetrics are available + * @throws IOException + */ + @Test (timeout=30000) + public void testScanMetrics() throws IOException { + TableName TABLE = TableName.valueOf("testScanMetrics"); + try (Table ht = TEST_UTIL.createTable(TABLE, FAMILY)) { + Scan scan = new Scan(); + scan.setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.TRUE)); + try (ResultScanner scanner = ht.getScanner(scan)) { + while(scanner.next() != null) continue; + // Just make sure basically works. + ScanMetrics metrics = ((AbstractClientScanner)scanner).getScanMetrics(); + assertTrue(metrics != null); + assertEquals(1, metrics.countOfRegions); + + } + } + } + + /** * Test from client side for batch of scan * * @throws Exception