Skip to main content

29 Tekniikkaa

29Tekniikka logo
Google Cloudiin siirtyessä puhutaan paljon palveluista, verkosta, käyttöoikeuksista ja siitä, miten uusi ympäristö rakennetaan. Data jää helposti vähän sivuosaan.
Se on outoa, koska juuri data aiheuttaa monessa migraatiossa eniten ylimääräistä työtä.
Tietokannan kopioiminen ei itsessään ole erityisen kiinnostava ongelma. Vaikeudet alkavat siinä vaiheessa, kun vanha järjestelmä ei voi olla pysähdyksissä.
Sovellus käy normaalisti. Asiakkaat tekevät tilauksia. Työntekijät muuttavat tietoja. Integraatiot tuovat uusia rivejä tietokantaan.
Samaan aikaan Google Cloudiin rakennetaan uutta ympäristöä.
Siinä syntyy hyvin yksinkertainen mutta hankala kysymys:

Miten uusi järjestelmä pysyy ajan tasalla, kun vanha järjestelmä jatkaa muuttumistaan?

Jos tähän ei ole kunnollista vastausta, migraatio voi näyttää onnistuneelta ja silti päätyä tilanteeseen, jossa kahdessa järjestelmässä on eri totuus.

Ensimmäinen kopio ei ole vielä migraatio

Otetaan tietokanta, jossa on esimerkiksi 600 miljoonaa riviä.

Data kopioidaan uuteen Google Cloud -ympäristöön. Työ valmistuu ja tarkistuksessa nähdään, että rivejä on suunnilleen sama määrä.

Tässä vaiheessa on helppo ajatella, että suurin työ on tehty.

Ei välttämättä.

Jos kopiointi kesti 14 tuntia, vanha järjestelmä ehti muuttua koko tuon ajan.

Ensimmäisen tunnin aikana syntyneet uudet rivit eivät välttämättä kuulu enää alkuperäiseen kopioon. Päivityksiä on voinut tulla samaan aikaan. Jotkin tietueet on ehkä poistettu.

Sama koskee järjestelmiä, jotka kirjoittavat tietokantaan automaattisesti.

ERP voi päivittää varastosaldoja. Maksupalvelu voi palauttaa maksun tilan. Integraatio voi lisätä uuden asiakkaan. Taustalla ajettava prosessi voi muuttaa tuhansia rivejä ilman, että yksikään ihminen koskee sovellukseen.

Siksi migraation ensimmäinen täysi datakopio on oikeastaan vain lähtötilanne.

Sen jälkeen pitää saada kiinni muutokset.

Muutokset ovat paljon kiinnostavampia kuin koko tietokanta

Koko tietokannan kopioiminen uudelleen ei yleensä ole järkevä tapa ratkaista asiaa.

Jos 600 miljoonasta rivistä muuttuu päivän aikana 200 000, ei ole mitään erityistä hyötyä siitä, että kaikki 600 miljoonaa siirretään taas kerran.

Tarvitaan tapa tunnistaa muuttunut osa.

Yksinkertaisessa järjestelmässä se voi olla esimerkiksi updated_at-kenttä.

Ajatuksena on silloin:
hae kaikki rivit, jotka ovat muuttuneet viimeisen onnistuneen synkronoinnin jälkeen.

Tämä toimii niin kauan kuin kenttä on luotettava.

Ja siinä tulee vastaan ensimmäinen käytännön ongelma.

Kaikissa vanhoissa järjestelmissä ei ole kunnollista muutosleimaa. Joissakin tauluissa päivämäärä voi tarkoittaa liiketoiminnan tapahtuma-aikaa eikä viimeistä teknistä muutosta. Joissakin päivityksissä kenttä ei päivity ollenkaan.

Silloin updated_at näyttää hyvältä ratkaisulta arkkitehtuurikaaviossa, mutta tuotannossa se voi jättää osan muutoksista huomaamatta.

Change Data Capture ei poista kaikkia ongelmia

➠ Kun jatkuva muutosten seuranta tarvitaan, vastaan tulee usein termi Change Data Capture, eli CDC.
➠ Ajatus on järkevä. Tietokannassa tapahtuvat muutokset luetaan ja välitetään eteenpäin sen sijaan, että kohdejärjestelmään lähetettäisiin jatkuvasti kokonaisia tauluja.
➠ Google Cloud -ympäristössä tämä voi liittyä esimerkiksi Datastreamiin ja edelleen muihin Google Cloudin datapalveluihin.
➠ Teknisesti tämä on paljon tehokkaampi tapa kuin jatkuva koko datan vertailu.
➠ Mutta CDC:n käyttöönotto ei tee vanhasta tietokannasta automaattisesti hyvin käyttäytyvää lähdejärjestelmää.
➠ Ennen toteutusta pitää selvittää esimerkiksi, mitä tapahtuu transaktioissa, miten poistot näkyvät, miten skeemamuutokset käsitellään ja missä järjestyksessä muutokset tulevat ulos.
➠ Varsinkin vanhoissa järjestelmissä kannattaa selvittää tämä ennen kuin valitaan työkalu.
➠ Muuten helposti päädytään tilanteeseen, jossa työkalu toimii juuri niin kuin sen pitääkin, mutta sovellus ei saanut tarvitsemaansa tietoa.

Poistettu rivi on myös muutos

Tämä unohtuu yllättävän helposti.

Jos asiakas lisätään lähdejärjestelmään, uusi rivi voidaan siirtää kohdejärjestelmään.

Jos asiakas päivitetään, muutos voidaan siirtää.

Mutta mitä tapahtuu, kun asiakas poistetaan?

Jos synkronointi seuraa vain uusia ja päivitettyjä rivejä, kohdejärjestelmään voi jäädä vanha tietue.

Tämän seurauksena vanha ja uusi ympäristö näyttävät aluksi hyvin samanlaisilta. Ero syntyy hiljalleen.

Poistot pitää siis käsitellä samalla tavalla kuin muutkin muutokset.

Joissakin järjestelmissä fyysistä poistoa ei tehdä lainkaan, vaan käytetään esimerkiksi deleted_at-kenttää tai muuta soft delete -mallia. Silloin tilanne on helpompi, mutta myös tässä pitää tietää, mikä tieto siirretään ja milloin.

Sitten tulee vastaan tapahtumien järjestys

Tämä on niitä asioita, joita on vaikea huomata tavallisessa testissä.

Kuvitellaan, että asiakkaan tilaus luodaan.

Ensin syntyy:
order_created

Sen jälkeen asiakas peruu tilauksen:
order_cancelled

Jos molemmat tapahtumat päätyvät kohdejärjestelmään oikeassa järjestyksessä, kaikki näyttää hyvältä.

Mutta jos peruuttaminen käsitellään ennen luontia, tulos voi olla aika outo.

Tapahtumapohjaisessa arkkitehtuurissa ei siis riitä, että viesti “saapui”.

Pitää tietää, mitä tapahtuma tarkoittaa ja voiko sen käsitellä tässä vaiheessa.

Tämä on yksi syy siihen, miksi tapahtumilla käytetään usein versionumeroita, järjestysnumeroita tai muuta tietoa, jonka perusteella vastaanottaja pystyy päättelemään, onko muutos vielä ajantasainen.

Sama viesti voi tulla uudestaan

➠ Toinen klassinen tilanne:
Tapahtuma käsitellään onnistuneesti, mutta kuittaus ei mene perille.
➠ Lähettäjä ei tiedä, että vastaanottaja ehti jo käsitellä tapahtuman.
➠ Viesti lähetetään uudestaan.
➠ Jos vastaanottaja tekee kaiken uudelleen, tulos voi olla huono.
➠ Siksi synkronointipalvelussa tarvitaan käytännössä tapa tunnistaa jo käsitelty tapahtuma.
➠ Tätä tarkoitetaan idempotenssilla.
➠ Esimerkiksi tapahtumalla voi olla oma tunniste:
event_id = 7f8c…
➠ Vastaanottaja tallentaa käsitellyn tunnisteen. Jos sama tapahtuma tulee uudestaan, sitä ei käsitellä uutena muutoksena.
➠ Tämä kuulostaa yksinkertaiselta. Toteutuksessa pitää kuitenkin miettiä, missä vaiheessa tapahtuma merkitään käsitellyksi.
➠ Jos tunniste tallennetaan ennen varsinaista tietokantapäivitystä ja prosessi kaatuu sen jälkeen, tapahtuma voidaan myöhemmin ohittaa vaikka data ei koskaan päivittynyt.
➠ Jos tunniste tallennetaan vasta lopussa, sama tapahtuma voi ehtiä käsittelyyn kahdesti.
➠ Juuri tällaiset yksityiskohdat ratkaisevat, onko synkronointi oikeasti luotettava.

Pub/Sub on kuljetusväline, ei liiketoimintalogiikka

Google Cloudin Pub/Sub sopii hyvin tähän kokonaisuuteen.

Se voi välittää tapahtumia palvelusta toiseen ilman, että lähettäjän ja vastaanottajan pitää olla samaan aikaan suorassa yhteydessä.

Mutta Pub/Sub ei tiedä, onko esimerkiksi asiakkaan osoitteen muuttaminen sallittua.

Se ei tiedä, onko tilaus jo olemassa.

Se ei tiedä, pitääkö tapahtuma käsitellä ennen jotain toista tapahtumaa.

Nämä päätökset tehdään sovelluksessa.

Siksi arkkitehtuurissa kannattaa pitää eri asiat erillään:
tapahtuman kuljetus on yksi asia.
tapahtuman käsittely on toinen.
datan oikeellisuuden varmistaminen on kolmas.

Kun nämä kaikki niputetaan yhdeksi “synkronoinniksi”, ongelman tutkiminen muuttuu nopeasti hankalaksi.

Suuri migraatio voi paljastaa vanhan järjestelmän ongelmat

Tämä on ehkä yksi kiinnostavimmista puolista datamigraatiossa.

Joskus uusi Google Cloud -ympäristö ei oikeastaan ole ongelman aiheuttaja.

Migraatio vain paljastaa asian, joka oli vanhassa järjestelmässä jo pitkään.

Esimerkiksi samalle asiakkaalle voi löytyä kolme eri tunnistetta.

Yhdessä järjestelmässä:
12345

Toisessa:
C-12345

Kolmannessa:
0012345

Kun kaikki olivat vuosia erillään, asia ei välttämättä aiheuttanut suurta ongelmaa.

Migraatiossa järjestelmät yhdistetään.

Yhtäkkiä pitää tietää, ovatko nämä kolme sama henkilö vai kolme eri asiakasta.

Tätä ei ratkaista Google Cloudin asetuksilla.

Se on datan laadun ja liiketoimintasääntöjen kysymys.

Tietokannan rakenne voi muuttua samalla kertaa

➠ Usein migraatioon liittyy myös sovelluksen uudistaminen.
➠ Vanha järjestelmä voi käyttää yhtä taulua, uusi palvelu toista rakennetta.
➠ Esimerkiksi vanhassa järjestelmässä asiakkaan nimi voi olla yhdessä kentässä:
name = “Matti Meikäläinen”
➠ Uudessa järjestelmässä tarvitaan:
first_name
ja
last_name.
➠ Tietoa ei voi vain kopioida.
➠ Se pitää muuttaa.
➠ Ja vielä tärkeämpää: muunnoksen pitää olla toistettavissa.
➠ Jos ensimmäinen siirto tehdään yhdellä tavalla ja seuraava delta toisella tavalla, vanhan ja uuden järjestelmän data alkaa erota.
➠ Siksi migraatiologiikka kannattaa pitää erillään varsinaisesta sovelluskoodista silloin, kun muunnokset ovat monimutkaisia.

Kehitysympäristö voi antaa aivan väärän kuvan

Tätä tapahtuu helposti.

Testitietokannassa on 50 000 asiakasta.

Tuotannossa on 8 miljoonaa.

Kehitysympäristössä synkronointi näyttää toimivan nopeasti. Tuotannossa sama prosessi alkaa muodostaa tuntien viivettä.

Syy voi olla niinkin yksinkertainen kuin se, että kyselyt, indeksit ja käsittelylogiikka eivät käyttäydy samalla tavalla suurella datamäärällä.

Myös tapahtumamäärä vaikuttaa.

Jos kehitysympäristössä syntyy sata tapahtumaa minuutissa ja tuotannossa 20 000, kuluttajan suorituskyky pitää mitoittaa aivan eri tavalla.

Tämän takia migraation testaamisessa ei pitäisi katsoa vain sitä, meneekö yksi tapahtuma onnistuneesti läpi.

Pitää nähdä myös, miten järjestelmä käyttäytyy kuormassa.

Kaikkea dataa ei tarvitse siirtää

Vanha tietokanta voi olla täynnä historiaa, jota uusi sovellus ei koskaan käytä.

Vuosien vanhat lokit.

Arkistoidut tilaukset.

Väliaikaiset taulut.

Poistetut käyttäjät.

Vanha integraatiodata.

Jos kaikki siirretään automaattisesti, uusi ympäristö saa kaiken mukana.

Se voi olla turhaa.

Joskus parempi ratkaisu on siirtää aktiivinen data uuteen tietokantaan ja säilyttää vanhempi aineisto erillisessä tallennuksessa tai arkistossa.

Tämä ei ole pelkästään kustannuskysymys.

Suuri määrä tarpeetonta dataa vaikuttaa myös indekseihin, varmistuksiin, kyselyihin ja siihen, kuinka helposti järjestelmän sisältöä pystytään myöhemmin ymmärtämään.

Aikaleimat ovat pieni mutta vaarallinen asia

➠ Migraatiossa kannattaa olla tarkkana myös ajan kanssa.
➠ Jos yksi järjestelmä tallentaa tapahtuman paikallisena aikana ja toinen UTC-aikana, tietojen vertailu voi näyttää väärältä vaikka molemmat järjestelmät toimivat omasta näkökulmastaan oikein.
➠ Erityisesti delta-synkronoinnissa tämä voi aiheuttaa ongelmia.
➠ Jos synkronointi kysyy:
kaikki muutokset viimeisen tunnin ajalta
pitää tietää täsmälleen, minkä aikavyöhykkeen mukaan tunti lasketaan.
➠ Muuten osa muutoksista voi jäädä välistä tai tulla käsitellyksi kahteen kertaan.
➠ Siksi aikaleimojen merkitys kannattaa määritellä jo ennen migraatiota, ei vasta silloin kun ensimmäinen raportti näyttää oudolta.

Mitä jos synkronointi pysähtyy?

Tätä kannattaa kokeilla tarkoituksella.
Pysäytetään kuluttava palvelu.
Annetaan lähdejärjestelmän jatkaa muutosten tuottamista.
Puolen tunnin kuluttua käynnistetään kuluttaja uudelleen.
Jos kaikki toimii, jonossa pitäisi olla käsittelemättömiä tapahtumia ja niiden pitäisi päästä eteenpäin.
Jos mitään ei tapahdu, ongelma löytyy yleensä nopeasti.
Mutta entä jos vain osa tapahtumista epäonnistuu?
Silloin tarvitaan näkyvyys siihen, mitä tapahtui.
Lokista pitäisi pystyä selvittämään ainakin:
  • mikä tapahtuma epäonnistui
  • milloin se epäonnistui
  • miksi se epäonnistui
  • montako kertaa sitä yritettiin
  • käsiteltiinkö se lopulta onnistuneesti
Jos tämä tieto puuttuu, migraation ylläpito muuttuu nopeasti käsityöksi.

Dead-letter-jono ei ole roskakori

Jos tapahtuma ei onnistu useista yrityksistä huolimatta, se voidaan siirtää erilliseen käsittelyjonoon.

Google Cloudin Pub/Subissa tähän voidaan käyttää dead-letter topic -ratkaisua.

Se on hyödyllinen mekanismi.

Mutta helposti syntyy ajatus, että ongelma on ratkaistu, kun epäonnistunut viesti siirtyy dead-letteriin.

Ei ole.

Nyt ongelma on vain siirretty paikkaan, josta joku tarvitsee löytää sen.

Jos siellä on 30 000 viestiä eikä kukaan tiedä, miksi ne epäonnistuivat, järjestelmä ei ole kovin hyvässä kunnossa.

Dead-letter-ratkaisun ympärille tarvitaan seuranta ja prosessi: kuka tutkii virheet, miten yksittäinen tapahtuma korjataan ja voidaanko se ajaa uudelleen turvallisesti.

Milloin uusi ympäristö on oikeasti ajan tasalla?

Tähän pitäisi olla mitattava vastaus.

Ei:
“Näyttää siltä, että synkronointi toimii.”

Vaan esimerkiksi:
“Kohdejärjestelmä on tällä hetkellä 4,2 sekuntia lähdejärjestelmää jäljessä.”

Tai:
“Jonossa on 182 käsittelemätöntä tapahtumaa.”

Näistä voidaan tehdä metriikoita ja hälytyksiä.

Jos viive normaalisti pysyy alle kymmenessä sekunnissa mutta nousee viiteen minuuttiin, siitä pitäisi saada tieto ennen kuin käyttäjä huomaa ongelman.

Tässä kohtaa Google Cloudin seuranta- ja lokityökalut ovat hyödyllisiä. Mutta jälleen kerran tärkeintä ei ole työkalun nimi.

Tärkeintä on päättää etukäteen, mikä arvo tarkoittaa ongelmaa.

Cutoverissa ei pidä yrittää olla rohkea

➠ Kun uusi ympäristö on valmis, liikenne pitää lopulta siirtää.
➠ Tässä vaiheessa kannattaa välttää ajatusta, että kaikki vain vaihdetaan yhdellä kertaa ja katsotaan mitä tapahtuu.
➠ Ennen cutoveria pitäisi olla selvää:
  • milloin vanhan järjestelmän kirjoitukset pysäytetään
  • mitä viimeisille tapahtumille tehdään
  • miten dataero tarkistetaan
  • milloin uusi järjestelmä saa kirjoitusoikeudet
  • miten käyttäjät ohjataan uuteen ympäristöön
  • kuinka kauan vanha ympäristö pidetään valmiina
➠ Erityisen tärkeää on viimeinen kohta.
➠ Vanhaa järjestelmää ei kannata sulkea heti ensimmäisen onnistuneen testin jälkeen.
➠ Jos uudessa ympäristössä huomataan myöhemmin virhe, vanha järjestelmä voi olla tarpeen palautusta varten.

Rollback muuttuu vaikeaksi, kun uusi järjestelmä alkaa vastaanottaa dataa

Tässä vaiheessa moni migraatiokaavio näyttää liian yksinkertaiselta.

Liikenne siirtyy uuteen järjestelmään.

Sitten huomataan ongelma.

Palataan vanhaan.

Mutta mitä tapahtuu kaikille niille muutoksille, jotka tehtiin uuden järjestelmän ollessa käytössä?

Jos asiakas teki uuden tilauksen, vaihtoi osoitettaan ja perui yhden vanhan tilauksen, vanhan järjestelmän pitää saada nämä muutokset ennen kuin sitä voidaan käyttää uudelleen.

Muuten rollback palauttaa kyllä sovelluksen, mutta vanhaan dataan.

Siksi palautussuunnitelma pitää tehdä datan näkökulmasta, ei vain infrastruktuurin näkökulmasta.

Datan omistajuus pitää päättää ennen hybridivaihetta

Kun vanha ja uusi ympäristö elävät rinnakkain, on hyvä päättää mahdollisimman tarkasti, kumpi järjestelmä saa kirjoittaa mitäkin.

Jos molemmat voivat muuttaa samaa asiakastietoa, ristiriitojen määrä kasvaa nopeasti.

Parempi malli voi olla esimerkiksi se, että uusi järjestelmä omistaa asiakasprofiilin, mutta vanha ERP saa edelleen hallita laskutustietoja.

Silloin tiedetään, missä muutos syntyy.

Synkronointi ei yritä ratkaista kaikkea kaikkeen.

Tämä voi kuulostaa enemmän järjestelmäarkkitehtuurilta kuin datamigraatiolta. Käytännössä nämä asiat ovat kuitenkin tiukasti sidoksissa toisiinsa.

Lopullinen tarkistus kannattaa tehdä liiketoiminnan kautta

➠ Tekninen tarkistus voi kertoa, että 99,99 prosenttia tapahtumista on käsitelty.
➠ Se kuulostaa hyvältä.
➠ Mutta jos puuttuva 0,01 prosenttia sisältää kaikki tietyn asiakkaan maksutapahtumat, prosenttiluku ei auta paljon.
➠ Siksi lopullisessa tarkistuksessa pitää katsoa myös liiketoiminnan kannalta tärkeitä asioita.
➠ Esimerkiksi:
  • täsmäävätkö tilausmäärät
  • täsmäävätkö maksujen kokonaissummat
  • löytyvätkö aktiiviset asiakkaat
  • ovatko varastosaldot järkeviä
  • ovatko kriittiset integraatiot käsitelleet tapahtumat
  • näkyvätkö viimeisimmät muutokset uudessa ympäristössä
➠ Näitä ei tarvitse tehdä käsin.
➠ Tarkistuksia voidaan automatisoida.
➠ Oleellista on, ettei migraation onnistumista määritellä vain teknisen infrastruktuurin perusteella.

Google Cloud ei tee huonosta datasta hyvää

Pilvimigraatioon liittyvissä keskusteluissa on helppo keskittyä palveluihin.
Cloud Storage.
Cloud SQL.
Pub/Sub.
Dataflow.
Datastream.
Cloud Run.
Nämä ovat työkaluja.
Ne voivat tehdä datan siirtämisestä paljon helpompaa, mutta ne eivät päätä sitä, mikä data on oikeaa.
Jos lähdejärjestelmässä on ristiriitoja, uusi ympäristö saa ristiriidat mukaansa.
Jos tapahtumien järjestystä ei ole mietitty, viestijärjestelmä ei ratkaise sitä.
Jos rollbackia ei ole suunniteltu, infrastruktuurin palauttaminen ei palauta menetettyjä liiketoimintatapahtumia.
Tämä on ehkä koko migraation tärkein asia.
Milloin synkronointi kannattaa pitää yksinkertaisena?
Kaikki järjestelmät eivät tarvitse reaaliaikaista tapahtumaketjua.
Jos kyseessä on raportointidata, tunnin viive voi olla täysin hyväksyttävä.
Jos kyseessä on varastosaldo tai maksun tila, tilanne voi olla toinen.
Siksi ennen toteutusta kannattaa kysyä yksi käytännöllinen kysymys:
  • Jos vastaus on “päivän vanhaa”, ei ole järkevää rakentaa sekuntitason tapahtumajärjestelmää vain siksi, että se on teknisesti hienompi.
  • Jos vastaus on “ei yli muutamaa sekuntia”, arkkitehtuurilta vaaditaan aivan toisenlaista toimintavarmuutta.
  • Tekninen ratkaisu kannattaa rakentaa tämän tarpeen ympärille.
Kuinka vanhaa dataa käyttäjä voi hyväksyä?
  • Google Cloud -migraation onnistumista on helppo mitata yhdellä hetkellä.
  • Vanha järjestelmä pois.
  • Uusi järjestelmä päälle.
  • Käyttäjä kirjautuu sisään.
  • Kaikki näyttää toimivan.
  • Datan synkronoinnin kannalta kiinnostavampi testi alkaa oikeastaan vasta sen jälkeen.
  • Tuleeko uusia tapahtumia?
  • Päivittyvätkö olemassa olevat tiedot?
  • Toimivatko poistot?
  • Entä jos tapahtuma tulee kahdesti?
  • Entä jos kohdejärjestelmä on hetken poissa käytöstä?
  • Entä jos viesti jää käsittelemättä?
  • Entä jos uusi järjestelmä tarvitsee datan eri muodossa?
  • Näihin tilanteisiin kannattaa valmistautua jo ennen cutoveria.
  • Hyvä migraatio ei tarkoita sitä, että kaikki näyttää onnistuneelta siirtopäivänä.
  • Se tarkoittaa, että vanhan ja uuden järjestelmän välinen ero tunnetaan myös silloin, kun jokin menee pieleen.
  • Ja juuri siinä datan synkronoinnin todellinen työ yleensä on.
Lopputulos ei ole onnistunut, jos vain data siirtyi