Palmesøndag 2020
Oppdatert 22. november 2020
og 6. desember 2020
Visma Business i et transaksjonstungt miljø
Jeg har et par observasjoner som umiddelbart overrasket meg, men ikke etter å ha tenkt meg om.
Den ene var i forbindelse med at det skulle etableres felles aktørtabell i to selskap med mange kunder. Og siden dette var én av flere endringer, så ble det gjort i test først og skarpt noe senere. Det var om lag 46 tusen kunder som måtte få nytt kundenr. Med standardrutinen i VBus tok det 13 ½ time. Det overrasket meg. Kundenr finnes i nesten 50 tabeller og i noen av dem i flere kolonner. Det VBus gjør, er å ta én og én aktør og bytter kundenr for denne aktøren i de nesten 50 tabellene; godt over 50 updates. Og så neste aktør. 46 tusen ganger.
Er det noe SQL-server er dårlig til så er det å oppdatere én eller et lite antall rader ad gangen. Det SQL-server er god til, er å oppdatere mange rader i én operasjon.
Så jeg skrev en prosedyre med noe over 50 updates på formen:
UPDATE tabell set CustNo=Actor.NwCNo
from tabell left join Actor on Actor.CustNo=tabell.CustNo
where Actor.NwCNo<>0 and tabell.CustNo<>0
Ikke akkurat rocket science. Men det var effektivt. Det tok 2 ½ minutt å endre kundenr på 46 tusen aktører. Og på pluss-siden; om to aktører byttet kundenr så ble det ikke nødvendig å gå veien om å «parkere» den ene på et annet kundenr for å unngå at de ble slått sammen. I det hele tatt; her er det en svakhet i VBus. Hvis du ved en inkurie slår sammen kunder – altså uten hensikt – så får du melding etter at det har skjedd. Det burde ha blitt gitt en advarsel om at det var i ferd med å skje før det skjer, med mulighet til å angre. Og det gjelder selvfølgelig leverandører og hovedbokskonti også.
Men altså; VBus bruker 13 ½ time på det som nøkternt sett kan gjøres på 2 ½ minutt. Dét gir grunn til ettertanke. Så kan man tenke at å bytte kundenr er noe som bare skjer unntaksvis – og da spiller det liten rolle om rutinen isolert sett ikke er skrevet på en effektiv måte.
Jeg har tidligere i denne uka (uke 47) gjort det samme hos en kunde som har byttet Produktnr på 105 tusen produkter. Da vi testet dette i standardrutinen, brukte VBus 30 timer og 9 minutter. Direkte i databasen tok 2 minutter og 7 sekunder. Dette er gjort på samme server. Og hos en tredje kunde skrev jeg denne uka en rutine som bokfører korrigerte kostnader fra logistikken; der VBus bruker en drøy time på 80.000 transaksjoner, tar det 2-3 minutter å gjøre det rett i databasen. Og her låser jeg produkttransaksjonene og ordrejournalen – og åpner etterpå – slik at ingen skal endre transaksjonen mens det pågår. Det er på ingen måte slik at jeg er bedre til å programmere enn utviklerne til Visma, men for SQL-server å behandle en og en transaksjon ad gangen, tar dramatisk mer tid når mange skal behandles enn å ta alle i en og samme operasjon. Verre er det at det kan oppstå headblock (men ikke deadlock) i databasen mens dette skjer. For hver produkttransaksjon som behandles låser VBus den tilhørende ordrejournalen, oppdaterer rett rad i tabellen Aktive bedriftsprosesser; angir at Ordrejournal er låst. Når produkttransaksjonen er behandlet fjerner VBus låsingen på ordrejournalen og oppdaterer raden i Aktive bedriftsprosesser; angir nå at det ikke er noen låste tabeller. At ordrejournalene låses etter hvert som produkttransaksjonene behandles er greit nok. Men trafikken på tabellen Aktive bedriftsprosesser – hvor samme rad oppdateres 2 ganger for hver produkttransaksjon, er helt ødeleggende når store transaksjonsmengder behandles. Det tar ikke lang tid før ingen andre brukere får åpne et nytt vindu (hvor VBus altså setter inn en ny rad i nettopp tabellen Aktive bedriftsprosesser). Her burde VBus ha startet prosessen med å sette at ordrejournalen har låste rader og ikke endre dette før alle rader er behandlet. Det kan være greit å merke seg at VBus ikke stress-oppdaterer tabellen Aktive bedriftsprosesser på denne måten om det i Bokføringstilfeller er valgt Automatisk kost-korreksjon.
Den andre observasjonen var hos en kunde som har store og komplekse leveranser med lang levetid. Her er det flere salgsordrer med mer enn tusen ordrelinjer. Så dukket det opp en salgsordre med tre tusen ordrelinjer. Det ble generert innkjøpsordre fra salgsordren og én av disse hadde førti ordrelinjer. Da de endret rabatten på de 40 innkjøpsordrelinene tok lagringen lenger tid enn forventet. Det viste seg at for hver av de 40 endrede innkjøpsordrelinjene så oppdaterer VBus samtlige salgsordrelinjer (med antall). Altså 120.000 oppdateringer. Og da mener jeg ikke 120.000 ordrelinjer oppdatert i én og samme update; jeg mener 120.000 single updates av én og én ordrelinje – hvor de aller fleste altså ikke endrer ordrelinjen!
Hvis du er i tvil: VBus behandler én og én rad. Og når innkjøpsordren lagres, så behandles altså én innkjøpsordrelinje ad gangen. Når det fører til at en salgsordrelinje blir endret (kostpris på salgsordrelinjen blir tross alt endret), så oppdaterer VBus like gjerne alle ordrelinjene på salgsordren (som har antall). Også de som ikke får endret kostpris! Og så gjentas suksessen for alle 40 innkjøpsordrelinjene. Du skal ha en bra SQL-server om 120.000 updates ikke merkes av bruker når en liten innkjøpsordre med 40 linjer blir lagret.
Jeg har spurt Visma om dette er som det skal være og utviklingsavdelingen svarer: JA, slik skal det være.
Jeg tenker at det å alltid behandle én og én rad kan være trygt – enkelt å programmere. Men altså ikke effektivt i transaksjonstunge miljøer. Hvis VBus skal kunne hevde seg i større (og da mener jeg transaksjonstunge) miljøer, må koden skrives om. Og da tenker jeg to ting samtidig (jeg har nemlig noen feminine trekk og evner å ha to tanker i hode samtidig):
1. Jeg gruer meg til omskrivingen skjer; jeg husker med gru omskrivingen som skjedde i versjonene 4.60, 5.10, 5.21 og 5.30. Det var et helvete av følgefeil.
2. Omtrent alle triggere jeg har sett, er skrevet med den forutsetning at bare én rad blir oppdatert. Dersom Visma skriver om koden for å gjøre den mer effektiv i transaksjonstunge miljøer (som de bør), må alle disse triggerne skrives om. Dét blir en utfordring. Kanskje er det på tide at vi som skriver triggere, skriver dem med tanke på at flere rader blir oppdatert i én og samme insert, update eller delete.
Resten av min blogg kan du lese her: frode.antun.no/VBus/blogg