Testarea codului intensiv de date cu parcurgere, Partea 3

Prezentare generală

Aceasta este o parte din trei din cinci într-o serie de tutori privind testarea codului intensiv de date cu Go. În partea a doua, am acoperit testarea împotriva unui strat real de date în memorie bazat pe SQLite-ul popular. În acest tutorial, voi examina un strat local complex de date care include o DB relațională și o cache Redis.

Testarea împotriva unui strat de date local

Testarea împotriva unui strat de date în memorie este minunată. Testele sunt cu fulgere rapidă și aveți control complet. Dar, uneori, trebuie să vă apropiați de configurația actuală a stratului de date de producție. Iată câteva motive posibile:

  • Utilizați detalii specifice despre DB-ul dvs. relațional pe care doriți să îl testați.
  • Stratul dvs. de date constă din mai multe magazine interactive de date.
  • Codul testat constă în mai multe procese care accesează același strat de date.
  • Doriți să vă pregătiți sau să respectați datele dvs. de testare utilizând instrumente standard.
  • Nu doriți să implementați un strat de date dedicat în memorie dacă stratul de date este în flux.
  • Vrei doar să știi că testezi împotriva stratului de date actual.
  • Trebuie să testați cu o mulțime de date care nu se potrivesc în memorie.

Sunt sigur că există și alte motive, dar puteți vedea de ce folosirea unui strat de date în memorie pentru testare nu este suficientă în multe cazuri.

O.K. Așadar, vrem să testați un strat de date real. Dar totuși dorim să fim cât mai ușori și mai agili. Asta înseamnă un strat de date local. Iată avantajele:

  • Nu este nevoie să furnizați și să configurați nimic în centrul de date sau în cloud.
  • Nu este nevoie să vă faceți griji că testele noastre corupe datele accidentale de producție.
  • Nu este nevoie să se coordoneze cu alți dezvoltatori într-un mediu de testare comun. 
  • Nu există o încetinire a apelurilor în rețea.
  • Control complet asupra conținutului stratului de date, cu posibilitatea de a începe de la zero în orice moment.  

În acest tutorial vom face ante. Vom implementa (foarte parțial) un strat de date hibrid care constă dintr-un DB relațional MariaDB și un server Redis. Apoi vom folosi Docker pentru a ridica un strat de date local pe care îl putem folosi în testele noastre. 

Folosind Docker pentru a evita instalarea durerilor de cap

În primul rând, aveți nevoie de Docker, desigur. Consultați documentația dacă nu sunteți familiarizat cu Docker. Următorul pas este să obții imagini pentru magazinele noastre de date: MariaDB și Redis. Fără a obține prea multe detalii, MariaDB este o DB relațională excelentă compatibilă cu MySQL, iar Redis este un magazin important în memorie cu cheie-valoare (și mult mai mult). 

> docker pull mariadb ...> docker pull redis ...> imagini docer REPOSITORY TAG IMAGINE ID CREATED SIZE mariadb Ultimele 51d6a5e69fa7 2 săptămâni în urmă 402MB redis latest b6dddb991dfa acum 2 săptămâni 107MB 

Acum, când am instalat Docker și avem imaginile pentru MariaDB și Redis, putem scrie un fișier docker-compose.yml, pe care îl vom folosi pentru a lansa magazinele noastre de date. Să numim DB noastre "songify".

mariadb-songify: imagine: mariadb: ultima comanda:> --general-log --general-log-file = / var / log / mysql / query.log expune: - porturile 3306: 3306: 3306 : MYSQL_DATABASE: "melodie" MYSQL_ALLOW_EMPTY_PASSWORD: "true" volumes_from: - mariadb-data mariadb-data: image: mariadb: ultimele volume: - / var / lib / mysql entrypoint: 6379 ": -" 6379: 6379 " 

Puteți lansa magazinul de date cu docker-compune-te comanda (similară cu vagrant sus). Rezultatul ar trebui să arate astfel: 

> docker-compune Pornire hybridtest_redis_1 ... Pornire hibridtest_mariadb-data_1 ... Pornire hibridtest_redis_1 Pornire hibridtest_mariadb-data_1 ... Pornire hibridtest_mariadb-songify_1 ... Pornire hibridtest_mariadb-songify_1 ... terminat Atașarea la hibridtest_mariadb-data_1, hybridtest_redis_1, hybridtest_mariadb-songify_1 ... redis_1 | * DB încărcat din disc: 0.002 secunde redis_1 | * Gata să accepte conexiuni ... mariadb-songify_1 | [Notă] mysqld: gata pentru conexiuni ... 

În acest moment, aveți un server MariaDB cu drepturi depline ascultând pe portul 3306 și un server Redis care ascultă pe portul 6379 (ambele sunt porturile standard).

Stratul de date hibrid

Să profităm de aceste stocări puternice de date și să ne modernizăm stratul de date într-un strat de date hibrid care cachează melodiile per utilizator în Redis. Cand GetSongsByUser ()se numește, stratul de date va verifica mai întâi dacă Redis stochează deja melodiile pentru utilizator. Dacă se întoarce apoi doar melodiile de la Redis, dar dacă nu (cache miss), atunci va prelua melodiile de la MariaDB și va popula cache-ul Redis, deci este gata pentru următoarea dată. 

Iată definiția structurilor și constructorilor. Structul păstrează un handle DB ca înainte și, de asemenea, un client redis. Constructorul se conectează la DB relațional și la Redis. Creează schema și redă replicile numai dacă parametrii corespunzători sunt adevărați, ceea ce este necesar numai pentru testare. În producție, creați schema o singură dată (ignorând migrările schemelor).

(* HybridDataLayer struct, db * sql.DB redis * redis.Client func NewHybridDataLayer (dbHost șir, dbPort int, redisHost șir, createSchema bool, clearRedis bool) dsn: = fmt.Sprintf tcp (% s:% d) / ", dbHost, dbPort) dacă createSchema err: = createMariaDBSchema (dsn)) err! = nil return nil, dsn + "desongent? parseTime = true") dacă err! = nil return nil, err redisClient: = redis.NewClient (& redis.Options Addr: redisHost + ": 6379" , err = redisClient.FlushDB () return & HybridDataLayer db, redisClient, nil

Folosind MariaDB

MariaDB și SQLite sunt puțin diferite în ceea ce privește DDL. Diferențele sunt mici, dar importante. Du-te nu are un set de instrumente de tip cross-DB matură, cum ar fi SQLAlchemy-ul fantastic al lui Python, așa că trebuie să-l gestionezi singur (nu, Gorm nu contează). Principalele diferențe sunt:

  • Driverul SQL este "github.com/go-sql-driver/mysql".
  • Baza de date nu trăiește în memorie, deci este recreată de fiecare dată (drop and create). 
  • Schema trebuie să fie o felie de instrucțiuni DDL independente în loc de un șir de toate instrucțiunile.
  • Tastele primare de incrementare automată sunt marcate cu INCREMENT AUTO.
  • VARCHAR in loc de TEXT.

Iată codul:

() db, err: = sql.Open ("mysql", dsn) dacă err! = nil return err "CREATE DATABASE songify;", pentru _, s: = interval (comenzi) _, err = db.Exec (e) dacă err! = Nil return err // Creare schemă db, err = sql.Open ("mysql", dsn + "songify? parseTime = true") dacă err! = nil return err schema: = [] șirul 'CREATE TABLE IF NOT EXISTS cântec (id INTEGER PRIMARY KEY AUTO_INCREMENT, url VARCHAR , VARCHAR (100), descriere VARCHAR (500)) ',' CREATE TABLE DACĂ NU ESTE EXISTS utilizator (id INTEGER KEY PRIMARY AUTO_INCREMENT, nume VARCHAR (100), email VARCHAR (100) UNIQUE, registered_at TIMESTAMP, last_login TIMESTAMP); ',' CREATE INDEX user_email_idx ON user (email); ',' CREATE TABLE DACĂ NU EXISTS etichetă (id INTEGER KEY PRIMARY AUTO_INCREMENT, nume VARCHAR (100) UNIQUE; 'CREAȚI TABELUL DACĂ NU EXISTĂ label_song (label_id INTEGER NOT NULL REFE RENCES etichetă (id), song_id INTEGER NOT NULL REFERINȚE song (ID), PRIMARY KEY (label_id, song_id)); ',' CREATE TABLE IF NU EXISTS user_song (user_id INTEGER NOT NULL REFERINȚE user id id song_id INTEGER NOT NULL REFERINȚE (id), PRIMARY KEY (user_id, song_id); ', pentru _, s: = interval (schema) _, err = db.Exec err! 

Folosind Redis

Redis este foarte ușor de folosit de la Go. Biblioteca clientului "github.com/go-redis/redis" este foarte intuitivă și respectă în mod fidel comenzile Redis. De exemplu, pentru a testa dacă există o cheie, utilizați doar Ieșirile () metoda clientului redis, care acceptă una sau mai multe chei și returnează câte dintre ele există. 

În acest caz, verific doar pentru o cheie:

 numără err: = m.redis.Exists (email) .Result () dacă err! = nil return err

Testarea accesului la mai multe stocuri de date

Testele sunt de fapt identice. Interfața nu sa schimbat și comportamentul nu sa schimbat. Singura modificare este că implementarea acum păstrează o memorie cache în Redis. GetSongsByEmail () metoda acum doar apeluri refreshUser_Redis ().

func (m * HybridDataLayer) GetSongsByUser (u Utilizator) (melodii [] Song, eroare eroare) err = m.refreshUser_Redis (u.Email, & melodii) 

refreshUser_Redis () metoda returnează melodiile de utilizator de la Redis dacă există și altfel le aduce de la MariaDB.

(melodii, melodii) eroare: count, err: = m.redis.Exists (e-mail) .Result () dacă err! = nil return err == 0 err = m.getSongsByUser_DB (email, out) dacă err! = Nil return err pentru _, song: = interval * out s, err: = serializeSong , err = m.redis.SAdd (e-mail, s) .Result () dacă err! = nil return err , membru: = membrii razei song, err: = deserializeSong ([] byte (membru)) err! = nil return err 

Există o mică problemă din punct de vedere al metodologiei de testare. Când testați prin interfața cu stratul abstract de date, nu avem nicio vizibilitate în implementarea stratului de date.

De exemplu, este posibil să existe un defect mare în care stratul de date să depășească complet memoria cache și să preia întotdeauna datele din DB. Testele vor trece, dar nu vom beneficia de memoria cache. Voi vorbi în partea a cincea despre testarea cache-ului, ceea ce este foarte important.  

Concluzie

În acest tutorial, am acoperit testarea împotriva unui strat local complex de date care constă din mai multe stocări de date (un DB relațional și o cache Redis). De asemenea, am folosit Docker pentru a implementa cu ușurință mai multe magazine de date pentru testare.

În partea a patra, ne vom concentra pe testarea împotriva stocărilor de date la distanță, utilizând instantanee ale datelor de producție pentru testele noastre și generând, de asemenea, propriile noastre date de testare. Rămâneți aproape!

Cod