El teu flux delta perd commits tardans: construeix un cursor fiable
Una fila va arribar tard perquè la marca d'aigua no era una frontera de commit. Recorre la fallada i després defineix cobertura, frontera, retenció i una represa segura.
Aquí hi ha una fallada que he vist passar dues vegades, en dos motors diferents. Una exportació s'executa a les 10:00 i registra la marca de temps de darrera modificació més alta que ha vist. Una transacció que va començar a les 09:58 encara és oberta, fa commit a les 10:03 i porta una marca de temps del moment en què va començar. La següent exportació només demana files més noves que les 10:00 i no la veu mai. La mida de la transferència era excel·lent i les dades eren incorrectes.
Recorre l'exportació que va perdre una fila
Ressegueix l'ordre dels esdeveniments, no l'SQL. L'aplicació escriu el temps de modificació de la fila quan comença la transacció, no quan fa commit. L'exportador llegeix una marca d'aigua de rellotge de paret mentre la transacció encara és oberta. Després la transacció fa commit amb un temps de modificació més antic que la marca desada. El següent lot del consumidor comença després de la marca, així que la fila queda darrere del cursor i no es tornarà a seleccionar mai.
Res no registra cap error, perquè res no ha fallat. Això és el que fa tan car aquest tipus d'error: el flux delta és ràpid, els recomptes de files semblen raonables i la fila que falta apareix setmanes després com un tiquet de reconciliació que ningú no pot reproduir.
Els números de seqüència tenen la mateixa forma de defecte per una raó diferent. Un ID monòton et diu l'ordre en què es van assignar els valors, no l'ordre en què les transaccions van fer commit. Una transacció que va agafar un número baix pot fer commit després d'una que va agafar un número alt, així que un cursor basat en l'ordre d'assignació també pot deixar un forat.
Cinc promeses que el flux ha de complir
Abans de triar un transport, demana al sistema d'origen un contracte amb cinc clàusules. Si en falta alguna, tens una heurística i no un flux de canvis.
- Cobertura: insercions, actualitzacions i supressions, cadascuna amb la clau estable necessària per aplicar-la.
- Frontera: què significa un valor de cursor i què conté un lot coherent.
- Coherència: com es llegeixen les taules relacionades sense barrejar estats d'origen incompatibles.
- Retenció: quant de temps pot reprendre un consumidor desconnectat des de la seva posició desada.
- Reinicialització: una instantània completa suportada per quan aquella posició ja no sigui vàlida.
Un mecanisme suportat pot satisfer les cinc. La documentació de SQL Server sobre el seguiment de canvis és un exemple publicat concret: defineix una versió mínima vàlida, registra les supressions així com els canvis i descriu una transacció d'instantània que abasta la validació, la captura de versió i les consultes de canvis relacionades. És un exemple del que cal exigir a una font, no una sintaxi que puguis enganxar en un altre motor.
Dissenya el punt de control com a part del pipeline
La posició desada del consumidor és la màquina d'estats real, així que escriu-la com a tal. El cursor només pot avançar a través del flux de treball que ja ha aplicat el lot i ha fet commit de la transacció de destinació.
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
Si el lliurament falla a mig camí, reconcilia aquell mateix lot en lloc d'inventar una posició d'origen nova. Reextreure amb una frontera superior més fresca és com una aplicació parcial es converteix en un buit permanent. La regla mirall també val a l'altre costat: l'aplicació idempotent a la destinació evita duplicats i no fa res per un canvi que l'extracció no va veure mai.
Les supressions necessiten registres, no conclusions
Comparar files que encara existeixen no pot dir a un consumidor quines claus han desaparegut. Una font que no registra mai les supressions et deixa dues opcions imperfectes: mantenir un historial de claus prou llarg per comparar-lo, o executar una reconciliació completa explícita segons un calendari. Totes dues són decisions de disseny amb un cost, i cap no existeix si algú no en té la responsabilitat.
La retenció és el mateix tipus de decisió. Cada flux de canvis suportat té una finestra; declara-la al contracte i avisa quan el punt de control d'un consumidor s'hi acosti. Un consumidor que es desperta després d'una aturada de dues setmanes hauria de llegir un error clar "cal reinicialitzar" en lloc de rebre en silenci un lot parcial.
Després mesura si la delta valia la pena
Tornar a comparar cada clau i hash de vegades és correcte, i val la pena saber quant costa. Encara transfereixes informació de cada fila, els hash no et donen una instantània coherent entre taules i un esquema de hash porta les seves pròpies preguntes sobre representació i col·lisions. Menys bytes amb una frontera verificable és l'objectiu; menys bytes que deixen caure en silenci un canvi confirmat no és cap delta.
Una columna de darrera modificació és un indici. Un cursor que sobreviu a un commit tardà, una supressió i una aturada llarga és un contracte.