Configuring connection pools

Some Cloud Bigtable client libraries let you configure the number of gRPC channels in a client's connection pool, also known as a channel pool. In most cases, the default configuration is correct, and there is no need to change it.

This page describes how to determine the optimal connection pool size for your application, and it presents code snippets that show how to configure the connection pools.

Before you read this page, read the overview of Bigtable connection pools to learn how they work and whether you should change yours.

The following client libraries offer connection pooling and let you configure the number of pools:

Determining the best connection pool size

Ideally, to leave room for traffic fluctuations, a connection pool has about half the number of connections it takes for maximum saturation. Because a connection can handle a maximum of 100 concurrent requests, around 50 requests per connection is optimal. This concept is described in more detail in Connection pools.

Monitor your traffic after making changes and adjust the number of connections in your pool if necessary.

The following steps help you calculate the optimal number of connections in your channel pool using client-side metrics such as those available from OpenCensus.

  1. From your client-side metrics, gather the following information:
    1. The average number of queries per second (QPS) when your application is running a typical workload.
    2. The average latency (the response time for a single request) in ms (L).
  2. Convert the QPS to ms by dividing it by 1,000.
  3. Multiply the result by L to find the number of requests that your application can send serially (S).
  4. Divide S by 100, which is the maximum number of streams available, to get the minimum number of channels that would work.
  5. Finally, double that value to get the optimal number of connections in the pool.

These steps are expressed in the following equation:

(((QPS sec ÷ 1,000) ÷ L ms) ÷ 100 streams) * 2 = Optimal number of connections

Example

Suppose your application typically sends 50,000 requests per second, and the average latency is 10 ms. This means that you can send 500 requests serially. Each channel can have at most 100 requests out concurrently, so 500 / 100 = 5 fully loaded channels. You want to leave some headroom, so double it to arrive at 10.

Setting the pool size

The following code samples demonstrate how to configure the number of pools in the client libraries that let you set the pool size.

Go

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/bigtable"
	"google.golang.org/api/option"
)

func configureConnectionPool(w io.Writer, projectID, instanceID string) error {
	// projectID := "my-project-id"
	// instanceID := "my-instance-id"
	ctx := context.Background()

	// Set up Bigtable data operations client.
	poolSize := 10
	client, err := bigtable.NewClient(ctx, projectID, instanceID, option.WithGRPCConnectionPool(poolSize))
	defer client.Close()

	if err != nil {
		return fmt.Errorf("bigtable.NewClient: %v", err)
	}

	fmt.Fprintf(w, "Connected with pool size of %d", poolSize)

	return nil
}

HBase



import static com.google.cloud.bigtable.hbase.BigtableOptionsFactory.BIGTABLE_DATA_CHANNEL_COUNT_KEY;

import com.google.cloud.bigtable.hbase.BigtableConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Connection;

public class ConfigureConnectionPool {

  public static void configureConnectionPool(String projectId, String instanceId) {
    // String projectId = "my-project-id";
    // String instanceId = "my-instance-id";
    Configuration config = BigtableConfiguration.configure(projectId, instanceId);
    config.setInt(BIGTABLE_DATA_CHANNEL_COUNT_KEY, 10);
    try (Connection connection = BigtableConfiguration.connect(config)) {
      int poolSize = connection.getConfiguration().getInt(BIGTABLE_DATA_CHANNEL_COUNT_KEY, 0);

      System.out.println(String.format("Connected with pool size of %d", poolSize));
    } catch (Exception e) {
      System.out.println("Error during ConfigureConnectionPool: \n" + e.toString());
    }
  }
}

Java


import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings;
import java.io.IOException;

public class ConfigureConnectionPool {

  public static void configureConnectionPool(String projectId, String instanceId) {
    // String projectId = "my-project-id";
    // String instanceId = "my-instance-id";

    BigtableDataSettings.Builder settingsBuilder = BigtableDataSettings.newBuilder()
        .setProjectId(projectId)
        .setInstanceId(instanceId);

    settingsBuilder.stubSettings()
        .setTransportChannelProvider(
            EnhancedBigtableStubSettings.defaultGrpcTransportProviderBuilder()
                .setPoolSize(10)
                .build());

    BigtableDataSettings settings = settingsBuilder.build();
    try (BigtableDataClient dataClient = BigtableDataClient.create(settings)) {
      InstantiatingGrpcChannelProvider provider =
          (InstantiatingGrpcChannelProvider) settings.getStubSettings()
              .getTransportChannelProvider();

      int poolSize = provider.toBuilder().getPoolSize();

      System.out.println(String.format("Connected with pool size of %d", poolSize));
    } catch (IOException e) {
      System.out.println("Error during ConfigureConnectionPool: \n" + e.toString());
    }
  }
}

C++

namespace cbt = ::google::cloud::bigtable;
namespace gc = ::google::cloud;
[](std::string const& project_id, std::string const& instance_id,
   std::string const& table_id) {
  auto constexpr kPoolSize = 10;
  auto options = gc::Options{}.set<gc::GrpcNumChannelsOption>(kPoolSize);
  auto table = cbt::Table(
      cbt::MakeDataClient(project_id, instance_id, options), table_id);
  std::cout << "Connected with channel pool size of " << kPoolSize << "\n";
}