Skip to main content
sqlx es un kit de herramientas SQL asíncrono, escrito íntegramente en Rust, que incluye de serie un grupo de conexiones y soporte para migraciones. En esta guía, creará una tabla todos mediante una migración de sqlx-cli y atenderá consultas de creación, lectura, actualización y eliminación desde un pequeño servidor axum. Todas las conexiones utilizan TLS con verificación completa de certificados.

Requisitos previos

  • Rust 1.94 o posterior. Esta guía se ha probado con Rust 1.98.1, sqlx 0.9.0, sqlx-cli 0.9.0 y axum 0.8.9.
  • Una cuenta de ClickHouse Cloud
  • psql, para crear la base de datos. También puede ejecutar la sentencia CREATE DATABASE en la consola SQL.

Crear un servicio de ClickHouse Managed Postgres

En la consola de ClickHouse Cloud, haga clic en New service y seleccione Postgres. La instancia estará lista en unos minutos. Consulte la guía de inicio rápido para ver las instrucciones paso a paso.

Obtén los detalles de conexión

Haz clic en Conectar en la barra lateral izquierda de tu servicio. Deja seleccionada la opción Directly y activa Use SSL. La URL de conexión de la pestaña url termina ahora en sslmode=verify-full&sslrootcert=<service-name>-ca-certificate.pem y el puerto es 5432. Haz clic en Download CA certificate. También puedes descargarlo más adelante desde Settings → CA Certificate. El certificado es exclusivo de tu instancia, por lo que el controlador puede usarlo para verificar que se está comunicando con tu servidor. Esta guía se conecta directamente a Postgres en el puerto 5432, tanto desde la aplicación como desde sqlx-cli. Un servidor axum es un proceso de larga duración y PgPool mantiene su propio grupo de conexiones, por lo que no hace falta un segundo pooler delante de Postgres. Las migraciones deben usar siempre la conexión directa. Si ejecutas muchas instancias de la aplicación, consulta Usar PgBouncer.

Configurar el proyecto

Cree un proyecto y agregue sqlx con el controlador de Postgres, el tiempo de ejecución de Tokio y el backend TLS rustls, además de axum y algunos crates auxiliares:
Instala sqlx-cli con el mismo backend de TLS:
En macOS, use rustls en lugar de native-tlsClickHouse Managed Postgres solo acepta TLS 1.3. En macOS, el backend native-tls utiliza la biblioteca Secure Transport del sistema, que no admite TLS 1.3. Un cargo install sqlx-cli sin más opciones utiliza native-tls, y a partir de ahí todos los comandos fallan con error occurred while attempting to establish a TLS connection: One or more parameters passed to a function were not valid. (o bad protocol version). Lo mismo ocurre con una aplicación compilada con la feature tls-native-tls. En Linux, native-tls utiliza OpenSSL y consigue conectarse, pero solo carga el primer certificado de ca-certificate.pem. rustls admite TLS 1.3 en todas las plataformas y carga todos los certificados del archivo.
¿Va a añadirlo a una aplicación existente?Omita cargo new. En su proyecto, ejecute los comandos cargo add y cargo install anteriores y continúe con Configurar la conexión. Si la aplicación utiliza sqlx con SQLite:
  • Elimine sqlite de las features de sqlx en Cargo.toml y sustituya SqlitePool por PgPool.
  • Cambie los marcadores de posición ? por $1, $2, etc.
  • Reescriba sus migraciones con la sintaxis de Postgres; por ejemplo, bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY en lugar de INTEGER PRIMARY KEY AUTOINCREMENT.

Configurar la conexión

Mueva el certificado de CA que descargó al directorio del proyecto y cámbiele el nombre a ca-certificate.pem. Cree una base de datos para la aplicación. Sustituya <PASSWORD> y el host por los valores que aparecen en el cuadro modal Conectar:
Cree un archivo .env en la raíz del proyecto, o actualice DATABASE_URL en el que ya tenga, para que apunte a la base de datos guide_rust. Tanto la aplicación como sqlx-cli leen DATABASE_URL de este archivo:
.env
sqlx lee sslmode y sslrootcert de la URL. Con verify-full, comprueba que el certificado del servidor esté firmado por la CA de su instancia y que coincida con el nombre del host. La ruta de sslrootcert se resuelve en relación con el directorio desde el que ejecuta los comandos. Mantenga .env fuera del control de versiones y, si su contraseña contiene caracteres como @, / o #, aplíqueles codificación porcentual.
Establezca siempre sslmode=verify-fullSi no se indica sslmode, sqlx usa prefer: cifra la conexión, pero acepta cualquier certificado, aunque se haya configurado sslrootcert. Con verify-full, si el certificado de CA falta o es incorrecto, se produce el error UnknownIssuer, y si el host no coincide con el certificado (por ejemplo, una dirección IP), se produce el error NotValidForNameContext (certificate not valid for name en sqlx-cli). Si la ruta de sslrootcert es incorrecta, se produce el error No such file or directory.

Crear la tabla mediante una migración

Cree un archivo de migración:
Abra el nuevo archivo en el directorio migrations y añada la definición de la tabla:
migrations/<timestamp>_create_todos.sql
Aplíquela:
sqlx migrate run registra las migraciones aplicadas en la tabla _sqlx_migrations y, en llamadas posteriores, solo ejecuta las nuevas. Haga commit del directorio migrations junto con su código. Si prefiere aplicar las migraciones al iniciar la aplicación, llame a sqlx::migrate!().run(&pool).await? en main, siempre que el grupo de conexiones utilice la conexión directa.

Crear la API

Sustituya src/main.rs por un pequeño servidor axum que ejecute una consulta por ruta. En una aplicación existente, cree el grupo de la misma forma que en main y añada el struct Todo, los handlers y las rutas a su propio enrutador:
src/main.rs
Los valores siempre se pasan con .bind como parámetros ($1) y nunca se formatean dentro de la cadena SQL. Cada cadena SQL distinta se prepara una sola vez por conexión y se guarda en una caché de sentencias con capacidad para 100, que sqlx cierra al desalojarlas.
Consultas comprobadas en tiempo de compilaciónEsta guía utiliza la función query_as, que comprueba el SQL en tiempo de ejecución, por lo que cargo build no necesita una base de datos. Las macros query! y query_as!, en cambio, comprueban el SQL y los tipos de columna contra la base de datos en tiempo de compilación. Leen DATABASE_URL desde .env durante el build, así que asegúrese de que apunte a la conexión directa. Para compilar sin una base de datos, por ejemplo en CI, ejecute cargo sqlx prepare, haga commit del directorio .sqlx generado y establezca SQLX_OFFLINE=true.

Ejecutar y verificar

Inicie el servidor:
La primera línea proviene de pg_stat_ssl y confirma que la sesión está cifrada. En otra terminal, cree dos tareas, marque la primera como completada y elimine la segunda:
Cada solicitud devuelve la fila afectada. La opción -w '\n' simplemente añade un salto de línea después de cada respuesta:
Abra http://localhost:3117/todos en su navegador para ver la lista de tareas pendientes:
Para ver los datos en la consola, abra consola SQL en la barra lateral izquierda de su servicio, expanda guide_rust y luego public, y haga clic en la tabla todos. La tabla _sqlx_migrations, situada a su lado, almacena el historial de migraciones. La tabla todos contiene la fila restante:

Usar PgBouncer

Para conectar la aplicación a través del PgBouncer incluido, seleccione via PgBouncer en el cuadro de diálogo Conectar y use el puerto 6432 en la DATABASE_URL de la aplicación. El mismo certificado de CA sigue siendo válido. Por ejemplo, mantenga .env con la conexión directa para sqlx-cli y defina la variable al iniciar la aplicación:
A través de PgBouncer, pg_stat_ssl informa sobre la propia conexión de PgBouncer con Postgres, por lo que la comprobación muestra none. La conexión de su aplicación con PgBouncer se sigue verificando: un certificado de CA incorrecto falla igualmente con UnknownIssuer. PgBouncer se ejecuta en modo de agrupación por transacción, por lo que cada transacción puede ejecutarse en una conexión de Postgres distinta. Con sqlx:
  • Para conectarse se requiere .extra_float_digits(None), como en main.rs. De forma predeterminada, sqlx envía extra_float_digits como parámetro de inicio, y PgBouncer rechaza la conexión con unsupported startup parameter: extra_float_digits. Por el mismo motivo, PgConnectOptions::options falla con unsupported startup parameter in options.
  • Las sentencias preparadas funcionan con la configuración predeterminada. sqlx libera las sentencias que desaloja de su caché mediante un mensaje Close a nivel de protocolo, que PgBouncer gestiona incluso dentro de transacciones. No establezca statement_cache_capacity(0): no es necesario y, con la caché desactivada, sqlx sigue preparando sentencias con nombre, pero nunca las cierra.
  • No utilice .persistent(false) en consultas que se ejecuten fuera de una transacción. En ese caso, sqlx prepara una sentencia sin nombre y la ejecuta en un segundo viaje de ida y vuelta, que PgBouncer puede enviar a otra conexión de Postgres. La consulta puede fallar con unnamed prepared statement does not exist, o bien ejecutar sin avisar la sentencia de otro cliente y devolver resultados incorrectos.
  • La configuración de sesión establecida con SET, incluso en el hook after_connect de un grupo, no se mantiene en su conexión y puede filtrarse a otros clientes. Utilice en su lugar SET LOCAL dentro de una transacción.
  • Las migraciones deben usar la conexión directa. sqlx-cli y las macros query! no pueden conectarse a través de PgBouncer, porque siempre envían extra_float_digits. sqlx::migrate! adquiere un bloqueo consultivo a nivel de sesión (pg_advisory_lock) y, a través de PgBouncer, la liberación puede llegar a otra conexión de Postgres. En ese caso, el bloqueo sigue retenido en una conexión del grupo, y las migraciones posteriores, incluso las que usan la conexión directa, se quedan colgadas hasta que PgBouncer cierra esa conexión.

Próximos pasos

  • Conexión: cadenas de conexión, PgBouncer y TLS
  • Configuración: parámetros de Postgres y PgBouncer, como max_connections
  • Réplicas de lectura: envíe consultas de solo lectura a una réplica mediante un segundo PgPool
  • Seguridad: listas de acceso por IP y redes privadas