Comment écrire des tests sur un service éventuellement cohérent?


17

Je crée un service au-dessus de Google App Engine Datastore, qui est un magasin de données finalement cohérent. Pour mon application, c'est très bien.

Cependant, je développe des tests qui font des choses comme PUT object puis GET object et vérifiant les propriétés sur l'objet retourné. Malheureusement, parce que le magasin de données est finalement cohérent, ces tests simples ne sont pas reproductibles.

Comment testez-vous un service éventuellement cohérent?


2
Pourquoi testez-vous en premier lieu la reproductibilité par rapport à un service externe?

... et qu'essayez-vous réellement de tester? votre code? ou de Google?

5
Je teste l'ensemble du système. Ce sont des tests d'intégration, pas des tests unitaires.
Doug Richardson

3
How can I reproducibly test an eventually consistent service? - Tu ne peux pas. Vous devez supprimer le mot "reproductiblement" ou le mot "éventuellement"; vous ne pouvez pas avoir les deux.
Robert Harvey

1
S'il est finalement cohérent, qu'il soit reproductible ou non, tout résultat sera un succès. Vous avez déjà dit que cela convenait à votre application, alors que testez-vous vraiment? L'éventualité? L'intégration avec GAE? Votre code?
Laiv

Réponses:


16

Tenez compte des exigences non fonctionnelles lors de la conception de vos tests fonctionnels - si votre service a une exigence non fonctionnelle de "Cohérente dans x (secondes / minutes / etc)", exécutez simplement les demandes PUT, attendez x, puis exécutez les demandes GET.

À ce stade, si les données ne sont pas encore «arrivées», vous pouvez considérer que la demande PUT n'est pas conforme à vos besoins.


7

Vous voulez vraiment que vos tests soient rapides et cohérents. Si vous commencez à créer des tests qui peuvent parfois échouer en raison d'une cohérence éventuelle, vous ignorerez le test lorsqu'il échoue, et à quoi sert-il?

Créez un faux service qui gère les requêtes PUT et GET, mais a une opération supplémentaire pour le rendre cohérent. Votre test est alors:

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

Cela vous permet de tester le comportement de votre logiciel lorsque le GET récupère correctement l'objet PUT. Il vous permet également de tester le comportement de votre logiciel lorsque le GET ne trouve pas l'objet (ou l'objet correct) car le service n'est pas encore cohérent. Laissez simplement de côté l'appel make_consistent().

Il vaut toujours la peine d'avoir des tests qui interagissent avec le service réel, mais ils doivent s'exécuter en dehors de votre flux de travail de développement normal, car ils ne seront jamais fiables à 100% (par exemple, si le service est en panne). Ces tests doivent être utilisés pour:

  1. fournir des métriques en moyenne et le pire des cas entre un PUT et un GET ultérieur devenant cohérent; et
  2. vérifiez que votre faux service se comporte de la même manière que le vrai service. Voir https://codewithoutrules.com/2016/07/31/verified-fakes/

6

D'accord alors. "Que testez-vous" est la question clé.

  • Je teste ma logique interne de ce qui se passe en supposant que les trucs google fonctionnent

Dans ce cas, vous devez vous moquer des services google et toujours renvoyer une réponse.

  • Je teste ma logique pour faire face aux erreurs transitoires que je sais que google produira

Dans ce cas, vous devez vous moquer des services google et toujours renvoyer l'erreur transitoire avant la réponse correcte

  • Je teste que mon produit fonctionnera réellement avec le vrai service google

Vous devez injecter les vrais services google et exécuter le test. Mais! Le code que vous testez doit avoir la gestion des erreurs transitoires (réessayer) intégrée. Vous devriez donc obtenir une réponse cohérente. (sauf si google se comporte très mal)


+1 pour la suggestion Mock - je donnerais plus de votes positifs pour les options supplémentaires si je le pouvais.
mcottle

6

Utilisez l'une des options suivantes:

  • Après PUT, réessayez GET N fois jusqu'à la réussite. Échec si aucun succès après N essais.
  • Dormez entre PUT et GET

Malheureusement, vous devez choisir des valeurs magiques (N ou durée du sommeil) pour ces deux techniques.


1
Pourriez-vous préciser: ces alternatives sont-elles ou complémentaires? Je pense que vous voulez dire que ce sont des alternatives - et c'est comme ça que je pense à elles. Mais je me trompe peut-être.
Robin Green

1
Exact, je voulais dire qu'ils étaient des alternatives.
Doug Richardson

2

Si je comprends bien, la banque de données Google Cloud permet à la fois des requêtes fortement cohérentes et éventuellement cohérentes .

Le compromis est que les requêtes fortement cohérentes sont assez sévèrement limitées en taux (quelque chose avec lequel vous pouvez vivre pendant les tests).

Une possibilité peut être de placer vos requêtes dans la banque de données dans un wrapper qui peut permettre une forte cohérence à des fins de test.

Par exemple, vous pourriez avoir des méthodes appelées start_debug_strong_consistency()et end_debug_strong_consistency().

La méthode de début créerait une clé qui peut être utilisée comme clé d'ancêtre pour toutes les requêtes suivantes, et la méthode de fin supprimerait la clé.

La seule modification des requêtes que vous testez serait d'appeler setAncestor(your_debug_key)si cette clé existe.


1

Une approche, intéressante en théorie mais pas toujours pratique, consiste à effectuer toutes les opérations d'écriture dans le système testé idempotentes . Cela signifie que, en supposant que votre code de test teste les choses dans un ordre séquentiel fixe, vous pouvez réessayer toutes les lectures et toutes les écritures individuellement jusqu'à ce que vous obteniez le résultat attendu, en recommençant jusqu'à ce que le délai que vous définissez dans le code de test soit dépassé. C'est-à-dire, faire la chose A1, réessayer si nécessaire jusqu'à ce que le résultat soit B1, puis faire la chose A2, réessayer si nécessaire jusqu'à ce que le résultat soit B2, et ainsi de suite.

Ensuite, vous n'avez pas besoin de vous soucier de vérifier les conditions préalables des opérations d'écriture, car les opérations d'écriture les vérifieront déjà pour vous, et vous devrez simplement les réessayer jusqu'à ce qu'elles réussissent!

Utilisez autant que possible les mêmes délais d'expiration "par défaut", qui peuvent être augmentés si l'ensemble du système devient plus lent, et remplacez les valeurs par défaut individuellement lors d'une nouvelle tentative d'opérations particulièrement lentes.


1

Un service tel que Google App Engine Datastore est basé sur la réplication des données à travers plusieurs points de présence répartis mondialement (POP). Tout test d'intégration pour un service éventuellement cohérent est vraiment un test du taux de réplication de ce service sur son ensemble de POP. Le taux auquel le contenu est diffusé à chaque POP dans un service donné ne sera pas le même pour chaque POP au sein du service en fonction d'un certain nombre de facteurs, tels que la méthode de réplication et divers problèmes de transport Internet - ce sont deux exemples qui représentent la majorité des rapports dans tout service de magasin de données finalement cohérent (du moins, c'était mon expérience alors que je travaillais pour un CDN majeur).

Afin de tester efficacement la réplication d'un objet sur une plate-forme donnée, vous devez définir le test pour demander le même objet récemment placé à partir de chacun des POP du service. Je suggère de tester la liste des POP une à cinq fois ou jusqu'à ce que tous les POP de votre liste de POP signalent avoir l'objet. Voici un ensemble d'intervalles pour effectuer le test que vous êtes libre de régler: 1, 5, 60 minutes, 12 heures, 25 heures après l'avoir placé sur le magasin de données. La clé consiste à consigner les résultats à chaque intervalle pour un examen et une analyse ultérieurs afin d'avoir une idée de la capacité d'un service donné à répliquer des objets à l'échelle mondiale. Souvent, les services de banque de données ne tirent une copie locale vers un POP qu'une fois qu'elle a été demandée localement [le routage est effectué via le protocole BGP, c'est pourquoi votre test doit demander l'objet à chaque POP spécifique pour qu'il soit globalement valide pour une plate-forme donnée] . Dans le cas du magasin de données de Google, vous envisagez de configurer votre test pour interroger un objet donné à partir de "plus de 70 points de présence dans 33 pays"; vous devrez probablement obtenir la liste des adresses URL spécifiques à POP auprès de l'assistance Google [réf:https://cloud.google.com/about/locations/ ] ou si Google utilise Fastly pour la réplication, Fastly Support [ https://www.fastly.com/resources ].

Quelques avantages de cette méthode: 1) Vous aurez une idée de la plate-forme de réplication d'un service donné, connaître ses points forts et points faibles dans son ensemble à l'échelle mondiale [comme lors du test d'intégration]. 2) Quel que soit l'objet que vous testez, vous disposez d'un outil pour réchauffer le contenu [faites cette première demande qui crée la copie à un POP local donné] - vous offrant ainsi un moyen de vous assurer que le contenu est diffusé dans le monde entier avant que vos clients ne le demandent à n'importe où sur terre.


0

J'ai de l'expérience avec Google App Engine Datastore. Fonctionnant localement, de manière surprenante, il est souvent plus "finalement" que "cohérent". L'exemple le plus simple: créez une nouvelle entité, puis récupérez-la. Souvent, au cours des 5 dernières années, j'ai vu le SDK exécuté localement ne pas trouver la nouvelle entité immédiatement, mais la trouver après environ une demi-seconde.

Cependant, contre les vrais serveurs Google, je n'ai pas vu ce comportement. Ils essaient de faire en sorte que votre client Datastore s'exécute toujours sur le même serveur de leur côté, donc généralement toutes les modifications sont immédiatement reflétées dans les requêtes.

Mon conseil pour les tests d'intégration est de les exécuter sur les vrais serveurs, et vous n'aurez probablement pas besoin de faire de faux sondages ou de retards pour obtenir vos résultats.


Bien que cela soit pratique, cela pourrait entraîner des ruptures subtiles impliquant plusieurs serveurs d'applications non détectées dans vos tests d'intégration. Je suppose qu'ils ont finalement rendu le serveur local cohérent pour une bonne raison!
Robin Green
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.