Esta es la tercera parte de cinco en una serie de tutoriales sobre pruebas de código de uso intensivo de datos con Go. En la segunda parte, cubrí las pruebas con una capa de datos en memoria real basada en el popular SQLite. En este tutorial, revisaré las pruebas en una capa de datos complejos locales que incluye una base de datos relacional y un caché Redis.
La prueba contra una capa de datos en memoria es impresionante. Las pruebas son muy rápidas y usted tiene control total. Pero a veces necesita estar más cerca de la configuración real de su capa de datos de producción. Aquí hay algunas razones posibles:
Estoy seguro de que hay otras razones, pero puede ver por qué solo usar una capa de datos en la memoria para las pruebas puede no ser suficiente en muchos casos.
DE ACUERDO. Así que queremos probar una capa de datos real. Pero todavía queremos ser lo más ligero y ágil posible. Eso significa una capa de datos local. Aquí están los beneficios:
En este tutorial subiremos la apuesta. Implementaremos (muy parcialmente) una capa de datos híbrida que consta de una base de datos relacional MariaDB y un servidor Redis. Luego usaremos Docker para crear una capa de datos local que podamos usar en nuestras pruebas..
Primero, necesitas a Docker, por supuesto. Revisa la documentación si no estás familiarizado con Docker. El siguiente paso es obtener imágenes para nuestros almacenes de datos: MariaDB y Redis. Sin entrar en demasiados detalles, MariaDB es un gran DB relacional compatible con MySQL, y Redis es un gran almacén de valor-clave en memoria (y mucho más).
> docker pull mariadb…> docker pull redis…> imágenes docker REPOSITORY TAG ID DE IMAGEN CREADO TAMAÑO mariadb última 51d6a5e69fa7 hace 2 semanas 402MB redis última b6dddb991dfa hace 2 semanas 107MB
Ahora que tenemos Docker instalado y tenemos las imágenes para MariaDB y Redis, podemos escribir un archivo docker-compose.yml, que usaremos para iniciar nuestros almacenes de datos. Llamemos a nuestro DB "songify".
mariadb-songify: imagen: mariadb: último comando:> --general-log --general-log-file = / var / log / mysql / query.log expose: - "3306" puertos: - "3306: 3306" entorno : MYSQL_DATABASE: "songify" MYSQL_ALLOW_EMPTY_PASSWORD: "true" volume_from: - mariadb-data mariadb-data: image: mariadb: últimos volúmenes: - / var / lib / mysql entrypoint: / bin / bash redis: image: redis expose: - " 6379 "puertos: -" 6379: 6379 "
Puede iniciar sus almacenes de datos con el docker-componer hasta
comando (similar a vagabundo
). La salida debería verse así:
> docker-compose up Iniciando hybridtest_redis_1 ... Iniciando hybridtest_mariadb-data_1 ... Iniciando hybridtest_redis_1 Iniciando hybridtest_mariadb-data_1 ... listo Comenzando híbridotest_mariaadillaAparticación_corazonAparte_mariatota_maria-tat_mariad_mariad__comeridad_comerida_corazon-Perigualidad_todas_corazon_todas_comeridad_corazamiento * DB cargada desde el disco: 0,002 segundos redis_1 | * Listo para aceptar conexiones ... mariadb-songify_1 | [Nota] mysqld: listo para conexiones ...
En este punto, tiene un servidor MariaDB completo que escucha en el puerto 3306 y un servidor Redis que escucha en el puerto 6379 (ambos son los puertos estándar).
Aprovechemos estos potentes almacenes de datos y actualicemos nuestra capa de datos a una capa de datos híbrida que almacena canciones por usuario en Redis. Cuando GetSongsByUser ()
se llama, la capa de datos primero verificará si Redis ya almacena las canciones para el usuario. Si lo hace, simplemente devuelve las canciones de Redis, pero si no lo hace (falta de caché), recuperará las canciones de MariaDB y llenará el caché de Redis, así que estará listo para la próxima vez..
Aquí está la definición de la estructura y el constructor. La estructura mantiene un identificador de base de datos como antes y también un cliente redis. El constructor se conecta a la base de datos relacional, así como a Redis. Crea el esquema y borra los redis solo si los parámetros correspondientes son verdaderos, lo cual es necesario solo para la prueba. En producción, creas el esquema una vez (ignorando las migraciones de esquema).
escriba HybridDataLayer struct db * sql.DB redis * redis.Client func NewHybridDataLayer (dbHost string, dbPort int, redisHost string, createSchema bool, clearRedis bool) (* HybridDataLayer, error) dnn :. tcp (% s:% d) / ", dbHost, dbPort) si createSchema err: = createMariaDBSchema (dsn) if err! = nil return nil, err db, err: = sql.Open (" mysql ", dsn + "desongcious? parseTime = true") if err! = nil return nil, err redisClient: = redis.NewClient (& redis.Options Addr: redisHost + ": 6379", Password: "", DB: 0, ) _, err = redisClient.Ping (). Result () if err! = nil return nil, err si clearRedis redisClient.FlushDB () return & HybridDataLayer db, redisClient, nil
MariaDB y SQLite son un poco diferentes en lo que respecta a DDL. Las diferencias son pequeñas, pero importantes. Go no tiene un conjunto de herramientas maduro de bases de datos cruzadas como el fantástico SQLAlchemy de Python, por lo que debe administrarlo usted mismo (no, Gorm no cuenta). Las principales diferencias son:
AUTOINCREMENTO
.VARCHAR
en lugar de TEXTO
.Aquí está el código:
func createMariaDBSchema (dsn string) error db, err: = sql.Open ("mysql", dsn) if err! = nil return err // Volver a crear los comandos DB: = [] string "DROP DATABASE songify;", "CREATE DATABASE songify;", para _, s: = rango (comandos) _, err = db.Exec (s) if err! = Nil return err // Crear db del esquema, err = sql.Open ("mysql", dsn + "songify? parseTime = true") if err! = nil return err schema: = [] string 'CREATE TABLE IF NOT EXISTS song (id INTEGER PRIMARY KEY AUTO_INCREMENT, url VARCHAR (2088) UNIQUE , título VARCHAR (100), descripción VARCHAR (500)); ',' CREAR TABLA SI NO EXISTE usuario (id INTEGER PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR (100), correo electrónico VARCHAR (100) ÚNICO, registrado en TIMESTAMP, last_login TIMESTAMP); ', "CREAR ÍNDICE user_email_idx ON usuario (correo electrónico);",' CREAR TABLA SI NO EXISTE la etiqueta (ID INTEGER PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR (100) UNIQUE); ', "CREATE INDEX label_name_idx ON label (nombre);", 'CREAR TABLA SI NO EXISTE label_song (label_id INTEGER NOT NULL REFE RENCES label (id), song_id INTEGER NOT NULL REFERENCES song (id), PRIMARY KEY (label_id, song_id)); ',' CREATE TABLE SI NO EXISTE user_song (user_id INTEGER NOT NULL REFERENCES usuario (id), song_id INTEGER NOT NULL REFERENCES song (id), PRIMARY KEY (user_id, song_id)); ', para _, s: = range (schema) _, err = db.Exec (s) if err! = nil return err return nil
Redis es muy fácil de usar desde Go. La biblioteca del cliente "github.com/go-redis/redis" es muy intuitiva y sigue fielmente los comandos de Redis. Por ejemplo, para probar si existe una clave, simplemente use la Salidas ()
Método del cliente redis, que acepta una o más claves y devuelve cuántas de ellas existen..
En este caso, compruebo una sola clave:
count, err: = m.redis.Exists (correo electrónico) .Resultar () si err! = nil return err
Las pruebas son en realidad idénticas. La interfaz no cambió, y el comportamiento no cambió. El único cambio es que la implementación ahora mantiene un caché en Redis. los GetSongsByEmail ()
método ahora solo llama refreshUser_Redis ()
.
func (m * HybridDataLayer) GetSongsByUser (u User) (canciones [] Song, error err) err = m.refreshUser_Redis (u.Email, & songs) return
los refreshUser_Redis ()
El método devuelve las canciones de usuario de Redis si existen y, de lo contrario, las obtiene de MariaDB..
tipo Canciones * [] Función de canción (m * HybridDataLayer) refreshUser_Redis (cadena de correo electrónico, canciones de salida) error cuenta, err: = m.redis.Exists (correo electrónico) .Resultar () si err err = nil return err si cuenta == 0 err = m.getSongsByUser_DB (correo electrónico, fuera) si err! = Nil return err para _, song: = range * out s, err: = serializeSong (song) si err! = Nil return err _, err = m.redis.SAdd (email, s) .Result () si err! = nil return err return miembros, err: = m.redis.SMembers (email) .Result () para _ , miembro: = rango de miembros canción, err: = deserializeSong ([] byte (miembro)) si err! = nil return err * out = append (* out, song) return out, nil
Hay un pequeño problema aquí desde el punto de vista de la metodología de prueba. Cuando probamos a través de la interfaz de la capa de datos abstracta, no tenemos visibilidad de la implementación de la capa de datos.
Por ejemplo, es posible que exista una gran falla en la capa de datos que omite completamente el caché y siempre obtiene los datos de la base de datos. Las pruebas pasarán, pero no podemos beneficiarnos del caché. Hablaré en la parte cinco acerca de probar su caché, lo cual es muy importante..
En este tutorial, cubrimos las pruebas en una capa de datos complejos locales que consta de múltiples almacenes de datos (una base de datos relacional y un caché de Redis). También utilizamos Docker para implementar fácilmente múltiples almacenes de datos para pruebas.
En la parte cuatro, nos centraremos en las pruebas contra los almacenes de datos remotos, utilizando instantáneas de los datos de producción para nuestras pruebas y también generando nuestros propios datos de prueba. Manténganse al tanto!