Come faccio a scrivere test su un servizio eventualmente coerente?


17

Sto creando un servizio su Google App Engine Datastore, che è un archivio dati alla fine coerente. Per la mia applicazione, va bene.

Tuttavia, sto sviluppando test che fanno cose come l'oggetto PUT e poi GET object e controllando le proprietà sull'oggetto restituito. Sfortunatamente, poiché il datastore è infine coerente, questi semplici test non sono riproducibili.

Come testare un servizio eventualmente coerente?


2
Perché in primo luogo stai testando di aspettarti la riproducibilità rispetto a un servizio esterno?

... e cosa stai davvero cercando di testare? il tuo codice? o di Google?

5
Sto testando l'intero sistema. Cioè, sono test di integrazione, non test unitari.
Doug Richardson,

3
How can I reproducibly test an eventually consistent service? - Non puoi. Devi rimuovere la parola "riproducibile" o la parola "eventualmente;" non puoi avere entrambi.
Robert Harvey,

1
Se alla fine è coerente, sia che sia riproducibile o meno, qualsiasi risultato avrà successo. Hai già detto che va bene per la tua app, quindi cosa stai testando davvero? L'eventualità? L'integrazione con GAE? Il tuo codice?
Laiv

Risposte:


16

Considerare i requisiti non funzionali durante la progettazione dei test funzionali - se il servizio ha un requisito non funzionale di "Coerente entro x (secondi / minuti / ecc.)", Eseguire semplicemente le richieste PUT, attendere x, quindi eseguire le richieste GET.

A quel punto, se i dati non sono ancora "arrivati", puoi considerare la richiesta PUT non conforme ai tuoi requisiti.


7

Vuoi davvero che i tuoi test siano veloci e coerenti. Se inizi a creare test che potrebbero occasionalmente fallire a causa di un'eventuale coerenza, ignorerai il test quando fallisce, e quindi a che serve?

Crea un servizio falso che gestisca le richieste PUT e GET, ma abbia un'operazione aggiuntiva per renderlo coerente. Il tuo test è quindi:

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

Ciò consente di testare il comportamento del software quando GET recupera correttamente l'oggetto PUT. Consente inoltre di testare il comportamento del software quando GET non trova l'oggetto (o l'oggetto corretto) a causa del servizio non ancora coerente. Basta lasciare la chiamata a make_consistent().

Vale comunque la pena avere test che interagiscono con il servizio reale, ma dovrebbero essere eseguiti al di fuori del normale flusso di lavoro di sviluppo, poiché non saranno mai affidabili al 100% (ad esempio se il servizio non è attivo). Questi test dovrebbero essere usati per:

  1. fornire metriche nel tempo medio e nel caso peggiore tra un PUT e un GET successivo che diventi coerente; e
  2. verifica che il tuo servizio falso si comporti in modo simile al servizio reale. Vedi https://codewithoutrules.com/2016/07/31/verified-fakes/

6

Ok allora. "Cosa stai testando" è la domanda chiave.

  • Sto testando la mia logica interna di ciò che accade supponendo che la roba di google funzioni

In questo caso dovresti deridere i servizi di Google e restituire sempre una risposta.

  • Sto testando la mia logica in grado di far fronte agli errori temporanei che so che Google produrrà

In questo caso dovresti prendere in giro i servizi di Google e restituire sempre l'errore temporaneo prima della risposta corretta

  • Sto testando che il mio prodotto funzionerà effettivamente con il vero servizio Google

Dovresti iniettare i veri servizi di Google ed eseguire il test. Ma! Il codice che stai testando dovrebbe contenere la gestione degli errori temporanei (riprova). Quindi dovresti ottenere una risposta coerente. (a meno che google non si comporti molto male)


+1 per il suggerimento Mock - Se potessi, darei più voti positivi per le opzioni aggiuntive.
mcottle

6

Utilizzare uno dei seguenti:

  • Dopo PUT, riprova GET N volte fino al successo. Fallire se non si riesce dopo che N prova.
  • Dormi tra PUT e GET

Sfortunatamente, devi scegliere valori magici (N o durata del sonno) per entrambe queste tecniche.


1
Potresti chiarire: sono queste alternative o complementari? Penso che intendi dire che sono alternative - ed è così che penso a loro. Ma forse mi sbaglio.
Robin Green,

1
Corretto, intendevo che fossero alternative.
Doug Richardson,

2

A quanto mi risulta, il datastore di Google Cloud consente query sia fortemente coerenti che eventualmente coerenti .

Il compromesso è che le query fortemente coerenti sono piuttosto severamente limitate (qualcosa con cui puoi convivere durante i test).

Una possibilità potrebbe essere quella di mettere le tue query nell'archivio dati all'interno di un wrapper che può consentire una forte coerenza a scopo di test.

Ad esempio, potresti avere metodi chiamati start_debug_strong_consistency()e end_debug_strong_consistency().

Il metodo start creerebbe una chiave che può essere utilizzata come chiave antenata per tutte le query successive e il metodo end eliminerebbe la chiave.

L'unica modifica alle query effettive che stai testando sarebbe quella di chiamare setAncestor(your_debug_key)se quel tasto esiste.


1

Un approccio, che è bello in teoria ma potrebbe non essere sempre pratico, è rendere idempotenti tutte le operazioni di scrittura nel sistema sotto test . Ciò significa che, supponendo che il codice di test verifichi le cose in un ordine sequenziale fisso, è possibile riprovare tutte le letture e tutte le scritture singolarmente fino a ottenere il risultato previsto, riprovare fino a quando non viene superato un timeout definito nel codice di test. Cioè, fai la cosa A1, riprova se necessario fino a quando il risultato è B1, quindi fai la cosa A2, riprova se necessario fino a quando il risultato è B2, e così via.

Quindi non devi preoccuparti di controllare i presupposti delle operazioni di scrittura, perché le operazioni di scrittura li controlleranno già per te, e dovrai semplicemente riprovare finché non avranno successo!

Utilizzare gli stessi timeout "predefiniti" il più possibile, che possono essere aumentati se l'intero sistema diventa più lento e sovrascrivere i valori predefiniti singolarmente quando si riprovano operazioni particolarmente lente.


1

Un servizio come Google App Engine Datastore si basa sulla replica dei dati in diversi punti di presenza (POP) diffusi a livello globale. Qualsiasi test di integrazione per un servizio eventualmente coerente è in realtà un test della velocità di replica di quel servizio attraverso la sua serie di POP. La velocità con cui il contenuto viene distribuito a ciascun POP in un determinato servizio non sarà la stessa per ogni POP all'interno del servizio a seconda di una serie di fattori, come il metodo di replica e vari problemi di trasporto su Internet: questi sono due esempi che rappresentano la maggior parte dei report in qualsiasi servizio di archivio dati eventualmente coerente (almeno questa è stata la mia esperienza mentre stavo lavorando per una CDN importante).

Per testare efficacemente la replica di un oggetto su una determinata piattaforma, è necessario impostare il test in modo che richieda lo stesso oggetto inserito di recente da ciascuno specifico POP del servizio. Sto suggerendo di testare l'elenco POP da una a cinque volte o fino a quando tutti i POP nell'elenco POP non segnalano di avere l'oggetto. Ecco una serie di intervalli in cui eseguire il test che si è liberi di regolare: 1, 5, 60 minuti, 12 ore, 25 ore dopo averlo inserito nell'archivio dati. La chiave sta registrando i risultati ad ogni intervallo per successive revisioni e analisi al fine di avere un'idea della capacità di un determinato servizio di replicare globalmente gli oggetti. Spesso i servizi di archivio dati eseguono il pull di una copia locale su un POP una volta che è stato richiesto localmente [l'instradamento viene eseguito tramite protocollo BGP, motivo per cui il test deve richiedere l'oggetto da ciascun POP specifico affinché sia ​​globalmente valido per una determinata piattaforma] . Nel caso di Google Datastore dovresti cercare di impostare il tuo test per interrogare un determinato oggetto da "oltre 70 punti di presenza in 33 paesi"; probabilmente dovresti ottenere l'elenco di URL dell'indirizzo specifico POP dall'assistenza Google [rif:https://cloud.google.com/about/locations/ ] o se Google sta utilizzando Velocemente per la replica, Velocemente Supporto [ https://www.fastly.com/resources ].

Un paio di vantaggi di questo metodo: 1) Avrai un'idea della piattaforma di replica di un determinato servizio, conoscerai i suoi punti di forza e punti deboli nel suo complesso su scala globale [come era durante il test di integrazione]. 2) Per qualsiasi oggetto testato avrai a disposizione uno strumento per riscaldare il contenuto [fai quella prima richiesta che crea la copia in un dato POP locale] - fornendo così un modo per garantire che il contenuto sia diffuso a livello globale prima che i tuoi clienti lo richiedano da ovunque sulla terra.


0

Ho esperienza con Google App Engine Datastore. Funzionando localmente, sorprendentemente, spesso è più "eventualmente" che "coerente". L'esempio più semplice: creare una nuova entità, quindi recuperarla. Spesso negli ultimi 5 anni ho visto che l'SDK localmente non trovava immediatamente la nuova entità, ma la trovava dopo circa mezzo secondo.

Tuttavia, in esecuzione contro i veri server di Google, non ho visto quel comportamento. Tentano di far funzionare sempre il tuo client Datastore sullo stesso server dalla loro parte, quindi di solito le modifiche si riflettono immediatamente nelle query.

Il mio consiglio per i test di integrazione è di eseguirli su server reali, e quindi probabilmente non dovrai mettere alcun polling falso o ritardo per ottenere i tuoi risultati.


Sebbene ciò sia conveniente, potrebbe causare impercettibili interruzioni che coinvolgono più server applicazioni nei test di integrazione. Immagino che alla fine abbiano reso coerente il server locale per una buona ragione!
Robin Green,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.