A ideia desse teste é subir um pouco o nível do teste, fazendo o código de teste montar uma estrutura de containers, criar um teste de integração e no meio dos testes pausar ou derrubar alguns containers enquanto troca o endereço IP de alguns dos containers derrubados antes de subir uma nova réplica.
O framework de teste é o docker builder criado depois de uma conversa com o Luis Serrano.
A ideia básica é permitir ao Golang subir e controlar containers de forma simples, permitindo a
transformação de pastas contendo projetos escritos em Golang virar containers desde que a pasta raiz
contenha os arquivos main.go
e go.mod
, não havendo a necessidade de saber criar um arquivo
Dockerfile.
O teste consiste em simular o CDC/Debezium gerando dados aleatórios e os enviando via
nats a dois containers montados a partir do projeto contido na pasta
chaostest/toContainer
.
Estes containers recebem dados via nats preenchem uma memória e ao final do teste os dados recebidos são colocados em um arquivo texto para comparação.
O problema desse teste, é o fato dele passar normalmente quando testado linearmente e falhar quando testado com anomalias ocorrendo entre os containers.
+-------------------------+
| |
| |
| TESTE |
| |
| |
+------------+------------+
|
| Simulação debezium
+--------------->->->------------+
|
+------------+------------+
| |
| |
| Nats |
| |
| |
+------------+------------+
|
Replicação do dado | Replicação do dado
simulado | simulado
+------------<-<-<-------+------->->->------------+
| |
+------------+------------+ +------------+------------+
| | | |
| | | |
| | | |
| CONTAINER_0 | | CONTAINER_N |
| | | |
| +---------------+ | | +---------------+ |
| | | | | | | |
+----| ARQUIVO DUMP |----+ +----| ARQUIVO DUMP |----+
| MEMORIA | | MEMORIA |
| | | |
+-------+-------+ +-------+-------+
| |
+------------>->->-------+-------<-<-<------------+
|
+------------+------------+
| |
| Comparação de arquivos |
| com dados da |
| simulação |
| |
+-------------------------+
Os passos para usar o módulo são:
Gere um struct{}
com todos os dados que você gostaria de simular e construa as funções Populate()
,
Update()
, Get()
e GetID()
de modo a serem compatíveis com a interface abaixo.
package interfaces
type DataToSimulateInterface interface {
Populate() (err error)
Update() (err error)
Get() (data interface{})
GetID() (ID interface{}, err error)
}
Veja um exemplo simples de dado a ser simulado.
package dataTest
import (
"encoding/json"
"github.com/brianvoe/gofakeit/v6"
)
type DataTest struct {
Id string
Name string
}
func (e *DataTest) GetID() (ID interface{}, err error) {
ID = e.Id
return
}
func (e *DataTest) getNextId() (id string) {
return gofakeit.UUID()
}
func (e *DataTest) Get() (data interface{}) {
return *e
}
func (e *DataTest) Populate() (err error) {
e.Id = e.getNextId()
e.Name = gofakeit.Name()
return
}
func (e *DataTest) Update() (err error) {
e.Name = gofakeit.Name()
return
}
Inicialize o sistema de mensageria.
package localDevOps
import (
"log"
"testing"
"time"
)
func TestLocalDevOps(t *testing.T) {
var err error
var messageSystem = messagingSystemNats.MessagingSystemNats{}
_, err = messageSystem.New("nats://0.0.0.0:4222")
if err != nil {
log.Printf("messageSystem.New().error: %v", err.Error())
t.FailNow()
}
}
Em seguida, inicialize a simulação do debezium.
package localDevOps
import (
"log"
"testing"
"time"
)
func TestLocalDevOps(t *testing.T) {
var err error
var messageSystem = messagingSystemNats.MessagingSystemNats{}
_, err = messageSystem.New("nats://0.0.0.0:4222")
if err != nil {
log.Printf("messageSystem.New().error: %v", err.Error())
t.FailNow()
}
var dataSimulation = dataTest.DataTest{}
var debezium = &debeziumSimulation.DebeziumSimulation{}
debezium.EnableOnStartData(10)
debezium.SetData(&dataSimulation)
debezium.SetMessagingSystem(&messageSystem)
debezium.SetMessagingTopic("dataSimulation")
debezium.SetTimers(
50*time.Millisecond,
500*time.Millisecond,
700*time.Millisecond,
1000*time.Millisecond,
60*time.Minute,
)
err = debezium.Init(false, "helmutkemper", "simulation")
if err != nil {
log.Printf("messageSystem.New().error: %v", err.Error())
t.FailNow()
}
ch := debezium.GetTerminationChannel()
<-ch
log.Print("fim da simulação")
}
Caso você não esteja familiarizado com debezium, ele envia mensagens quando o dado contido no banco de dados muda.
Os principais pontos da mensagem são:
- source: dados do banco de dados, onde você deve ficar de olho no campos bd e table com os respectivos nomes do banco de dados e da table;
- before: dado antes da alteração;
- after: dado depois da alteração;
- op: operação realizada, pode ser
c
create;r
read;u
update;d
delete ez
fim da simulação.
Veja exemplos para o dado criado acima.
Crate:
{
"source": {
"version": "1.5.0.Final",
"connector": "mysql",
"name": "mysql",
"ts_ms": 1622826301925,
"snapshot": true,
"db": "helmutkemper",
"sequence": null,
"table": "simulation",
"server_id": 0,
"gtid": null,
"file": "mysql-bin.000008",
"pos": 82625,
"row": 0,
"thread": null,
"query": null
},
"before": null,
"after": {
"Id": "93cec220-8278-49c2-86bd-994641d239b8",
"Name": "Adelle Hamill"
},
"op": "c",
"ts_ms": 1648133772,
"transaction": null
}
Update:
{
"source": {
"version": "1.5.0.Final",
"connector": "mysql",
"name": "mysql",
"ts_ms": 1622826301925,
"snapshot": true,
"db": "helmutkemper",
"sequence": null,
"table": "simulation",
"server_id": 0,
"gtid": null,
"file": "mysql-bin.000008",
"pos": 82625,
"row": 0,
"thread": null,
"query": null
},
"before": {
"Id": "e3112e32-c819-4097-9ef6-43a31b2f6a5d",
"Name": "Obie Windler"
},
"after": {
"Id": "e3112e32-c819-4097-9ef6-43a31b2f6a5d",
"Name": "Karolann Leannon"
},
"op": "u",
"ts_ms": 1648133772,
"transaction": null
}
Delete:
{
"source": {
"version": "1.5.0.Final",
"connector": "mysql",
"name": "mysql",
"ts_ms": 1622826301925,
"snapshot": true,
"db": "helmutkemper",
"sequence": null,
"table": "simulation",
"server_id": 0,
"gtid": null,
"file": "mysql-bin.000008",
"pos": 82625,
"row": 0,
"thread": null,
"query": null
},
"before": {
"Id": "4f3d5501-aa95-4756-a5c0-dfa8807f5361",
"Name": "Leonie Kerluke"
},
"after": null,
"op": "d",
"ts_ms": 1648133774,
"transaction": null
}
Fim de simulação:
{
"source": {
"version": "1.5.0.Final",
"connector": "mysql",
"name": "mysql",
"ts_ms": 1622826301925,
"snapshot": true,
"db": "helmutkemper",
"sequence": null,
"table": "simulation",
"server_id": 0,
"gtid": null,
"file": "mysql-bin.000008",
"pos": 82625,
"row": 0,
"thread": null,
"query": null
},
"before": null,
"after": null,
"op": "z",
"ts_ms": 1648135688,
"transaction": null
}
Caso você necessite refazer o sistema mensageria para algum outro diferente do nats, escreva seu código de modo a compatibilizar com a interface abaixo.
package interfaces
import "test/support/commonTypes"
type MessagingSystemInterface interface {
// Subscribe
//
// Adiciona uma função para ser invocada quando um novo evento ocorre no canal.
// Entrada:
// subject: nome do canal.
// function: Ponteiro de função a ser executada quando houver um evento no canal;
// Saída:
// err: Objeto de erro padrão do go.
//
// Nota:
// * O segundo parâmetro, de nome function, é o ponteiro de uma função invocada pelo código
// quando ocorre um evento no canal, de nome contido em subject, e ela receberá o nome do
// canal e o dado transmitido.
// Entrada:
// subject: nome do canal
// data: array de byte com a informação transmitida no canal (geralmente json)
// Saída:
// err: Objeto de erro padrão do go.
Subscribe(subject string, function func(subject string, data []byte) (err error)) (err error)
// SetReport
//
// Invoca uma função periodicamente para informar o status da conexão.
SetReport(function func(status commonTypes.QueueStatus))
// Publish
//
// Publica uma informação no canal específico da fila.
// Entrada:
// subject: Nome do canal;
// data: Array de bytes contendo a informação a ser compartilhada.
// Saída:
// err: Objeto de erro padrão do go.
Publish(subject string, data []byte) (err error)
}
Para o rodar o código no Linux ou no MacOs use o comando abaixo.
make help ## Este comando de ajuda
make build-normal ## Executa o teste TestLocalDevOps sem habilitar o teste de caos, sem log de dados na saída padrão
make build-chaos ## Executa o teste TestLocalDevOps habilitando o teste de caos, sem log de dados na saída padrão
make build-normal-log ## Executa o teste TestLocalDevOps sem habilitar o teste de caos, com log de dados na saída padrão
make build-chaos-log ## Executa o teste TestLocalDevOps habilitando o teste de caos, com log de dados na saída padrão
Para rodar no windows, use
shutdown -s -t 00