Connect to Spanner using the Cassandra Adapter

This page describes the Cassandra Adapter and explains how to use and connect Spanner from it.

The Cassandra Adapter is designed to run on the same machine as your application. The adapter exposes an endpoint on localhost that supports the Cassandra Query Language (CQL) wire protocol. It translates the CQL wire protocol into gRPC, the Spanner wire protocol. With this proxy running locally, a Cassandra client can connect to a Spanner database.

You can start the Cassandra Adapter in the following ways:

  • In-process with your Go application
  • In-process with your Java application
  • As a standalone process
  • In a Docker container

Before you begin

Before starting the Cassandra Adapter, ensure that you have authenticated with either a user account or service account on the machine where Cassandra Adapter will be running. If you are using a service account, you must know the location of the JSON key file (the credentials file). You set the GOOGLE_APPLICATION_CREDENTIALS environment variable to specify the credentials path.

For more information, see:

Connect the Cassandra Adapter to your application

The Cassandra Adapter requires the following information:

  • Project name
  • Spanner instance name
  • Database to connect to

If you use Docker, you need the path for a JSON-formatted credentials file (key file).

Go in-process

For Go applications, you only need to make a one-line change to the cluster initialization file to integrate the Spanner Cassandra Go client. You can then link the Cassandra Adapter to the application directly.

  1. Import the adapter's spanner package from Spanner Cassandra Go Client in your Go application.

    import spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
    
  2. Modify your cluster creation code to use spanner.NewCluster instead of gocql.NewCluster, and provide the Spanner database URI:

    import (
    	"fmt"
    	"io"
    	"math"
    	"math/rand/v2"
    	"time"
    
    	spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
    )
    
    // This sample assumes your spanner database <your_db> contains a table <users>
    // with the following schema:
    //
    // CREATE TABLE users (
    //	id   	 	INT64          OPTIONS (cassandra_type = 'int'),
    //	active    	BOOL           OPTIONS (cassandra_type = 'boolean'),
    //	username  	STRING(MAX)    OPTIONS (cassandra_type = 'text'),
    // ) PRIMARY KEY (id);
    
    func quickStart(databaseURI string, w io.Writer) error {
    	opts := &spanner.Options{
    		DatabaseUri: databaseURI,
    	}
    	cluster := spanner.NewCluster(opts)
    	if cluster == nil {
    		return fmt.Errorf("failed to create cluster")
    	}
    	defer spanner.CloseCluster(cluster)
    
    	// You can still configure your cluster as usual after connecting to your
    	// spanner database
    	cluster.Timeout = 5 * time.Second
    	cluster.Keyspace = "your_db_name"
    
    	session, err := cluster.CreateSession()
    
    	if err != nil {
    		return err
    	}
    
    	randomUserId := rand.IntN(math.MaxInt32)
    	if err = session.Query("INSERT INTO users (id, active, username) VALUES (?, ?, ?)",
    			       randomUserId, true, "John Doe").
    		Exec(); err != nil {
    		return err
    	}
    
    	var id int
    	var active bool
    	var username string
    	if err = session.Query("SELECT id, active, username FROM users WHERE id = ?",
    			       randomUserId).
    		Scan(&id, &active, &username); err != nil {
    		return err
    	}
    	fmt.Fprintf(w, "%d %v %s\n", id, active, username)
    	return nil
    }

    You can still configure your cluster as usual after connecting to your Spanner database.

Java in-process

  1. If you are using a service account for authentication, ensure that the GOOGLE_APPLICATION_CREDENTIALS environment variable is set to the path of the credentials file.

  2. For Java applications, you can link the Cassandra Adapter to the application directly by adding google-cloud-spanner-cassandra as a dependency to your project.

    For Maven, add the following:

     <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-spanner-cassandra</artifactId>
            <version>0.1.0</version>
     </dependency>
    

    For Gradle, add the following:

     dependencies {
            implementation 'com.google.cloud:google-cloud-spanner-cassandra:0.1.0'
     }
    
  3. Modify your CqlSession creation code. Instead of using CqlSessionBuilder, use SpannerCqlSessionBuilder and provide the Spanner database URI:

    
    import com.datastax.oss.driver.api.core.CqlSession;
    import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
    import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
    import com.datastax.oss.driver.api.core.cql.ResultSet;
    import com.datastax.oss.driver.api.core.cql.Row;
    import com.google.cloud.spanner.adapter.SpannerCqlSession;
    import java.net.InetSocketAddress;
    import java.time.Duration;
    import java.util.Random;
    
    // This sample assumes your spanner database <my_db> contains a table <users>
    // with the following schema:
    /*
    CREATE TABLE users (
    	id   INT64          OPTIONS (cassandra_type = 'int'),
    	active    BOOL           OPTIONS (cassandra_type = 'boolean'),
    	username  STRING(MAX)    OPTIONS (cassandra_type = 'text'),
    ) PRIMARY KEY (id);
    */
    class QuickStartSample {
    
      public static void main(String[] args) {
    
        // TODO(developer): Replace these variables before running the sample.
        final String projectId = "my-gcp-project";
        final String instanceId = "my-spanner-instance";
        final String databaseId = "my_db";
    
        final String databaseUri =
            String.format("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId);
    
        try (CqlSession session =
            SpannerCqlSession.builder() // `SpannerCqlSession` instead of `CqlSession`
                .setDatabaseUri(databaseUri) // Set spanner database URI.
                .addContactPoint(new InetSocketAddress("localhost", 9042))
                .withLocalDatacenter("datacenter1")
                .withKeyspace(databaseId) // Keyspace name should be the same as spanner database name
                .withConfigLoader(
                    DriverConfigLoader.programmaticBuilder()
                        .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4")
                        .withDuration(
                            DefaultDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, Duration.ofSeconds(5))
                        .build())
                .build()) {
    
          final int randomUserId = new Random().nextInt(Integer.MAX_VALUE);
    
          System.out.printf("Inserting user with ID: %d%n", randomUserId);
    
          // INSERT data
          session.execute(
              "INSERT INTO users (id, active, username) VALUES (?, ?, ?)",
              randomUserId,
              true,
              "John Doe");
    
          System.out.printf("Successfully inserted user: %d%n", randomUserId);
          System.out.printf("Querying user: %d%n", randomUserId);
    
          // SELECT data
          ResultSet rs =
              session.execute("SELECT id, active, username FROM users WHERE id = ?", randomUserId);
    
          // Get the first row from the result set
          Row row = rs.one();
    
          System.out.printf(
              "%d %b %s%n", row.getInt("id"), row.getBoolean("active"), row.getString("username"));
    
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    

Standalone

  1. Clone the repository:

     git clone https://github.com/googleapis/go-spanner-cassandra.git
     cd go-spanner-cassandra
    
  2. Run the cassandra_launcher.go with the required -db flag:

     go run cassandra_launcher.go \
     -db "projects/my_project/instances/my_instance/databases/my_database"
    

    Replace -db with your Spanner database URI.

Docker

Start the Cassandra Adapter with the following command.

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
docker run -d -p 9042:9042 \
-e GOOGLE_APPLICATION_CREDENTIALS \
-v ${GOOGLE_APPLICATION_CREDENTIALS}:${GOOGLE_APPLICATION_CREDENTIALS}:ro \
gcr.io/cloud-spanner-adapter/cassandra-adapter \
-db DATABASE_URI

The following list contains the most frequently used startup options for the Spanner Cassandra Adapter:

  • -db <DatabaseUri>

    • The Spanner database URI (required). This specifies the Spanner database that the client connects to. For example, projects/YOUR_PROJECT/instances/YOUR_INSTANCE/databases/YOUR_DATABASE
  • -tcp <TCPEndpoint>

    • The client proxy listener address. This defines the TCP endpoint where the client listens for incoming Cassandra client connections.
    • Default:
      • When running in-process inside a Go language application: localhost:9042
      • When running as a sidecar proxy: :9042 to bind all network interfaces, suitable for Docker forwarding.
  • -grpc-channels <NumGrpcChannels>

    • The number of gRPC channels to use when connecting to Spanner.
    • Default: 4

    For example, the following command starts the Cassandra Adapter on port 9042 using the application credentials, and connects the adapter to the projects/my_project/instances/my_instance/databases/my_database database:

    export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
    docker run -d -p 9042:9042 \
    -e GOOGLE_APPLICATION_CREDENTIALS \
    -v ${GOOGLE_APPLICATION_CREDENTIALS}:${GOOGLE_APPLICATION_CREDENTIALS}:ro \
    gcr.io/cloud-spanner-adapter/cassandra-adapter \
    -db projects/my_project/instances/my_instance/databases/my_database
    

What's next