Her er en fejl, jeg har set ske to gange, i to forskellige motorer. Et eksportkørsel kører klokken 10:00 og registrerer det højeste tidsstempel for seneste ændring, den så. En transaktion, der begyndte klokken 09:58, er stadig åben, committer klokken 10:03 og bærer et tidsstempel fra det tidspunkt, den startede. Den næste eksport beder kun om rækker nyere end 10:00 og ser den aldrig. Overførselsstørrelsen var fremragende, og dataene var forkerte.

Følg den eksport, der mistede en række

Følg rækkefølgen af hændelser i stedet for SQL'en. Applikationen skriver rækkens ændringstid, når transaktionen begynder, ikke når den committer. Eksportøren læser et vægur-vandmærke, mens transaktionen stadig er åben. Derefter committer transaktionen med en ændringstid, der er ældre end det gemte vandmærke. Forbrugerens næste batch starter efter vandmærket, så rækken ligger bag cursoren og vil aldrig blive valgt igen.

Intet logger en fejl, fordi intet fejlede. Det er det, der gør denne type fejl dyr: delta-feedet er hurtigt, rækkeantal ser rimelige ud, og den manglende række dukker op uger senere som en afstemningsticket, som ingen kan genskabe.

Sekvensnumre har samme form for fejl af en anden grund. Et monotont ID fortæller dig rækkefølgen, hvori værdier blev tildelt, ikke rækkefølgen, hvori transaktioner committede. En transaktion, der tog et lavt nummer, kan committe efter en, der tog et højt nummer, så en cursor baseret på tildelingsrækkefølge kan også efterlade et hul.

Fem løfter, feedet skal indfri

Før du vælger en transport, bed kildesystemet om en kontrakt med fem klausuler. Hvis en klausul mangler, har du en heuristik og ikke et ændringsfeed.

  • Dækning: indsættelser, opdateringer og sletninger, hver med den stabile nøgle, der er nødvendig for at anvende den.
  • Grænse: hvad en cursorværdi betyder, og hvad en konsistent batch indeholder.
  • Konsistens: hvordan relaterede tabeller læses uden at blande inkompatible kildetilstande.
  • Opbevaring: hvor længe en afbrudt forbruger kan genoptage fra sin gemte position.
  • Geninitialisering: et understøttet fuldt snapshot til når den position ikke længere er gyldig.

En understøttet mekanisme kan opfylde alle fem. SQL Servers dokumentation om change tracking er et konkret offentliggjort eksempel: den definerer en mindste gyldig version, registrerer sletninger såvel som ændringer og beskriver en snapshot-transaktion, der spænder over validering, versionsfangst og de relaterede ændringsforespørgsler. Det er et eksempel på, hvad man skal kræve af en kilde, ikke syntaks, du kan indsætte i en anden motor.

Design kontrolpunktet som en del af pipelinen

Forbrugerens gemte position er den egentlige tilstandsmaskine, så skriv den som sådan. Cursoren må kun rykke frem gennem det workflow, der allerede har anvendt batchen og committet måltransaktionen.

cursor  = durable_checkpoint()          # last fully applied position
batch   = extract(cursor, upper_bound)  # one consistent batch
apply(batch)                            # idempotent at the target
commit()
checkpoint(batch.upper_bound)           # only now

Hvis leveringen fejler halvvejs, skal du afstemme den samme batch i stedet for at opfinde en ny kildeposition. Genudtræk med en friskere øvre grænse er, hvordan en delvis anvendelse bliver et permanent hul. Spejlreglen gælder også på den anden side: idempotent anvendelse ved målet forhindrer dubletter og gør intet for en ændring, som udtrækket aldrig så.

Sletninger kræver registreringer, ikke konklusioner

At sammenligne rækker, der stadig findes, kan ikke fortælle en forbruger, hvilke nøgler der forsvandt. En kilde, der aldrig registrerer sletninger, efterlader dig med to ufuldkomne muligheder: at holde en nøglehistorik længe nok til at diffe imod, eller at køre en eksplicit fuld afstemning efter en tidsplan. Begge er designvalg med en pris, og ingen af dem findes, medmindre nogen ejer den.

Opbevaring er samme slags beslutning. Hvert understøttet ændringsfeed har et vindue; angiv det i kontrakten, og advar, når en forbrugers kontrolpunkt nærmer sig det. En forbruger, der vågner efter et afbrud på to uger, bør læse en klar fejl "geninitialisering påkrævet" i stedet for stille at modtage en delvis batch.

Mål derefter, om deltaet var besværet værd

At falde tilbage til at sammenligne hver nøgle og hash er nogle gange rigtigt, og det er værd at vide, hvad det koster. Du overfører stadig information om hver række, hashes giver dig ikke et konsistent snapshot på tværs af tabeller, og et hash-skema bringer sine egne spørgsmål om repræsentation og kollisioner. Færre bytes med en verificerbar grænse er målet; færre bytes, der stille taber en committet ændring, er slet ikke et delta.

En kolonne for seneste ændring er et hint. En cursor, der overlever en sen commit, en sletning og et langt afbrud, er en kontrakt.

Relaterede artikler