Google Cloud Spanner C++ Client  0.8.0
A C++ Client Library for Google Cloud Spanner
Cloud Spanner C++ Client Library

The Cloud Spanner C++ Client library offers types and functions to use Cloud Spanner from C++11 applications.

This library requires a C++11 compiler, it is supported (and tested) on multiple Linux distributions.

Quickstart

The following instructions show you how to perform basic tasks in Cloud Spanner using the C++ client library.

Before you begin

  1. Select or create a Google Cloud Platform (GCP) project using the manage resource page. Make a note of the project id, you will need to use it later.
  2. Make sure that billing is enabled for your project.
  3. Learn about key terms and concepts for Cloud Spanner.
  4. Setup the authentication for the examples:

Downloading and Compiling the C++ Client Library

The source code for the Cloud Spanner C++ Client Library can be found on GitHub. Download or clone this repository as usual:

git clone https://github.com/googleapis/google-cloud-cpp-spanner.git

The top-level README file in this repository includes detailed instructions on how to compile the library. The examples used in this guide should be automatically compiled when you follow said instructions.

Configuring authentication for the C++ Client Library

Like all Google Cloud Platform (GCP) services, Cloud Spanner requires that your application authenticates with the service before accessing any data. If you are not familiar with GCP authentication please take this opportunity to review the Authentication Overview. This library uses the GOOGLE_APPLICATION_CREDENTIALS environment variable to find the credentials file. For example:

Shell Command
Bash/zsh/ksh/etc. export GOOGLE_APPLICATION_CREDENTIALS=[PATH]
sh GOOGLE_APPLICATION_CREDENTIALS=[PATH]; export GOOGLE_APPLICATION_CREDENTIALS
csh/tsch setenv GOOGLE_APPLICATION_CREDENTIALS [PATH]
Windows Powershell $env:GOOGLE_APPLICATION_CREDENTIALS=[PATH]
Windows cmd.exe set GOOGLE_APPLICATION_CREDENTIALS=[PATH]

Setting this environment variable is the recommended way to configure the authentication preferences, though if the environment variable is not set, the library searches for a credentials file in the same location as the Cloud SDK.

Create a database

This is a short example that creates a Cloud Spanner database and two tables. This example assumes you have configured the authentication using GOOGLE_APPLICATION_CREDENTIALS and has created a Cloud Spanner instance:

void Quickstart(std::string const& project_id, std::string const& instance_id,
std::string const& database_id) {
namespace spanner = ::google::cloud::spanner;
spanner::Client client(spanner::MakeConnection(
spanner::Database(project_id, instance_id, database_id)));
auto rows =
client.ExecuteQuery(spanner::SqlStatement("SELECT 'Hello World'"));
using RowType = std::tuple<std::string>;
for (auto const& row : spanner::StreamOf<RowType>(rows)) {
if (!row) throw std::runtime_error(row.status().message());
std::cout << std::get<0>(*row) << "\n";
}
}

This quickstart creates a database with two tables: Singers and Albums. You must provide the project id and instance in the command-line when you run the quickstart program. Assuming you followed the build instructions referenced above this would be:

./cmake-out/google/cloud/spanner/samples/samples [PROJECT_ID] [INSTANCE_ID]

Using the library in your own projects

Our continuous integration builds compile and test the code using both Bazel, and CMake. Integrating the Cloud Spanner C++ client library should work with either.

Integrating with CMake

Compile and install the library using the usual CMake commands. Once the library is installed, you can use it in your CMakeLists.txt file like any other package:

cmake_minimum_required(VERSION 3.5)
find_package(spanner_client REQUIRED)
add_executable(my_program my_program.cc)
target_link_libraries(my_program spanner_client)

Integrating with Bazel

As we do not currently have releases of google-cloud-cpp-spanner you need to select the SHA-1 of the commit you want to use.

In your WORKSPACE file add a dependency to download and install the library, for example:

# Add the necessary Starlark functions to fetch google-cloud-cpp-spanner.
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Change the version and SHA256 hash as needed.
http_archive(
name = "com_github_googleapis_google_cloud_cpp_spanner",
sha256 = "9d7bd3de8e38f7e4b3e917766ed08fd8c8ff59a8b2f0997c4b1cb781a6fd1558",
strip_prefix = "google-cloud-cpp-spanner-0.7.0",
url = "https://github.com/googleapis/google-cloud-cpp-spanner/archive/v0.7.0.tar.gz",
)

Then load the dependencies of the library:

load("@com_github_googleapis_google_cloud_cpp_spanner//bazel:google_cloud_cpp_spanner_deps.bzl", "google_cloud_cpp_spanner_deps")
google_cloud_cpp_spanner_deps()
# Configure @com_google_googleapis to only compile C++ and gRPC:
load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language")
switched_rules_by_language(
name = "com_google_googleapis_imports",
cc = True,
grpc = True,
)
# You have to manually call the corresponding function for google-cloud-cpp and
# gRPC: https://github.com/bazelbuild/bazel/issues/1550
load("@com_github_googleapis_google_cloud_cpp_common//bazel:google_cloud_cpp_deps.bzl", "google_cloud_cpp_deps")
google_cloud_cpp_deps()
load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
grpc_deps()
load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")
grpc_extra_deps()

You can now use the library as a dependency in your BUILD files, for example:

cc_binary(
name = "my_program",
srcs = [
"my_program.cc",
],
deps = [
"@com_github_googleapis_google_cloud_cpp_spanner//google/cloud/spanner:spanner_client",
],
)

Integrating with Make

Installing the client library with CMake also creates pkg-config support files for application developers that prefer to use Make as their build system. Once the library is installed, you can use it in your Makefile like any other pkg-config module:

# Configuration variables to compile and link against the Cloud Spanner C++
# client library.
SPANNER_CXXFLAGS := $(shell pkg-config spanner_client --cflags)
SPANNER_CXXLDFLAGS := $(shell pkg-config spanner_client --libs-only-L)
SPANNER_LIBS := $(shell pkg-config spanner_client --libs-only-l)
# A target using the Cloud Spanner C++ client library.
my_program: my_program.cc
$(CXX) $(CXXFLAGS) $(SPANNER_CXXFLAGS) $(SPANNER_CXXLDFLAGS) -o $@ $^ $(SPANNER_LIBS)

Error Handling

This library never throws exceptions to signal error. In general, the library returns a StatusOr<T> if an error is possible. Some functions return objects that are not wrapped in a StatusOr<> but will themselves return a StatusOr<T> to signal an error. For example, wrappers for long running operations return future<StatusOr<T>>, and the methods on Client that read from a database return a RowStream, which iterates a stream of StatusOr<T>.

Error Handling Example

Applications should check if the StatusOr<T> contains a value before using it, much like how you might check that a pointer is not null before dereferencing it. Indeed, a StatusOr<T> object can be used like a smart-pointer to T, with the main difference being that when it does not hold a T it will instead hold a Status object with extra information about the error.

You can check that a StatusOr<T> contains a value by calling the .ok() method, or by using operator bool() (like with other smart pointers). If there is no value, you can access the contained Status object using the .status() member. If there is a value, you may access it by dereferencing with operator*() or operator->(). As with all smart pointers, callers must first check that the StatusOr<T> contains a value before dereferencing and accessing the contained value. Alternatively, callers may instead use the .value() member function which is defined to throw a RuntimeStatusError if there is no value.

Note
If you're compiling with exceptions disabled, calling .value() on a StatusOr<T> that does not contain a value will terminate the program instead of throwing.
namespace spanner = ::google::cloud::spanner;
[](spanner::Client client) {
auto rows = client.Read("Albums", spanner::KeySet::All(), {"AlbumTitle"});
// The actual type of `row` is google::cloud::StatusOr<spanner::Row>, but
// we expect it'll most often be declared with auto like this.
for (auto const& row : rows) {
// Use `row` like a smart pointer; check it before dereferencing
if (!row) {
// `row` doesn't contain a value, so `.status()` will contain error info
std::cerr << row.status();
break;
}
// The actual type of `song` is google::cloud::StatusOr<std::string>, but
// again we expect it'll be commonly declared with auto as we show here.
auto song = row->get<std::string>("AlbumTitle");
// Instead of checking then dereferencing `song` as we did with `row`
// above, here we demonstrate use of the `.value()` member, which will
// return a reference to the contained `T` if it exists, otherwise it
// will throw an exception (or terminate if compiled without exceptions).
std::cout << "SongName: " << song.value() << "\n";
}
}

Retry, Backoff, and Idempotency Policies.

The library automatically retries requests that fail with transient errors, and follows the recommended practices with respect to backoff between retries. Application developers can override the default retry and backoff policies.

The default policies are to continue retrying for up to 15 minutes, and to use truncated (at 5 minutes) exponential backoff, doubling the maximum backoff period between retries.

namespace spanner = ::google::cloud::spanner;
[](std::string const& project_id, std::string const& instance_id,
std::string const& database_id) {
auto client = spanner::Client(spanner::MakeConnection(
spanner::Database(project_id, instance_id, database_id),
spanner::ConnectionOptions{}, spanner::SessionPoolOptions{},
// Retry for at most 25 minutes.
/*maximum_duration=*/std::chrono::minutes(25))
.clone(),
// Use an truncated exponential backoff with jitter to wait between
// retries:
// https://en.wikipedia.org/wiki/Exponential_backoff
// https://cloud.google.com/storage/docs/exponential-backoff
/*initial_delay=*/std::chrono::seconds(2),
/*maximum_delay=*/std::chrono::minutes(10),
/*scaling=*/1.5)
.clone()));
auto rows =
client.ExecuteQuery(spanner::SqlStatement("SELECT 'Hello World'"));
for (auto const& row : spanner::StreamOf<std::tuple<std::string>>(rows)) {
if (!row) throw std::runtime_error(row.status().message());
std::cout << std::get<0>(*row) << "\n";
}
}
See also
LimitedTimeRetryPolicy and LimitedErrorCountRetryPolicy for alternative retry policies.
ExponentialBackoffPolicy to configure different parameters for the exponential backoff policy.

Next Steps

Mocking the Cloud Spanner C++ Client with Google Mock