CodeGym /Java Course /All lectures for IT purposes /Come implementare ACID in un'applicazione: pratica

Come implementare ACID in un'applicazione: pratica

All lectures for IT purposes
Livello 1 , Lezione 270
Disponibile

8.1 ID transazione

È designato come XID o TxID (se c'è una differenza, dimmelo). I timestamp possono essere usati come TxID, che può giocare a favore se vogliamo ripristinare tutte le azioni a un certo punto nel tempo. Il problema può sorgere se il timestamp non è sufficientemente granulare, quindi le transazioni possono ottenere lo stesso ID.

Pertanto, l'opzione più affidabile è generare ID prodotto UUID univoci. In Python questo è molto semplice:

>>> import uuid 
>>> str(uuid.uuid4()) 
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0' 
>>> str(uuid.uuid4()) 
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba' 

C'è anche un'opzione per eseguire l'hashing di un set di dati che definiscono la transazione e utilizzare questo hash come TxID.

8.2 Tentativi

Se sappiamo che una determinata funzione o programma è idempotente, ciò significa che possiamo e dobbiamo provare a ripetere la sua chiamata in caso di errore. E dobbiamo solo essere preparati al fatto che alcune operazioni genereranno un errore: dato che le applicazioni moderne sono distribuite sulla rete e sull'hardware, l'errore non dovrebbe essere considerato un'eccezione, ma la norma. L'errore può verificarsi a causa di un arresto anomalo del server, errore di rete, congestione dell'applicazione remota. Come dovrebbe comportarsi la nostra applicazione? Esatto, prova a ripetere l'operazione.

Poiché un pezzo di codice può dire più di un'intera pagina di parole, usiamo un esempio per capire come dovrebbe idealmente funzionare l'ingenuo meccanismo di ripetizione dei tentativi. Lo dimostrerò utilizzando la libreria Tenacity (è così ben progettata che anche se non hai intenzione di usarla, l'esempio dovrebbe mostrarti come puoi progettare il meccanismo di ricorrenza):

import logging
import random
import sys
from tenacity import retry, stop_after_attempt, stop_after_delay, wait_exponential, retry_if_exception_type, before_log

logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)

@retry(
	stop=(stop_after_delay(10) | stop_after_attempt(5)),
	wait=wait_exponential(multiplier=1, min=4, max=10),
	retry=retry_if_exception_type(IOError),
	before=before_log(logger, logging.DEBUG)
)
def do_something_unreliable():
	if random.randint(0, 10) > 1:
    	raise IOError("Broken sauce, everything is hosed!!!111one")
	else:
    	return "Awesome sauce!"

print(do_something_unreliable.retry.statistics)

> Per ogni evenienza, dirò: \@retry(...) è una sintassi Python speciale chiamata "decoratore". È solo una funzione retry(...) che esegue il wrapping di un'altra funzione e fa qualcosa prima o dopo che è stata eseguita.

Come possiamo vedere, i tentativi possono essere progettati in modo creativo:

  • Puoi limitare i tentativi in ​​base al tempo (10 secondi) o al numero di tentativi (5).
  • Può essere esponenziale (ovvero 2 ** un numero crescente n ). o in qualche altro modo (ad esempio fisso) per aumentare il tempo tra i tentativi separati. La variante esponenziale è chiamata "collasso di congestione".
  • Puoi riprovare solo per determinati tipi di errori (IOError).
  • I tentativi di ripetizione possono essere preceduti o completati da alcune voci speciali nel registro.

Ora che abbiamo completato il corso per giovani combattenti e conosciamo gli elementi costitutivi di base di cui abbiamo bisogno per lavorare con le transazioni dal lato dell'applicazione, conosciamo due metodi che ci consentono di implementare le transazioni nei sistemi distribuiti.

8.3 Strumenti avanzati per gli amanti delle transazioni

Darò solo definizioni abbastanza generali, poiché questo argomento è degno di un ampio articolo separato.

Commit a due fasi (2pc) . 2pc ha due fasi: una fase di preparazione e una fase di commit. Durante la fase di preparazione, a tutti i microservizi verrà chiesto di prepararsi per alcune modifiche ai dati che possono essere eseguite in modo atomico. Una volta che sono tutti pronti, la fase di commit apporterà le modifiche effettive. Per coordinare il processo, è necessario un coordinatore globale, che blocca gli oggetti necessari, ovvero diventano inaccessibili per le modifiche fino a quando il coordinatore non li sblocca. Se un particolare microservizio non è pronto per le modifiche (ad esempio, non risponde), il coordinatore interromperà la transazione e avvierà il processo di rollback.

Perché questo protocollo è buono? Fornisce atomicità. Inoltre, garantisce l'isolamento durante la scrittura e la lettura. Ciò significa che le modifiche a una transazione non sono visibili agli altri fino a quando il coordinatore non conferma le modifiche. Ma queste proprietà hanno anche uno svantaggio: poiché questo protocollo è sincrono (bloccante), rallenta il sistema (nonostante il fatto che la stessa chiamata RPC sia piuttosto lenta). E ancora, c'è il pericolo di un blocco reciproco.

Saga . In questo modello, una transazione distribuita viene eseguita da transazioni locali asincrone in tutti i microservizi associati. I microservizi comunicano tra loro tramite un bus di eventi. Se un microservizio non riesce a completare la transazione locale, altri microservizi eseguiranno transazioni di compensazione per ripristinare le modifiche.

Il vantaggio di Saga è che nessun oggetto viene bloccato. Ma ci sono, ovviamente, dei lati negativi.

Saga è difficile da eseguire il debug, soprattutto quando sono coinvolti molti microservizi. Un altro svantaggio del pattern Saga è la mancanza di isolamento in lettura. Cioè, se le proprietà indicate in ACID sono importanti per noi, allora Saga non è molto adatta a noi.

Cosa vediamo dalla descrizione di queste due tecniche? Il fatto che nei sistemi distribuiti la responsabilità dell'atomicità e dell'isolamento ricade sull'applicazione. La stessa cosa accade quando si utilizzano database che non forniscono garanzie ACID. Cioè, cose come risoluzione dei conflitti, rollback, commit e liberare spazio ricadono sulle spalle dello sviluppatore.

8.4 Come faccio a sapere quando ho bisogno delle garanzie ACID?

Quando c'è un'alta probabilità che un certo insieme di utenti o processi lavorino contemporaneamente sugli stessi dati .

Scusate la banalità, ma un tipico esempio sono le transazioni finanziarie.

Quando l'ordine in cui vengono eseguite le transazioni è importante.

Immagina che la tua azienda stia per passare da FunnyYellowChat messenger a FunnyRedChat messenger, perché FunnyRedChat ti consente di inviare gif, ma FunnyYellowChat no. Ma non stai solo cambiando il messenger: stai migrando la corrispondenza della tua azienda da un messenger all'altro. Lo fai perché i tuoi programmatori erano troppo pigri per documentare programmi e processi da qualche parte centralmente, e invece hanno pubblicato tutto in canali diversi nel messenger. Sì, e i tuoi venditori hanno pubblicato i dettagli delle negoziazioni e degli accordi nello stesso posto. Insomma, tutta la vita della tua azienda è lì, e siccome nessuno ha il tempo di trasferire il tutto a un servizio di documentazione, e la ricerca di messaggistica istantanea funziona bene, hai deciso invece di sgombrare le macerie di copiare semplicemente tutto il messaggi in una nuova posizione. L'ordine dei messaggi è importante

A proposito, per la corrispondenza in un messenger, l'ordine è generalmente importante, ma quando due persone scrivono qualcosa nella stessa chat contemporaneamente, in generale non è così importante quale messaggio apparirà per primo. Quindi, per questo particolare scenario, ACID non sarebbe necessario.

Un altro possibile esempio è la bioinformatica. Non lo capisco affatto, ma presumo che l'ordine sia importante quando si decifra il genoma umano. Tuttavia, ho sentito che i bioinformatici generalmente usano alcuni dei loro strumenti per tutto, forse hanno i loro database.

Quando non puoi fornire a un utente o elaborare dati obsoleti.

E ancora: transazioni finanziarie. Ad essere sincero, non mi veniva in mente nessun altro esempio.

Quando le transazioni in sospeso sono associate a costi significativi. Immagina i problemi che possono sorgere quando un medico e un infermiere aggiornano entrambi la cartella clinica di un paziente e cancellano le modifiche reciproche allo stesso tempo, perché il database non è in grado di isolare le transazioni. Il sistema sanitario è un'altra area, oltre alla finanza, in cui le garanzie ACID tendono ad essere critiche.

8.5 Quando non ho bisogno di ACID?

Quando gli utenti aggiornano solo alcuni dei loro dati privati.

Ad esempio, un utente lascia commenti o note adesive su una pagina web. Oppure modifica i dati personali in un account personale con un fornitore di qualsiasi servizio.

Quando gli utenti non aggiornano affatto i dati, ma li integrano solo con quelli nuovi (aggiungi).

Ad esempio, un'applicazione in esecuzione che salva i dati sulle tue corse: quanto hai corso, per quanto tempo, percorso, ecc. Ogni nuova esecuzione è costituita da nuovi dati e quelli vecchi non vengono affatto modificati. Forse, in base ai dati, ottieni analisi e solo i database NoSQL sono utili per questo scenario.

Quando la logica aziendale non determina la necessità di un determinato ordine in cui vengono eseguite le transazioni.

Probabilmente, per un blogger di Youtube che raccoglie donazioni per la produzione di nuovo materiale durante la prossima diretta, non è così importante chi, quando e in quale ordine, gli ha buttato i soldi.

Quando gli utenti rimarranno sulla stessa pagina Web o finestra dell'applicazione per diversi secondi o addirittura minuti, e quindi vedranno in qualche modo dati obsoleti.

Teoricamente, si tratta di qualsiasi media di notizie online o dello stesso Youtube. O "Habr". Quando non ti interessa che le transazioni incomplete possano essere temporaneamente memorizzate nel sistema, puoi ignorarle senza alcun danno.

Se aggreghi dati da molte fonti e dati che vengono aggiornati con una frequenza elevata, ad esempio dati sull'occupazione dei parcheggi in una città che cambiano almeno ogni 5 minuti, in teoria non sarà un grosso problema per te se a un certo punto la transazione per uno dei parcheggi non andrà a buon fine. Anche se, ovviamente, dipende da cosa esattamente vuoi fare con questi dati.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION