¿Cómo escribo pruebas contra un servicio eventualmente consistente?


17

Estoy construyendo un servicio sobre el almacén de datos de Google App Engine, que eventualmente es un almacén de datos consistente. Para mi aplicación, esto está bien.

Sin embargo, estoy desarrollando pruebas que hacen cosas como PUT object y luego GET object y verificando propiedades en el objeto devuelto. Desafortunadamente, debido a que el almacén de datos es consistente, estas pruebas simples no son reproducibles.

¿Cómo se prueba un servicio eventualmente consistente?


2
¿Por qué estás probando, en primer lugar, esperar la reproducibilidad frente a un servicio externo?

... y qué estás tratando de probar? ¿tu codigo? o de Google?

55
Estoy probando todo el sistema. Es decir, son pruebas de integración, no pruebas unitarias.
Doug Richardson

3
How can I reproducibly test an eventually consistent service? No puedes. Debe eliminar la palabra "reproduciblemente" o la palabra "eventualmente"; No puedes tener ambos.
Robert Harvey

1
Si finalmente es consistente, ya sea reproducible o no, cualquier resultado será exitoso. Ya dijiste que está bien para tu aplicación, entonces, ¿qué estás probando realmente? La eventualidad? La integración con GAE? ¿Tu codigo?
Laiv

Respuestas:


16

Tenga en cuenta los requisitos no funcionales al diseñar sus pruebas funcionales: si su servicio tiene un requisito no funcional de "Consistente en x (segundos / minutos / etc.)", simplemente ejecute las solicitudes PUT, espere x, luego ejecute las solicitudes GET.

En ese momento, si los datos aún no han "llegado", puede considerar que la solicitud PUT no cumple con sus requisitos.


7

Realmente desea que sus pruebas sean rápidas y consistentes. Si comienza a crear pruebas que ocasionalmente pueden fallar debido a la consistencia eventual, ignorará la prueba cuando falle, y entonces ¿de qué sirve?

Cree un servicio falso que maneje las solicitudes PUT y GET, pero que tenga una operación adicional para que sea coherente. Su prueba es entonces:

datastore.do_put(myobj);
datastore.make_consistent();
validate(datastore.do_get(), myobj);

Esto le permite probar el comportamiento de su software cuando GET recupera con éxito el objeto PUT. También le permite probar el comportamiento de su software cuando GET no encuentra el objeto (o el objeto correcto) debido a que el servicio aún no es consistente. Solo deja de lado la llamada a make_consistent().

Todavía vale la pena realizar pruebas que interactúen con el servicio real, pero deberían ejecutarse fuera de su flujo de trabajo de desarrollo normal, ya que nunca serán 100% confiables (por ejemplo, si el servicio está inactivo). Estas pruebas deben usarse para:

  1. proporcionar métricas en el tiempo promedio y el peor de los casos entre un PUT y un GET posterior que se vuelve consistente y
  2. verifique que su servicio falso se comporte de manera similar al servicio real. Ver https://codewithoutrules.com/2016/07/31/verified-fakes/

6

OK entonces. "¿Qué estás probando?" Es la pregunta clave.

  • Estoy probando mi lógica interna de lo que sucede suponiendo que las cosas de Google funcionen

En este caso, debe burlarse de los servicios de Google y siempre devolver una respuesta.

  • Estoy probando que mi lógica puede hacer frente a los errores transitorios que sé que Google producirá

En este caso, debe burlarse de los servicios de Google y siempre devolver el error transitorio antes de la respuesta correcta

  • Estoy probando que mi producto realmente funcionará con el servicio real de google

Debe inyectar los servicios reales de google y ejecutar la prueba. ¡Pero! El código que está probando debe tener incorporado el manejo de error transitorio (reintento). Por lo tanto, debe obtener una respuesta consistente. (a menos que google se porte muy mal)


+1 para la sugerencia simulada: si pudiera, daría más votos positivos para las opciones adicionales.
mcottle

6

Use uno de los siguientes:

  • Después de PUT, vuelva a intentar GET N veces hasta el éxito. Falla si no hay éxito después de N intentos.
  • Dormir entre PUT y GET

Desafortunadamente, debes elegir valores mágicos (N o duración del sueño) para ambas técnicas.


1
¿Podría aclarar: son estas alternativas o complementarias? Creo que quieres decir que son alternativas, y así es como pienso en ellas. Pero tal vez me equivoque.
Robin Green el

1
Correcto, quise decir que fueran alternativas.
Doug Richardson el

2

Según tengo entendido, el almacén de datos de Google Cloud permite consultas tanto consistentes como eventualmente consistentes .

La compensación es que las consultas muy consistentes tienen una tasa de transmisión bastante limitada (algo con lo que puede vivir durante las pruebas).

Una posibilidad puede ser colocar sus consultas en el almacén de datos dentro de un contenedor que pueda permitir una fuerte consistencia para fines de prueba.

Por ejemplo, podría tener métodos llamados start_debug_strong_consistency()y end_debug_strong_consistency().

El método de inicio crearía una clave que se puede utilizar como clave ancestral para todas las consultas posteriores, y el método de finalización eliminaría la clave.

El único cambio en las consultas reales que está probando sería llamar setAncestor(your_debug_key)si esa clave existe.


1

Un enfoque, que es bueno en teoría pero que no siempre es práctico, es hacer que todas las operaciones de escritura en el sistema bajo prueba sean idempotentes . Eso significa que, suponiendo que su código de prueba pruebe las cosas en un orden secuencial fijo, puede volver a intentar todas las lecturas y todas las escrituras individualmente hasta que obtenga el resultado que espera, volviendo a intentarlo hasta que se exceda el tiempo de espera que define en el código de prueba. Es decir, haga la cosa A1, vuelva a intentar si es necesario hasta que el resultado sea B1, luego haga la cosa A2, vuelva a intentar si es necesario hasta que el resultado sea B2, y así sucesivamente.

¡Entonces no necesita molestarse en verificar las condiciones previas de las operaciones de escritura, porque las operaciones de escritura ya las verificarán por usted, y simplemente las vuelve a intentar hasta que tengan éxito!

Use los mismos tiempos de espera "predeterminados" tanto como sea posible, que se pueden aumentar si todo el sistema se vuelve más lento, y anule los valores predeterminados individualmente cuando vuelva a intentar operaciones particularmente lentas.


1

Un servicio como el almacén de datos de Google App Engine se basa en la replicación de datos en varios puntos de presencia (POP) distribuidos globalmente. Cualquier prueba de integración para un servicio eventualmente consistente es realmente una prueba de la tasa de replicación de ese servicio en su conjunto de POP. La velocidad a la que el contenido se distribuye a cada POP en un servicio dado no va a ser la misma para cada POP dentro del servicio, dependiendo de una serie de factores, como el método de replicación y varios problemas de transporte por Internet; estos son dos ejemplos que representan la mayoría de los informes en cualquier servicio de almacenamiento de datos consistente (al menos esa fue mi experiencia mientras trabajaba para un CDN importante).

Para probar de manera efectiva la replicación de un objeto en una plataforma determinada, debe configurar la prueba para solicitar el mismo objeto colocado recientemente de cada uno de los POP del servicio. Sugiero probar la lista de POP de una a cinco veces o hasta que todos los POP en su lista de COP informen que tienen el objeto. Aquí hay un conjunto de intervalos para realizar la prueba que puede ajustar libremente: 1, 5, 60 minutos, 12 horas, 25 horas después de colocarlo en el almacén de datos. La clave es registrar los resultados en cada intervalo para su posterior revisión y análisis a fin de tener una idea de la capacidad de un servicio determinado para replicar objetos globalmente. A menudo, los servicios del almacén de datos solo extraen una copia local a un POP una vez que se ha solicitado localmente [el enrutamiento se realiza a través del protocolo BGP, por lo que su prueba debe solicitar el objeto de cada POP específico para que sea globalmente válido para una plataforma dada] . En el caso del Almacén de datos de Google, estaría buscando configurar su prueba para consultar un objeto determinado desde "más de 70 puntos de presencia en 33 países"; probablemente tenga que obtener la lista de direcciones URL de direcciones específicas de POP del Soporte de Google [ref:https://cloud.google.com/about/locations/ ] o si Google está usando Fastly para la replicación, Fastly Support [ https://www.fastly.com/resources ].

Un par de ventajas de este método: 1) Obtendrá una idea de la plataforma de replicación de un servicio determinado, conocerá sus fortalezas y puntos débiles en su conjunto a escala global [como lo fue durante la prueba de integración]. 2) Para cualquier objeto que pruebe, tendrá una herramienta disponible para calentar el contenido [haga esa primera solicitud que crea la copia en un POP local determinado], lo que le proporciona una forma de garantizar que el contenido se distribuya globalmente antes de que sus clientes lo soliciten en cualquier parte de la tierra.


0

Tengo experiencia con el almacén de datos de Google App Engine. Ejecutando localmente, sorprendentemente, a menudo es más "eventual" que "consistente". El ejemplo más simple: crear una nueva entidad y luego recuperarla. A menudo, en los últimos 5 años, he visto que el SDK que se ejecuta localmente no encuentra la nueva entidad de inmediato, sino que la encuentra después de aproximadamente medio segundo.

Sin embargo, corriendo contra los servidores reales de Google, no he visto ese comportamiento. Intentan hacer que su cliente Datastore siempre se ejecute en el mismo servidor, por lo que, por lo general, cualquier cambio se refleja inmediatamente en las consultas.

Mi consejo para las pruebas de integración es ejecutarlas contra los servidores reales, y luego probablemente no necesite poner ninguna encuesta falsa o demoras para obtener sus resultados.


Si bien eso es conveniente, podría causar interrupciones sutiles que involucren múltiples servidores de aplicaciones que no se detectarán en sus pruebas de integración. ¡Supongo que finalmente hicieron que el servidor local fuera consistente por una buena razón!
Robin Green el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.