8.1 Transactie-ID's
Het wordt aangeduid als XID of TxID (als er een verschil is, vertel het me). Tijdstempels kunnen worden gebruikt als TxID, wat in de kaart kan spelen als we alle acties naar een bepaald tijdstip willen herstellen. Het probleem kan ontstaan als de tijdstempel niet gedetailleerd genoeg is - dan kunnen transacties dezelfde ID krijgen.
Daarom is de meest betrouwbare optie het genereren van unieke UUID-product-ID's. In Python is dit heel eenvoudig:
>>> import uuid
>>> str(uuid.uuid4())
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0'
>>> str(uuid.uuid4())
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba'
Er is ook een optie om een set transactiebepalende gegevens te hashen en deze hash als TxID te gebruiken.
8.2 Nieuwe pogingen
Als we weten dat een bepaalde functie of programma idempotent is, betekent dit dat we kunnen en moeten proberen de aanroep ervan te herhalen in geval van een fout. En we moeten er gewoon op voorbereid zijn dat een of andere bewerking een fout zal geven - aangezien moderne applicaties over het netwerk en hardware worden gedistribueerd, moet de fout niet als een uitzondering worden beschouwd, maar als de norm. De fout kan optreden als gevolg van een servercrash, netwerkfout, congestie van externe applicaties. Hoe moet onze applicatie zich gedragen? Dat klopt, probeer de operatie te herhalen.
Aangezien één stuk code meer kan zeggen dan een hele pagina met woorden, laten we een voorbeeld gebruiken om te begrijpen hoe het naïeve mechanisme voor opnieuw proberen idealiter zou moeten werken. Ik zal dit demonstreren met behulp van de Tenacity-bibliotheek (deze is zo goed ontworpen dat zelfs als je niet van plan bent het te gebruiken, het voorbeeld je zou moeten laten zien hoe je het herhalingsmechanisme kunt ontwerpen):
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)
> Voor het geval dat, zeg ik: \@retry(...) is een speciale Python-syntaxis die een "decorator" wordt genoemd. Het is gewoon een retry(...) functie die een andere functie inpakt en iets doet voordat of nadat het is uitgevoerd.
Zoals we kunnen zien, kunnen nieuwe pogingen creatief worden ontworpen:
- U kunt pogingen beperken op tijd (10 seconden) of aantal pogingen (5).
- Kan exponentieel zijn (dat wil zeggen 2 ** een oplopend getal n ). of op een andere manier (bijvoorbeeld opgelost) om de tijd tussen afzonderlijke pogingen te verlengen. De exponentiële variant wordt "instorting van congestie" genoemd.
- U kunt het alleen opnieuw proberen voor bepaalde soorten fouten (IOError).
- Nieuwe pogingen kunnen worden voorafgegaan of voltooid door enkele speciale vermeldingen in het logboek.
Nu we de Young Fighter-cursus hebben voltooid en de basisbouwstenen kennen die we nodig hebben om met transacties aan de applicatiekant te werken, laten we kennis maken met twee methoden waarmee we transacties in gedistribueerde systemen kunnen implementeren.
8.3 Geavanceerde tools voor liefhebbers van transacties
Ik zal alleen vrij algemene definities geven, aangezien dit onderwerp een apart groot artikel waard is.
Commit in twee fasen (2 stuks) . 2pc kent twee fasen: een voorbereidingsfase en een commit-fase. Tijdens de voorbereidingsfase wordt aan alle microservices gevraagd om zich voor te bereiden op enkele gegevenswijzigingen die atomair kunnen worden uitgevoerd. Zodra ze allemaal klaar zijn, zal de commit-fase de daadwerkelijke wijzigingen aanbrengen. Om het proces te coördineren is een globale coördinator nodig, die de benodigde objecten vergrendelt - dat wil zeggen dat ze ontoegankelijk worden voor wijzigingen totdat de coördinator ze ontgrendelt. Als een bepaalde microservice niet klaar is voor wijzigingen (bijvoorbeeld niet reageert), breekt de coördinator de transactie af en begint het terugdraaiproces.
Waarom is dit protocol goed? Het zorgt voor atomiciteit. Bovendien garandeert het isolatie bij het schrijven en lezen. Dit betekent dat wijzigingen in één transactie niet zichtbaar zijn voor anderen totdat de coördinator de wijzigingen doorvoert. Maar deze eigenschappen hebben ook een nadeel: aangezien dit protocol synchroon is (blokkerend), vertraagt het het systeem (ondanks het feit dat de RPC-aanroep zelf vrij traag is). En weer dreigt het gevaar van wederzijdse blokkering.
Saga . In dit patroon wordt een gedistribueerde transactie uitgevoerd door asynchrone lokale transacties over alle bijbehorende microservices. Microservices communiceren met elkaar via een eventbus. Als een microservice zijn lokale transactie niet voltooit, voeren andere microservices compenserende transacties uit om de wijzigingen ongedaan te maken.
Het voordeel van Saga is dat er geen objecten worden geblokkeerd. Maar er zijn natuurlijk ook nadelen.
Saga is moeilijk te debuggen, vooral als er veel microservices bij betrokken zijn. Een ander nadeel van het Saga-patroon is dat het geen leesisolatie heeft. Dat wil zeggen, als de eigenschappen aangegeven in ACID belangrijk voor ons zijn, dan is Saga niet erg geschikt voor ons.
Wat zien we uit de beschrijving van deze twee technieken? Het feit dat in gedistribueerde systemen de verantwoordelijkheid voor atomiciteit en isolatie bij de toepassing ligt. Hetzelfde gebeurt bij het gebruik van databases die geen ACID-garanties bieden. Dat wil zeggen, zaken als conflictoplossing, rollbacks, commits en het vrijmaken van ruimte vallen op de schouders van de ontwikkelaar.
8.4 Hoe weet ik wanneer ik ACID-garanties nodig heb?
Wanneer de kans groot is dat een bepaalde set gebruikers of processen gelijktijdig aan dezelfde gegevens zal werken .
Sorry voor de banaliteit, maar een typisch voorbeeld zijn financiële transacties.
Wanneer de volgorde waarin transacties worden uitgevoerd ertoe doet.
Stel je voor dat je bedrijf op het punt staat over te stappen van FunnyYellowChat messenger naar FunnyRedChat messenger, want met FunnyRedChat kun je gifs versturen, maar met FunnyYellowChat niet. Maar u verandert niet alleen de boodschapper - u migreert de correspondentie van uw bedrijf van de ene boodschapper naar de andere. Je doet dit omdat je programmeurs te lui waren om programma's en processen ergens centraal te documenteren, en in plaats daarvan alles in verschillende kanalen in de messenger publiceerden. Ja, en uw verkopers hebben de details van onderhandelingen en overeenkomsten op dezelfde plaats gepubliceerd. Kortom, het hele leven van uw bedrijf is er, en aangezien niemand tijd heeft om het hele ding over te dragen aan een dienst voor documentatie, en het zoeken naar instant messengers goed werkt, besloot u in plaats van het puin op te ruimen om gewoon alle berichten naar een nieuwe locatie. De volgorde van de berichten is belangrijk
Trouwens, voor correspondentie in een messenger is de volgorde over het algemeen belangrijk, maar als twee mensen tegelijkertijd iets in dezelfde chat schrijven, dan is het over het algemeen niet zo belangrijk wiens bericht als eerste verschijnt. Dus voor dit specifieke scenario zou ACID niet nodig zijn.
Een ander mogelijk voorbeeld is bioinformatica. Ik begrijp hier helemaal niets van, maar ik neem aan dat ordening belangrijk is bij het ontcijferen van het menselijk genoom. Ik heb echter gehoord dat bio-informatici over het algemeen een deel van hun tools voor alles gebruiken - misschien hebben ze hun eigen databases.
Wanneer u een gebruiker of proces geen verouderde gegevens kunt geven.
En nogmaals - financiële transacties. Ik zou eerlijk gezegd geen ander voorbeeld kunnen bedenken.
Wanneer lopende transacties gepaard gaan met aanzienlijke kosten. Stelt u zich de problemen voor die kunnen ontstaan wanneer een arts en een verpleegkundige tegelijkertijd een patiëntendossier bijwerken en elkaars wijzigingen wissen, omdat de database transacties niet kan isoleren. Het gezondheidszorgsysteem is een ander gebied, naast financiën, waar ACID-garanties vaak van cruciaal belang zijn.
8.5 Wanneer heb ik ACID niet nodig?
Wanneer gebruikers slechts enkele van hun privégegevens bijwerken.
Een gebruiker laat bijvoorbeeld opmerkingen of plaknotities achter op een webpagina. Of persoonsgegevens bewerkt in een persoonlijk account bij een aanbieder van eventuele diensten.
Wanneer gebruikers gegevens helemaal niet bijwerken, maar alleen aanvullen met nieuwe (toevoegen).
Bijvoorbeeld een hardloopapplicatie die gegevens over je hardloopsessies opslaat: hoeveel je hebt hardgelopen, hoe laat, route, etc. Elke nieuwe run is nieuwe data en de oude worden helemaal niet bewerkt. Misschien krijg je op basis van de gegevens analyses - en alleen NoSQL-databases zijn goed voor dit scenario.
Wanneer bedrijfslogica niet de noodzaak bepaalt van een bepaalde volgorde waarin transacties worden uitgevoerd.
Waarschijnlijk is het voor een YouTube-blogger die donaties inzamelt voor de productie van nieuw materiaal tijdens de volgende live-uitzending, niet zo belangrijk wie, wanneer en in welke volgorde hem geld heeft gegooid.
Wanneer gebruikers enkele seconden of zelfs minuten op dezelfde webpagina of hetzelfde applicatievenster blijven en daarom op de een of andere manier verouderde gegevens zien.
Theoretisch zijn dit alle online nieuwsmedia of dezelfde YouTube. Of "Habr". Wanneer het u niets uitmaakt dat onvolledige transacties tijdelijk in het systeem kunnen worden opgeslagen, kunt u deze zonder schade negeren.
Als u gegevens uit vele bronnen verzamelt en gegevens die met een hoge frequentie worden bijgewerkt, bijvoorbeeld gegevens over de bezetting van parkeerplaatsen in een stad die minstens elke 5 minuten verandert, dan zal het in theorie geen groot probleem zijn voor u als op een gegeven moment de transactie voor een van de parkeerplaatsen niet doorgaat. Hoewel het natuurlijk afhangt van wat je precies met deze gegevens wilt doen.
GO TO FULL VERSION