Conéctate a Spanner con el adaptador de Cassandra

En esta página, se describe el adaptador de Cassandra y se explica cómo usar y conectar Spanner desde él.

El adaptador de Cassandra está diseñado para ejecutarse en la misma máquina que tu aplicación. El adaptador expone un extremo en localhost que admite el protocolo de transmisión de Cassandra Query Language (CQL). Traduce el protocolo de transmisión de CQL a gRPC, el protocolo de transmisión de Spanner. Con este proxy ejecutándose de forma local, un cliente de Cassandra puede conectarse a una base de datos de Spanner.

Puedes iniciar el adaptador de Cassandra de las siguientes maneras:

  • En proceso con tu aplicación de Go
  • En proceso con tu aplicación de Java
  • Como un proceso independiente
  • En un contenedor de Docker

Antes de comenzar

Antes de iniciar el adaptador de Cassandra, asegúrate de haberte autenticado con una cuenta de usuario o una cuenta de servicio en la máquina en la que se ejecutará el adaptador de Cassandra. Si usas una cuenta de servicio, debes conocer la ubicación del archivo de claves JSON (el archivo de credenciales). Establece la variable de entorno GOOGLE_APPLICATION_CREDENTIALS para especificar la ruta de acceso de las credenciales. Antes de iniciar el adaptador de Cassandra, asegúrate de haberte autenticado con una cuenta de usuario o una cuenta de servicio en la máquina en la que se ejecutará el adaptador de Cassandra. Si usas una cuenta de servicio, debes conocer la ubicación del archivo de claves JSON (el archivo de credenciales). Establece la variable de entorno GOOGLE_APPLICATION_CREDENTIALS para especificar la ruta de acceso de las credenciales.

Para obtener más información, consulte:

Conecta el adaptador de Cassandra a tu aplicación

El adaptador de Cassandra requiere la siguiente información:

  • Nombre del proyecto
  • Nombre de la instancia de Spanner
  • Base de datos a la que te conectarás

Si usas Docker, necesitas la ruta de acceso de un archivo de credenciales con formato JSON (archivo de claves).

Java en proceso

  1. Si usas una cuenta de servicio para la autenticación, asegúrate de que la variable de entorno GOOGLE_APPLICATION_CREDENTIALS esté establecida en la ruta de acceso del archivo de credenciales.

  2. Para las aplicaciones de Java, puedes vincular el adaptador de Cassandra a la aplicación directamente agregando google-cloud-spanner-cassandra como una dependencia a tu proyecto.

En Maven, agrega la siguiente dependencia nueva en la <dependencies> sección:

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

En Gradle, agrega lo siguiente:

dependencies {
    implementation 'com.google.cloud:google-cloud-spanner-cassandra:1.2.0'
}

  1. Modifica tu código de creación de CqlSession. En lugar de usar CqlSessionBuilder, usa SpannerCqlSessionBuilder y proporciona el URI de la base de datos de Spanner:
    
    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();
        }
      }
    }
    

Go en proceso

Para las aplicaciones de Go, debes realizar un cambio de una línea en el archivo de inicialización del clúster para integrar el cliente de Spanner Cassandra Go. Luego, puedes vincular el adaptador de Cassandra a la aplicación directamente.

  1. Importa el paquete spanner del adaptador desde el cliente de Spanner Cassandra Go en tu aplicación de Go.
import spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
  1. Modifica tu código de creación de clúster para usar spanner.NewCluster en lugar de gocql.NewCluster y proporciona el URI de la base de datos de Spanner:
    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
    }

Puedes configurar tu clúster como de costumbre después de conectarte a tu base de datos de Spanner.

Independiente

  1. Clona el repositorio:
git clone https://github.com/googleapis/go-spanner-cassandra.git
cd go-spanner-cassandra
  1. Ejecuta cassandra_launcher.go con la marca -db obligatoria:
go run cassandra_launcher.go \
-db "projects/my_project/instances/my_instance/databases/my_database"
  1. Reemplaza -db por el URI de tu base de datos de Spanner.

Docker

Ejecuta el siguiente comando para iniciar el adaptador de Cassandra.

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

La siguiente lista contiene las opciones de inicio que se usan con mayor frecuencia para el adaptador de Spanner Cassandra:

  • -db <DatabaseUri>

El URI de la base de datos de Spanner (obligatorio). Especifica la base de datos de Spanner a la que se conecta el cliente. Por ejemplo, projects/YOUR_PROJECT/instances/YOUR_INSTANCE/databases/YOUR_DATABASE.

  • -tcp <TCPEndpoint>

La dirección del objeto de escucha del proxy del cliente. Define el extremo TCP en el que el cliente escucha las conexiones entrantes del cliente de Cassandra. Valor predeterminado:localhost:9042

  • -grpc-channels <NumGrpcChannels>

La cantidad de canales de gRPC que se usarán cuando se conecten a Spanner. Valor predeterminado: 4

Por ejemplo, el siguiente comando inicia el adaptador de Cassandra en el puerto 9042 con las credenciales de la aplicación y conecta el adaptador a la base de datos projects/my_project/instances/my_instance/databases/my_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

Recomendaciones

Las siguientes recomendaciones te ayudan a mejorar tu experiencia con el adaptador de Cassandra. Estas recomendaciones se centran en Java, específicamente en el controlador de cliente de Cassandra para Java versión 4.

Aumenta el tiempo de espera de la solicitud

Un tiempo de espera de la solicitud de cinco segundos o más proporciona una mejor experiencia con el adaptador de Cassandra que el valor predeterminado de dos segundos.

# Sample application.conf: increases request timeout to five seconds
datastax-java-driver {
  basic {
    request {
      timeout = 5 seconds
    }
  }
}

Ajusta la agrupación de conexiones

Las configuraciones predeterminadas para la cantidad máxima de conexiones y la cantidad máxima de solicitudes simultáneas por conexión o host son adecuadas para el desarrollo, las pruebas y los entornos de producción o etapa de pruebas de bajo volumen. Sin embargo, te recomendamos que aumentes estos valores, ya que el adaptador de Cassandra se hace pasar por un solo nodo, a diferencia de un grupo de nodos dentro del clúster de Cassandra.

Aumentar estos valores permite más conexiones simultáneas entre el cliente y la interfaz de Cassandra. Esto puede evitar el agotamiento del grupo de conexiones con una carga pesada.

# Sample application.conf: increases maximum number of requests that can be
# executed concurrently on a connection
advanced.connection {
  max-requests-per-connection = 32000
  pool {
    local.size = 10
  }
}

Ajusta los canales de gRPC

El cliente de Spanner usa los canales de gRPC para la comunicación. Un canal de gRPC es aproximadamente equivalente a una conexión TCP. Un canal de gRPC puede controlar hasta 100 solicitudes simultáneas. Esto significa que una aplicación necesitará al menos tantos canales de gRPC como la cantidad de solicitudes simultáneas que ejecutará la aplicación, dividida por 100.

Inhabilita el enrutamiento con reconocimiento de tokens

Es posible que los controladores que usan el balanceo de cargas con reconocimiento de tokens impriman una advertencia o no funcionen cuando se usa el adaptador de Cassandra. Debido a que el adaptador de Cassandra se hace pasar por un solo nodo, no siempre funciona bien con los controladores con reconocimiento de tokens que esperan que haya al menos un factor de replicación de nodos en el clúster. Es posible que algunos controladores impriman una advertencia (que se puede ignorar) y recurran a algo como la política de balanceo de cargas de round robin, mientras que otros controladores pueden fallar con un error. Para los controladores que fallan con un error, debes inhabilitar el reconocimiento de tokens o configurar la política de balanceo de cargas de round robin.

# Sample application.conf: disables token-aware routing
metadata {
  token-map {
    enabled = false
  }
}

Fija la versión del protocolo en V4

El adaptador de Cassandra es compatible con cualquier controlador de cliente de Apache Cassandra de código abierto que cumpla con el protocolo de transmisión binario de CQL v4. Asegúrate de fijar PROTOCOL_VERSION en V4; de lo contrario, es posible que veas errores de conexión.

# Sample application.conf: overrides protocol version to V4
datastax-java-driver {
  advanced.protocol.version = V4
}

¿Qué sigue?