Para oferecer suporte a um painel de integração contínua escalonável, com bom desempenho e flexível, o back-end do painel do VTS precisa ser projetado com cuidado e com um bom entendimento da funcionalidade do banco de dados. O Google Cloud Datastore é um banco de dados NoSQL que oferece garantias ACID transacionais e consistência eventual, além de consistência forte em grupos de entidades. No entanto, a estrutura é muito diferente dos bancos de dados SQL (e até do Cloud Bigtable). Em vez de tabelas, linhas e células, há tipos, entidades e propriedades.
As seções a seguir descrevem a estrutura de dados e os padrões de consulta para criar um back-end eficaz para o serviço da Web do painel do VTS.
Entidades
As entidades a seguir armazenam resumos e recursos das execuções de teste do VTS:
- Entidade de teste. Armazena metadados sobre execuções de teste de um teste específico. A chave é o nome do teste, e as propriedades incluem a contagem de falhas, a contagem de aprovação e a lista de falhas de casos de teste desde que os trabalhos de alerta foram atualizados.
- Entidade de execução de teste. Contém metadados de execuções de um teste específico. Ele precisa armazenar os carimbos de data/hora de início e de término do teste, o ID do build do teste, o número de casos de teste aprovados e reprovados, o tipo de execução (por exemplo, pré-envio, pós-envio ou local), uma lista de links de registro, o nome da máquina host e as contagens de resumo de cobertura.
- Entidade de informações do dispositivo. Contém detalhes sobre os dispositivos usados durante a execução do teste. Ele inclui o ID do build do dispositivo, o nome do produto, o destino do build, a ramificação e as informações da ABI. Ela é armazenada separadamente da entidade de execução de teste para oferecer suporte a execuções de teste em vários dispositivos de uma maneira um-para-muitos.
- Entidade de execução do ponto de análise de perfil. Resume os dados coletados para um determinado ponto de criação de perfil em uma execução de teste. Ele descreve os rótulos do eixo, o nome do ponto de perfil, os valores, o tipo e o modo de regressão dos dados de perfil.
- Entidade de cobertura. Descreve os dados de cobertura coletados para um arquivo. Ele contém as informações do projeto do Git, o caminho do arquivo e a lista de contagens de cobertura por linha no arquivo de origem.
- Entidade de execução do caso de teste. Descreve o resultado de um caso de teste específico de uma execução de teste, incluindo o nome do caso de teste e o resultado.
- Entidade "Favoritos do usuário". Cada assinatura de usuário pode ser representada em uma entidade que contém uma referência ao teste e o ID do usuário gerado pelo serviço de usuário do App Engine. Isso permite consultas bidirecionais eficientes (ou seja, para todos os usuários inscritos em um teste e para todos os testes marcados como favoritos por um usuário).
Agrupamento de entidades
Cada módulo de teste representa a raiz de um grupo de entidades. As entidades de execução de teste são filhos desse grupo e pais de entidades de dispositivo, entidades de ponto de perfil e entidades de cobertura relevantes para o respectivo teste e execução de teste ancestral.

Importante:ao projetar relacionamentos de ancestralidade, é preciso equilibrar a necessidade de fornecer mecanismos de consulta eficazes e consistentes com as limitações impostas pelo banco de dados.
Vantagens
O requisito de consistência garante que as operações futuras não vão notar os efeitos de uma transação até que ela seja confirmada e que as transações do passado sejam visíveis para as operações atuais. No Cloud Datastore, o agrupamento de entidades cria ilhas de consistência de leitura e gravação no grupo, que, neste caso, são todas as execuções de teste e dados relacionados a um módulo de teste. Isso oferece os seguintes benefícios:
- As leituras e atualizações para testar o estado do módulo por jobs de alerta podem ser tratadas como atômicas.
- Visualização consistente garantida dos resultados dos casos de teste nos módulos de teste
- Consultas mais rápidas nas árvores genealógicas
Limitações
Não é recomendável gravar em um grupo de entidades a uma taxa mais rápida do que uma entidade por segundo, porque algumas gravações podem ser rejeitadas. Contanto que os trabalhos de alerta e o upload não aconteçam a uma taxa mais rápida do que uma gravação por segundo, a estrutura é sólida e garante uma consistência forte.
No final, o limite de uma gravação por módulo de teste por segundo é razoável porque as execuções de teste geralmente levam pelo menos um minuto, incluindo a sobrecarga do framework VTS. A menos que um teste seja executado de forma consistente simultaneamente em mais de 60 hosts diferentes, não pode haver um gargalo de gravação. Isso se torna ainda menos provável, já que cada módulo faz parte de um plano de teste que geralmente leva mais de uma hora. As anomalias podem ser facilmente tratadas se os hosts executarem os testes ao mesmo tempo, causando breves rajadas de gravações nos mesmos hosts (por exemplo, detectando erros de gravação e tentando novamente).
Considerações sobre escalonamento
Uma execução de teste não precisa necessariamente ter o teste como pai (por exemplo, ela pode usar outra chave e ter o nome do teste, o horário de início do teste como propriedades); no entanto, isso vai trocar a consistência forte pela consistência eventual. Por exemplo, o job de alerta pode não mostrar um snapshot mutuamente consistente das execuções de teste mais recentes em um módulo de teste, o que significa que o estado global pode não mostrar uma representação totalmente precisa da sequência de execuções de teste. Isso também pode afetar a exibição de execuções de teste em um único módulo de teste, que pode não ser necessariamente um instantâneo consistente da sequência de execução. Eventualmente, o instantâneo será consistente, mas não há garantias de que os dados mais recentes serão.
Casos de teste
Outro gargalo potencial são testes grandes com muitos casos de teste. As duas restrições operacionais são a capacidade de gravação máxima em um grupo de entidades de uma por segundo, além de um tamanho máximo de transação de 500 entidades.
Uma abordagem seria especificar um caso de teste que tenha uma execução de teste como ancestral, semelhante à forma como os dados de cobertura, de perfil e de dispositivo são armazenados:

Embora essa abordagem ofereça atomicidade e consistência, ela impõe fortes limitações aos testes: se uma transação for limitada a 500 entidades, um teste não poderá ter mais de 498 casos de teste (assumindo que não haja dados de cobertura ou perfil). Se um teste exceder esse valor, uma única transação não poderá gravar todos os resultados do caso de teste de uma só vez. Além disso, dividir os casos de teste em transações separadas pode exceder a capacidade de gravação máxima do grupo de entidades de uma iteração por segundo. Como essa solução não é escalonada bem sem sacrificar o desempenho, ela não é recomendada.
No entanto, em vez de armazenar os resultados do caso de teste como filhos da execução de teste, os casos de teste podem ser armazenados de forma independente e as chaves fornecidas à execução de teste (uma execução de teste contém uma lista de identificadores para as entidades de casos de teste):

À primeira vista, isso pode parecer uma quebra da garantia de consistência forte. No entanto, se o cliente tiver uma entidade de execução de teste e uma lista de identificadores de casos de teste, ele não precisará criar uma consulta. Em vez disso, ele poderá receber os casos de teste diretamente pelos identificadores, o que é sempre garantido ser consistente. Essa abordagem alivia muito a restrição no número de casos de teste que uma execução de teste pode ter, mantendo uma consistência forte sem ameaçar gravações excessivas em um grupo de entidades.
Padrões de acesso a dados
O painel do VTS usa os seguintes padrões de acesso a dados:
- Favoritos do usuário. Pode ser consultado usando um filtro de igualdade em entidades de favoritos do usuário que têm o objeto de usuário do App Engine como uma propriedade.
- Listagem de teste. Consulta simples de entidades de teste. Para reduzir a largura de banda para renderizar a página inicial, uma projeção pode ser usada em contagens de sucesso e de falha para omitir a lista potencialmente longa de IDs de casos de teste com falha e outros metadados usados pelos jobs de alerta.
- Execuções de teste. A consulta de entidades de execução de teste exige uma classificação na chave (carimbo de data/hora) e uma possível filtragem nas propriedades de execução de teste, como ID de build, contagem de aprovação etc. Ao executar uma consulta ancestral com uma chave de entidade de teste, a leitura é altamente consistente. Nesse ponto, todos os resultados do caso de teste podem ser recuperados usando a lista de IDs armazenados em uma propriedade de execução de teste. Isso também garante um resultado altamente consistente pela natureza das operações de recebimento de repositório de dados.
- Dados de criação de perfil e cobertura. A consulta de dados de perfil ou de cobertura associados a um teste pode ser feita sem recuperar outros dados de execução de teste (como outros dados de perfil/cobertura, dados de caso de teste etc.). Uma consulta ancestral que usa as chaves de entidade de teste e de execução de teste vai extrair todos os pontos de perfil registrados durante a execução do teste. Ao filtrar também o nome do ponto de perfil ou o nome do arquivo, uma única entidade de perfil ou cobertura pode ser extraída. Pela natureza das consultas de ancestral, essa operação é altamente consistente.
Para detalhes sobre a interface e capturas de tela desses padrões de dados em ação, consulte Interface do painel do VTS.