Repository: GoogleCloudPlatform/cloud-bigtable-client
Pull request #1880 (2018-07-13 17:32:08)

Making a distinction between RPC and Operation deadlines
Users set deadlines on logical operations.  Logical operations may consist of multiple RPCs, depending on the number of times temporary failures occur.  Each RPC has an additional concept of a deadline as a "safety net" if a user doesn't explicitly set a Deadline; this prevents hanging behavior. This PR distinguishes between the two in `AbstractRetryingOperation`.  This is a step towards fixing issue #1858. 


Patch:
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
index 345d67e9b..54461d805 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSession.java
@@ -39,6 +39,7 @@ import com.google.cloud.bigtable.grpc.io.Watchdog;
 import com.google.cloud.bigtable.grpc.io.WatchdogInterceptor;
 import com.google.cloud.bigtable.metrics.BigtableClientMetrics;
 import com.google.cloud.bigtable.metrics.BigtableClientMetrics.MetricLevel;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
@@ -48,7 +49,6 @@ import io.grpc.ClientInterceptors;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.MethodDescriptor;
-import io.grpc.internal.GrpcUtil;
 import java.io.Closeable;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -103,7 +103,7 @@ public class BigtableSession implements Closeable {
   private static void performWarmup() {
     // Initialize some core dependencies in parallel.  This can speed up startup by 150+ ms.
     ExecutorService connectionStartupExecutor = Executors
-        .newCachedThreadPool(GrpcUtil.getThreadFactory("BigtableSession-startup-%d", true));
+        .newCachedThreadPool(ThreadUtil.getThreadFactory("BigtableSession-startup-%d", true));
 
     connectionStartupExecutor.execute(new Runnable() {
       @Override
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
index f1afeada1..b7dd57a8a 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/BigtableSessionSharedThreadPools.java
@@ -15,12 +15,12 @@
  */
 package com.google.cloud.bigtable.grpc;
 
+import com.google.cloud.bigtable.util.ThreadUtil;
+
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
-import io.grpc.internal.GrpcUtil;
-
 /**
  * This class contains executors and other thread pool related resources that can be reused across a
  * few {@link com.google.cloud.bigtable.grpc.BigtableSession}s.  All executors are automatically expand if there is higher use.
@@ -66,7 +66,7 @@ public class BigtableSessionSharedThreadPools {
   public synchronized ExecutorService getBatchThreadPool() {
     if (batchThreadPool == null) {
       batchThreadPool = Executors
-          .newCachedThreadPool(GrpcUtil.getThreadFactory(BATCH_POOL_THREAD_NAME_PATTERN, true));
+          .newCachedThreadPool(ThreadUtil.getThreadFactory(BATCH_POOL_THREAD_NAME_PATTERN, true));
     }
     return batchThreadPool;
   }
@@ -79,7 +79,7 @@ public class BigtableSessionSharedThreadPools {
   public synchronized ScheduledExecutorService getRetryExecutor() {
     if (retryExecutor == null) {
       retryExecutor = Executors.newScheduledThreadPool(RETRY_THREAD_COUNT,
-        GrpcUtil.getThreadFactory(RETRY_THREADPOOL_NAME_PATTERN, true));
+          ThreadUtil.getThreadFactory(RETRY_THREADPOOL_NAME_PATTERN, true));
     }
     return retryExecutor;
   }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
index 40b5c23c6..9315f1171 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/AbstractRetryingOperation.java
@@ -317,7 +317,7 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
         // There's a subtle race condition in RetryingStreamOperation which requires a separate
         // newCall/start split. The call variable needs to be set before onMessage() happens; that
         // usually will occur, but some unit tests broke with a merged newCall and start.
-        call = rpc.newCall(getCallOptions());
+        call = rpc.newCall(getRpcCallOptions());
         rpc.start(getRetryRequest(), this, metadata, call);
       }
     } catch (Exception e) {
@@ -329,16 +329,47 @@ public abstract class AbstractRetryingOperation<RequestT, ResponseT, ResultT>
     return call;
   }
 
-  protected CallOptions getCallOptions() {
-    if (callOptions.getDeadline() != null) {
-      return callOptions;
-    }
-    if (request instanceof ReadRowsRequest
-        && !CallOptionsFactory.ConfiguredCallOptionsFactory.isGet((ReadRowsRequest) request)) {
-      // This is a streaming read.
-      return callOptions;
+  /**
+   * Returns the {@link CallOptions} that a user set for the entire Operation, which can span multiple RPCs/retries.
+   * @return The {@link CallOptions}
+   */
+  protected CallOptions getOperationCallOptions() {
+    return callOptions;
+  }
+
+  /**
+   * Create an {@link CallOption} that has a fail safe RPC deadline to make sure that unary operations don't hang. This
+   * will have to be overridden for streaming RPCs like read rows.
+   * <p>
+   * The logic is as follows:
+   * <ol>
+   *   <li> If the user provides a deadline, use the deadline</li>
+   *   <li> Else If this is a streaming read, don't set an explicit deadline.  The
+   *   {@link com.google.cloud.bigtable.grpc.io.Watchdog} will handle hangling</li>
+   *   <li> Else Set a deadline of {@link #UNARY_DEADLINE_MINUTES} minutes deadline.</li>
+   * </ol>
+   *
+   * @ee the
+   * {@link com.google.cloud.bigtable.grpc.io.Watchdog} handles hanging for streaming reads
+   *
+   * @return a {@link CallOptions}
+   */
+  protected CallOptions getRpcCallOptions() {
+    if (callOptions.getDeadline() != null || isStreamingRead()) {
+      // If the user set a deadline, honor it.
+      // If this is a streaming read, then the Watchdog will take affect and ensure that hanging does not occur.
+      return getOperationCallOptions();
+    } else {
+      // Unary calls should fail after 6 minutes, if there isn't any response from the server.
+      return callOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
     }
-    return callOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
+  }
+
+  // TODO(sduskis): This is only required because BigtableDataGrpcClient doesn't always use
+  //      RetryingReadRowsOperation like it should.
+  protected boolean isStreamingRead() {
+    return request instanceof ReadRowsRequest &&
+            !CallOptionsFactory.ConfiguredCallOptionsFactory.isGet((ReadRowsRequest) request);
   }
 
   protected RequestT getRetryRequest() {
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
index cfb3813b5..4464d0602 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/async/RetryingUnaryOperation.java
@@ -70,13 +70,4 @@ public class RetryingUnaryOperation<RequestT, ResponseT>
     }
     return true;
   }
-
-  @Override
-  protected CallOptions getCallOptions() {
-    CallOptions originalCallOptions = super.getCallOptions();
-    if (originalCallOptions.getDeadline() != null) {
-      return originalCallOptions;
-    }
-    return originalCallOptions.withDeadlineAfter(UNARY_DEADLINE_MINUTES, TimeUnit.MINUTES);
-  }
 }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
index f2c33aeb5..4935cb0f3 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/io/CredentialInterceptorCache.java
@@ -21,10 +21,10 @@ import com.google.cloud.bigtable.config.CredentialFactory;
 import com.google.cloud.bigtable.config.CredentialOptions;
 import com.google.cloud.bigtable.config.CredentialOptions.CredentialType;
 import com.google.cloud.bigtable.config.RetryOptions;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import com.google.common.base.Preconditions;
 
 import io.grpc.ClientInterceptor;
-import io.grpc.internal.GrpcUtil;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
@@ -51,7 +51,7 @@ public class CredentialInterceptorCache {
   }
 
   private final ExecutorService executor =
-      Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("Credentials-Refresh-%d", true));
+      Executors.newCachedThreadPool(ThreadUtil.getThreadFactory("Credentials-Refresh-%d", true));
 
   private ClientInterceptor defaultCredentialInterceptor;
 
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
index 28a11d4e7..8e1ed239c 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/grpc/scanner/RowMerger.java
@@ -15,7 +15,6 @@
  */
 package com.google.cloud.bigtable.grpc.scanner;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -29,7 +28,6 @@ import com.google.bigtable.v2.ReadRowsResponse;
 import com.google.bigtable.v2.ReadRowsResponse.CellChunk;
 import com.google.bigtable.v2.ReadRowsResponse.CellChunk.RowStatusCase;
 import com.google.bigtable.v2.RowFilter.Interleave;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.protobuf.ByteString;
@@ -242,7 +240,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     // cell in progress info
     private CellIdentifier currentId;
-    private ByteArrayOutputStream outputStream;
+    private ByteString.Output outputStream;
 
 
     private final Map<String, List<FlatRow.Cell>> cells = new TreeMap<>();
@@ -257,7 +255,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     private final void completeMultiChunkCell() {
       Preconditions.checkArgument(hasChunkInProgess());
-      addCell(ByteStringer.wrap(outputStream.toByteArray()));
+      addCell(outputStream.toByteString());
       outputStream = null;
     }
 
@@ -328,7 +326,7 @@ public class RowMerger implements StreamObserver<ReadRowsResponse> {
 
     private void addPartialCellChunk(ReadRowsResponse.CellChunk chunk) throws IOException {
       if (outputStream == null) {
-        outputStream = new ByteArrayOutputStream(chunk.getValueSize());
+        outputStream = ByteString.newOutput();
       }
       chunk.getValue().writeTo(outputStream);
     }
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java
new file mode 100644
index 000000000..3ab7d7845
--- /dev/null
+++ b/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ThreadUtil.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google LLC. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.util;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Creates a {@link ThreadFactory} that's safe to use in AppEngine.
+ * <p>
+ * This class copies code that originates in {@link io.grpc.internal.GrpcUtil#getThreadFactory(String, boolean)}.
+ */
+public class ThreadUtil {
+
+  // AppEngine runtimes have constraints on threading and socket handling
+  // that need to be accommodated.
+  public static final boolean IS_RESTRICTED_APPENGINE =
+          System.getProperty("com.google.appengine.runtime.environment") != null
+                  && "1.7".equals(System.getProperty("java.specification.version"));
+
+  /**
+   * Get a {@link ThreadFactory} suitable for use in the current environment.
+   * @param nameFormat to apply to threads created by the factory.
+   * @param daemon {@code true} if the threads the factory creates are daemon threads, {@code false}
+   *     otherwise.
+   * @return a {@link ThreadFactory}.
+   */
+  public static ThreadFactory getThreadFactory(String nameFormat, boolean daemon) {
+    if (IS_RESTRICTED_APPENGINE) {
+      return MoreExecutors.platformThreadFactory();
+    } else {
+      return new ThreadFactoryBuilder()
+          .setDaemon(daemon)
+          .setNameFormat(nameFormat)
+          .build();
+    }
+  }
+
+}
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
index b53f7b5d1..a449c75c2 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/BigtableExtendedScan.java
@@ -17,7 +17,7 @@ package com.google.cloud.bigtable.hbase;
 
 import com.google.bigtable.v2.RowRange;
 import com.google.bigtable.v2.RowSet;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyUtil;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.Table;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
index fefe1e86b..3e709a934 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/MutationAdapter.java
@@ -22,7 +22,7 @@ import org.apache.hadoop.hbase.client.Row;
 
 import com.google.bigtable.v2.MutateRowRequest;
 import com.google.bigtable.v2.MutateRowsRequest;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
index 9279cdc44..1deac86d8 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/FamilyFilterAdapter.java
@@ -20,6 +20,7 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.filter.BinaryComparator;
 import org.apache.hadoop.hbase.filter.ByteArrayComparable;
 import org.apache.hadoop.hbase.filter.CompareFilter;
@@ -53,9 +54,9 @@ public class FamilyFilterAdapter extends TypedFilterAdapterBase<FamilyFilter> {
     } else if (comparator instanceof RegexStringComparator) {
       family = Bytes.toString(comparator.getValue());
     } else if (comparator instanceof BinaryComparator) {
-      byte[] quotedRegularExpression =
+      ByteString quotedRegularExpression =
           ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-      family = Bytes.toString(quotedRegularExpression);
+      family = quotedRegularExpression.toStringUtf8();
     } else {
       throw new IllegalStateException(
           "Cannot adapt comparator " + comparator.getClass().getCanonicalName());
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
index f5ca768db..7bb35e4b0 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/MultipleColumnPrefixFilterAdapter.java
@@ -20,11 +20,10 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.InterleaveFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 /**
@@ -43,10 +42,10 @@ public class MultipleColumnPrefixFilterAdapter
       FilterAdapterContext context,
       MultipleColumnPrefixFilter filter) throws IOException {
     InterleaveFilter interleave = FILTERS.interleave();
-    ByteArrayOutputStream outputStream = null;
+    ByteString.Output outputStream = null;
     for (byte[] prefix : filter.getPrefix()) {
       if (outputStream == null) {
-        outputStream = new ByteArrayOutputStream(prefix.length * 2);
+        outputStream = ByteString.newOutput(prefix.length * 2);
       } else {
         outputStream.reset();
       }
@@ -54,7 +53,7 @@ public class MultipleColumnPrefixFilterAdapter
       ReaderExpressionHelper.writeQuotedExpression(outputStream, prefix);
       outputStream.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
 
-      interleave.filter(FILTERS.qualifier().regex(ByteStringer.wrap(outputStream.toByteArray())));
+      interleave.filter(FILTERS.qualifier().regex(outputStream.toByteString()));
     }
     return interleave.toProto();
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
index 45aa00f2d..4e4dae228 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/PrefixFilterAdapter.java
@@ -19,14 +19,12 @@ import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyUtil;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.collect.ImmutableRangeSet;
 import com.google.common.collect.Range;
 import com.google.common.collect.RangeSet;
 import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import org.apache.hadoop.hbase.filter.PrefixFilter;
 
@@ -44,11 +42,11 @@ public class PrefixFilterAdapter extends TypedFilterAdapterBase<PrefixFilter> {
   @Override
   public RowFilter adapt(FilterAdapterContext context, PrefixFilter filter)
       throws IOException {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream(filter.getPrefix().length * 2);
-    ReaderExpressionHelper.writeQuotedRegularExpression(baos, filter.getPrefix());
+    ByteString.Output output = ByteString.newOutput(filter.getPrefix().length * 2);
+    ReaderExpressionHelper.writeQuotedRegularExpression(output, filter.getPrefix());
     // Unquoted all bytes:
-    baos.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
-    return FILTERS.key().regex(ByteStringer.wrap(baos.toByteArray())).toProto();
+    output.write(ReaderExpressionHelper.ALL_QUALIFIERS_BYTES);
+    return FILTERS.key().regex(output.toByteString()).toProto();
   }
 
   /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
index 57806f0b6..91483ca8c 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/QualifierFilterAdapter.java
@@ -21,7 +21,6 @@ import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.Filter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.QualifierRangeFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 import org.apache.hadoop.hbase.filter.BinaryComparator;
@@ -71,8 +70,7 @@ public class QualifierFilterAdapter extends TypedFilterAdapterBase<QualifierFilt
   private Filter adaptBinaryComparator(
       FilterAdapterContext context, CompareOp compareOp, BinaryComparator comparator)
       throws IOException {
-    byte[] quoted = ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-    ByteString quotedValue = ByteStringer.wrap(quoted);
+    ByteString quotedValue = ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
     switch (compareOp) {
       case LESS:
         return range(context).endOpen(quotedValue);
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
index 5671893fb..75f7cd490 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/RowFilterAdapter.java
@@ -27,7 +27,6 @@ import org.apache.hadoop.hbase.filter.RegexStringComparator;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.hbase.adapters.read.ReaderExpressionHelper;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 /**
@@ -61,9 +60,8 @@ public class RowFilterAdapter
     } else if (comparator instanceof RegexStringComparator) {
       regexValue = ByteString.copyFrom(comparator.getValue());
     } else if (comparator instanceof BinaryComparator) {
-      byte[] quotedRegularExpression =
+      regexValue =
           ReaderExpressionHelper.quoteRegularExpression(comparator.getValue());
-      regexValue = ByteStringer.wrap(quotedRegularExpression);
     } else {
       throw new IllegalStateException(String.format("Cannot adapt comparator %s", comparator
           .getClass().getCanonicalName()));
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
index 77d64a3fe..54b69e293 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/filters/SingleColumnValueFilterAdapter.java
@@ -23,12 +23,10 @@ import java.io.IOException;
 
 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
 import org.apache.hadoop.hbase.filter.ValueFilter;
-import org.apache.hadoop.hbase.util.Bytes;
 
 import com.google.bigtable.v2.RowFilter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.Filter;
 import com.google.cloud.bigtable.data.v2.wrappers.Filters.ChainFilter;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.protobuf.ByteString;
 
@@ -180,8 +178,8 @@ public class SingleColumnValueFilterAdapter
   @VisibleForTesting
   static ChainFilter getColumnSpecFilter(byte[] family, byte[] qualifier, boolean latestVersionOnly)
       throws IOException {
-    ByteString wrappedQual = ByteStringer.wrap(quoteRegularExpression(qualifier));
-    String wrappedFamily = Bytes.toString(quoteRegularExpression(family));
+    ByteString wrappedQual = quoteRegularExpression(qualifier);
+    String wrappedFamily = quoteRegularExpression(family).toStringUtf8();
     ChainFilter builder = FILTERS.chain()
         .filter(FILTERS.family().regex(wrappedFamily))
         .filter(FILTERS.qualifier().regex(wrappedQual));
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
index 8d48cd014..a06aa036b 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/FlatRowAdapter.java
@@ -19,7 +19,7 @@ import com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.cloud.bigtable.hbase.BigtableConstants;
 import com.google.cloud.bigtable.hbase.adapters.ResponseAdapter;
 import com.google.cloud.bigtable.hbase.util.TimestampConverter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.common.base.Objects;
 
 import java.util.ArrayList;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
index b14ce6458..94344c1bd 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ReaderExpressionHelper.java
@@ -15,6 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase.adapters.read;
 
+import com.google.protobuf.ByteString;
 import org.apache.hadoop.hbase.util.Bytes;
 
 import java.io.ByteArrayOutputStream;
@@ -37,42 +38,10 @@ public class ReaderExpressionHelper {
   public static final byte[] ANY_BYTE_BYTES = Bytes.toBytes(ANY_BYTE);
   /** Constant <code>ANY_BYTE_BYTES=Bytes.toBytes(ANY_BYTE)</code> */
   public static final byte[] ALL_BYTE_BYTES = Bytes.toBytes(ALL_BYTES);
-  /** Constant <code>ALL_QUALIFIERS="\\C*"</code> */
-  public static final String ALL_QUALIFIERS = ALL_BYTES;
   /** Constant <code>ALL_QUALIFIERS_BYTES=Bytes.toBytes(ALL_QUALIFIERS)</code> */
   public static final byte[] ALL_QUALIFIERS_BYTES = ALL_BYTE_BYTES;
-  /** Constant <code>ALL_FAMILIES=".*"</code> */
-  public static final String ALL_FAMILIES = ".*";
-  /** Constant <code>ALL_FAMILIES_BYTES=Bytes.toBytes(ALL_FAMILIES)</code> */
-  public static final byte[] ALL_FAMILIES_BYTES = Bytes.toBytes(ALL_FAMILIES);
-  /** Constant <code>ALL_VERSIONS="all"</code> */
-  public static final String ALL_VERSIONS = "all";
-  /** Constant <code>ALL_VERSIONS_BYTES=Bytes.toBytes(ALL_VERSIONS)</code> */
-  public static final byte[] ALL_VERSIONS_BYTES = Bytes.toBytes(ALL_VERSIONS);
-  /** Constant <code>LATEST_VERSION="latest"</code> */
-  public static final String LATEST_VERSION = "latest";
-  /** Constant <code>INTERLEAVE_CHARACTERS=Bytes.toBytes(" + ")</code> */
-  public static final byte[] INTERLEAVE_CHARACTERS = Bytes.toBytes(" + ");
-  /** Constant <code>PIPE_CHARACTER_BYTES=Bytes.toBytes(" | ")</code> */
-  public static final byte[] PIPE_CHARACTER_BYTES = Bytes.toBytes(" | ");
   private final static byte[] NULL_CHARACTER_BYTES = Bytes.toBytes("\\x00");
 
-  /**
-   * Write unquoted to the OutputStream applying just Bigtable reader
-   * expression quoting. Used when the supplied value is NOT a regular expression.
-   *
-   * @param unquoted A byte-array, possibly containing bytes outside of the ASCII
-   * @param outputStream A stream to write quoted output to
-   * @throws java.io.IOException if any.
-   */
-  public static void writeFilterQuotedExpression(byte[] unquoted, OutputStream outputStream)
-      throws IOException{
-    QuoteFilterExpressionStream quoteFilterExpressionStream =
-        new QuoteFilterExpressionStream(outputStream);
-    quoteFilterExpressionStream.write(unquoted);
-    quoteFilterExpressionStream.close();
-  }
-
   /**
    * Write unquoted to the OutputStream applying both RE2:QuoteMeta and Bigtable reader
    * expression quoting.
@@ -93,10 +62,10 @@ public class ReaderExpressionHelper {
    * @return an array of byte.
    * @throws java.io.IOException if any.
    */
-  public static byte[] quoteRegularExpression(byte[] unquoted) throws IOException {
-    ByteArrayOutputStream baos = new ByteArrayOutputStream(unquoted.length * 2);
-    writeQuotedRegularExpression(baos, unquoted);
-    return baos.toByteArray();
+  public static ByteString quoteRegularExpression(byte[] unquoted) throws IOException {
+    ByteString.Output output = ByteString.newOutput(unquoted.length * 2);
+    writeQuotedRegularExpression(output, unquoted);
+    return output.toByteString();
   }
 
   /**
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
index 99a640347..c611dadec 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/RowAdapter.java
@@ -21,7 +21,7 @@ import com.google.bigtable.v2.Family;
 import com.google.bigtable.v2.Row;
 import com.google.cloud.bigtable.hbase.adapters.ResponseAdapter;
 import com.google.cloud.bigtable.hbase.util.TimestampConverter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import org.apache.hadoop.hbase.KeyValue;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
index 72f11824f..94d483643 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/adapters/read/ScanAdapter.java
@@ -17,7 +17,6 @@ package com.google.cloud.bigtable.hbase.adapters.read;
 
 import static com.google.cloud.bigtable.data.v2.wrappers.Filters.FILTERS;
 
-import com.google.bigtable.v2.BigtableGrpc;
 import com.google.common.collect.Range;
 import com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.v2.RowFilter;
@@ -31,7 +30,6 @@ import com.google.cloud.bigtable.hbase.BigtableConstants;
 import com.google.cloud.bigtable.hbase.BigtableExtendedScan;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapter;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapterContext;
-import com.google.cloud.bigtable.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.base.Optional;
 import com.google.common.collect.RangeSet;
@@ -175,7 +173,7 @@ public class ScanAdapter implements ReadOperationAdapter<Scan> {
     }
   }
 
-  private static byte[] quoteRegex(byte[] unquoted)  {
+  private static ByteString quoteRegex(byte[] unquoted)  {
     try {
       return ReaderExpressionHelper.quoteRegularExpression(unquoted);
     } catch (IOException e) {
@@ -211,7 +209,7 @@ public class ScanAdapter implements ReadOperationAdapter<Scan> {
   }
 
   private Filters.Filter createColumnQualifierFilter(byte[] unquotedQualifier) {
-    return FILTERS.qualifier().regex(ByteStringer.wrap(quoteRegex(unquotedQualifier)));
+    return FILTERS.qualifier().regex(quoteRegex(unquotedQualifier));
   }
 
   private Filters.Filter createFamilyFilter(byte[] familyName) {
diff --git a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
index 6cdf75fc3..651c0c54f 100755
--- a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableIO.java
@@ -73,12 +73,12 @@ import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableSes
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.async.ResourceLimiterStats;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.scanner.ResultScanner;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ZeroCopyByteStringUtil;
 import com.google.bigtable.repackaged.com.google.common.annotations.VisibleForTesting;
 import com.google.bigtable.repackaged.com.google.common.base.Preconditions;
 import com.google.cloud.bigtable.batch.common.CloudBigtableServiceImpl;
 import com.google.cloud.bigtable.hbase.BigtableOptionsFactory;
 import com.google.cloud.bigtable.hbase.adapters.read.FlatRowAdapter;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 
 /**
  * <p>
@@ -630,7 +630,7 @@ public class CloudBigtableIO {
     public boolean advance() throws IOException {
       FlatRow row = scanner.next();
       if (row != null && rangeTracker.tryReturnRecordAt(true,
-        ByteKey.copyFrom(ZeroCopyByteStringUtil.get(row.getRowKey())))) {
+        ByteKey.copyFrom(ByteStringer.extract(row.getRowKey())))) {
         current = FLAT_ROW_ADAPTER.adaptResponse(row);
         rowsRead.addAndGet(1l);
         return true;
diff --git a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
index 35b557e7d..1b6295e56 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-beam/src/main/java/com/google/cloud/bigtable/beam/CloudBigtableScanConfiguration.java
@@ -19,6 +19,7 @@ import java.io.Serializable;
 import java.util.Map;
 import java.util.Objects;
 
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import org.apache.beam.sdk.io.range.ByteKey;
 import org.apache.beam.sdk.io.range.ByteKeyRange;
 import org.apache.beam.sdk.options.ValueProvider;
@@ -29,7 +30,6 @@ import com.google.bigtable.repackaged.com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowRange;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowSet;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableInstanceName;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;
 import com.google.cloud.bigtable.hbase.adapters.read.DefaultReadHooks;
diff --git a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
index 3bf3d71c2..5f12019d8 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/dataflow/CloudBigtableScanConfiguration.java
@@ -18,13 +18,13 @@ package com.google.cloud.bigtable.dataflow;
 import java.util.Map;
 import java.util.Objects;
 
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import org.apache.hadoop.hbase.client.Scan;
 
 import com.google.bigtable.repackaged.com.google.bigtable.v2.ReadRowsRequest;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowRange;
 import com.google.bigtable.repackaged.com.google.bigtable.v2.RowSet;
 import com.google.bigtable.repackaged.com.google.cloud.bigtable.grpc.BigtableInstanceName;
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
 import com.google.cloud.bigtable.batch.common.ByteStringUtil;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;


Test patch:
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
similarity index 98%
rename from bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java
rename to bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
index 8a8063427..10804f5a6 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ByteStringer.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ByteStringer.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.google.cloud.bigtable.util;
+package com.google.cloud.bigtable.hbase.util;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
diff --git a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
similarity index 98%
rename from bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java
rename to bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
index 3ae296fc5..70b95276c 100644
--- a/bigtable-client-core-parent/bigtable-client-core/src/main/java/com/google/cloud/bigtable/util/ZeroCopyByteStringUtil.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/main/java/com/google/cloud/bigtable/hbase/util/ZeroCopyByteStringUtil.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.google.cloud.bigtable.util;
+package com.google.cloud.bigtable.hbase.util;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
index f54bb56ae..2de382a36 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/TestBatchExecutor.java
@@ -38,7 +38,7 @@ import com.google.cloud.bigtable.grpc.scanner.FlatRow;
 import com.google.cloud.bigtable.grpc.scanner.FlatRow.Cell;
 import com.google.cloud.bigtable.hbase.adapters.Adapters;
 import com.google.cloud.bigtable.hbase.adapters.HBaseRequestAdapter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
index 6a1e73432..601842eb5 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestFamilyFilterAdapter.java
@@ -69,8 +69,7 @@ public class TestFamilyFilterAdapter {
             CompareFilter.CompareOp.EQUAL, comparator);
     Assert.assertEquals(
         RowFilter.newBuilder()
-            .setRowKeyRegexFilter(
-                ByteString.copyFrom(ReaderExpressionHelper.quoteRegularExpression(bytes)))
+            .setRowKeyRegexFilter(ReaderExpressionHelper.quoteRegularExpression(bytes))
             .build(),
         adapter.adapt(context, filter));
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
index 74a3a5ba0..5518719d9 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/filters/TestRowFilterAdapter.java
@@ -69,8 +69,7 @@ public class TestRowFilterAdapter {
             CompareFilter.CompareOp.EQUAL, comparator);
     Assert.assertEquals(
         RowFilter.newBuilder()
-            .setRowKeyRegexFilter(
-                ByteString.copyFrom(ReaderExpressionHelper.quoteRegularExpression(bytes)))
+            .setRowKeyRegexFilter(ReaderExpressionHelper.quoteRegularExpression(bytes))
             .build(),
         adapter.adapt(context, filter));
   }
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
index 30c4b3306..e56d31767 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestRowAdapter.java
@@ -23,8 +23,7 @@ import com.google.bigtable.v2.Cell;
 import com.google.bigtable.v2.Column;
 import com.google.bigtable.v2.Family;
 import com.google.bigtable.v2.Row;
-import com.google.cloud.bigtable.hbase.adapters.read.RowAdapter;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.protobuf.ByteString;
 
 import org.apache.hadoop.hbase.client.Result;
diff --git a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
index 12b7db77d..37adfe3ca 100644
--- a/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
+++ b/bigtable-client-core-parent/bigtable-hbase/src/test/java/com/google/cloud/bigtable/hbase/adapters/read/TestScanAdapter.java
@@ -27,7 +27,7 @@ import com.google.bigtable.v2.RowSet;
 import com.google.cloud.bigtable.hbase.BigtableExtendedScan;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapter;
 import com.google.cloud.bigtable.hbase.adapters.filters.FilterAdapterContext;
-import com.google.cloud.bigtable.util.ByteStringer;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.bigtable.util.RowKeyWrapper;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
diff --git a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
index c79619c44..1ddbc2319 100644
--- a/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
+++ b/bigtable-dataflow-parent/bigtable-hbase-dataflow/src/main/java/com/google/cloud/bigtable/batch/common/ByteStringUtil.java
@@ -15,8 +15,8 @@
  */
 package com.google.cloud.bigtable.batch.common;
 
-import com.google.bigtable.repackaged.com.google.cloud.bigtable.util.ByteStringer;
 import com.google.bigtable.repackaged.com.google.protobuf.ByteString;
+import com.google.cloud.bigtable.hbase.util.ByteStringer;
 import com.google.cloud.dataflow.sdk.io.range.ByteKey;
 
 /**
diff --git a/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java b/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
index 34d16b029..5cd650218 100644
--- a/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
+++ b/bigtable-hbase-1.x-parent/bigtable-hbase-1.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
@@ -15,7 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase;
 
-import io.grpc.internal.GrpcUtil;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HTableDescriptor;
@@ -101,7 +101,7 @@ public class ManyThreadDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = BigtableConfiguration.connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -144,7 +144,7 @@ public class ManyThreadDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {
diff --git a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
index 8446ab4fb..3da057ca2 100644
--- a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
+++ b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/ManyThreadDriver.java
@@ -15,7 +15,7 @@
  */
 package com.google.cloud.bigtable.hbase;
 
-import io.grpc.internal.GrpcUtil;
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Admin;
@@ -101,7 +101,7 @@ public class ManyThreadDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = BigtableConfiguration.connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -144,7 +144,7 @@ public class ManyThreadDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {
diff --git a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
index 9bf893668..af0356c31 100644
--- a/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
+++ b/bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/test/java/com/google/cloud/bigtable/hbase/RecreateConnectionDriver.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import com.google.cloud.bigtable.util.ThreadUtil;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Admin;
@@ -35,8 +36,6 @@ import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
 import org.apache.hadoop.hbase.shaded.org.apache.commons.lang.RandomStringUtils;
 import org.apache.hadoop.hbase.util.Bytes;
 
-import io.grpc.internal.GrpcUtil;
-
 public class RecreateConnectionDriver {
   
   static final byte[] COLUMN_FAMILY = Bytes.toBytes("cf");
@@ -54,7 +53,7 @@ public class RecreateConnectionDriver {
     final TableName tableName = TableName.valueOf(tableNameStr);
     final AtomicBoolean finished = new AtomicBoolean(false);
     ExecutorService executor = Executors.newFixedThreadPool(numThreads,
-      GrpcUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
+      ThreadUtil.getThreadFactory("WORK_EXECUTOR-%d", true));
     ScheduledExecutorService finishExecutor = setupShutdown(finished);
     try (Connection connection = connect(projectId, instanceId)) {
       setupTable(tableName, connection);
@@ -112,7 +111,7 @@ public class RecreateConnectionDriver {
 
   static ScheduledExecutorService setupShutdown(final AtomicBoolean finished) {
     ScheduledExecutorService finishExecutor =
-        Executors.newScheduledThreadPool(1, GrpcUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
+        Executors.newScheduledThreadPool(1, ThreadUtil.getThreadFactory("FINISH_SCHEDULER-%d", true));
     finishExecutor.schedule(new Runnable() {
       @Override
       public void run() {