Počnimo s dijelom koji se najlakše izgubi dok se čita ostatak: novac nije uzet. Nijedan novčanik nije terećen dvaput, nijedna kartica nije naplaćena, nijedna pretplatnička kvota nije potrošena na posao koji se nikada nije dogodio.
Pokvaren je bio broj na ekranu. Na nekim je računima razrada potrošnje na tvojoj vlastitoj stranici računa pokazivala previše — na najgorem koji smo izmjerili, 2,4 puta više od glavne knjige koju bi trebala sažimati. Knjiga je cijelo vrijeme bila točna. Jednako i mjesečne gornje granice, koje se čitaju s posve drugog mjesta. Bio je to nedostatak prikaza, ispravljen je, a evo i uzroka.
Ključ koji je tražio nešto što nitko nije zapisao
Posao koji nastaje zbog nečega što si učinio — ažuriranje dnevnog sažetka potrošnje, knjiženje preporuke, razašiljanje obavijesti — ne događa se unutar tvog zahtjeva. Zahtjev zapiše poruku, a pozadinski je potrošač preuzme trenutak kasnije; upravo to sprječava da spor nuspojedinac uspori brzu stranicu.
Opasnost takvog rasporeda jest obaviti isti posao dvaput, jer posrednik nakon zastoja rado ponovno isporuči istu poruku. Obrana je pristigla pošta: prije obrade potrošač preuzima poruku zapisivanjem retka koji nosi ključ te poruke, a jedinstveni indeks čini drugo preuzimanje nemogućim.
Naš je preuzimao po paru — najmoprimac kojem je poruka pripadala, plus ključ poruke — i taj je par tražio prema identifikatoru najmoprimca koji nosi omotnica poruke, a koji jest popunjen.
Reci preuzimanja nisu nosili identifikator najmoprimca uopće. Svaki je bio prazan, i bio je takav od početka. Vlastita dokumentacija entiteta doslovce je tvrdila da ga okvir utisne pri umetanju. Nikada nije. Izmjereno na produkciji na dan kad smo to našli: svih 3.344 redaka pristigle pošte prazno, dok je 1.174 od 1.451 retka na proizvodnoj strani nosilo stvarnog najmoprimca — upravo zato nitko nije posumnjao u stranu koja čita.
Dakle, pretraga je tražila (najmoprimac, ključ), spremljeni redak bio je (ništa, ključ), i nikad se nisu mogli podudariti.
Ništa nije palo, i zato je trajalo tjednima
Pretraga koja se nikad ne može podudariti zvuči kao nešto što bi trebalo podići pogrešku iz prvog pokušaja. Učinila je suprotno.
Preuzimanje je odlučilo da je poruka nova. Umetanje je zatim udarilo u jedinstveni indeks i bilo odbijeno kao duplikat — ispravno, jer je redak već bio ondje. Rezervna je grana uhvatila to odbijanje i pročitala ga dobronamjerno: valjda je neka druga isporuka ovo preuzela, ovdje nema što raditi. Omotnica je ostala u stanju „još za obradu", a kako je bila još za obradu, sljedeća ju je isporuka preuzela i posao obavila ponovno.
Svaki je potrošač iznova obrađivao vlastiti zaostatak otprilike svakih četrdeset pet sekundi, beskonačno. To je radilo njih dvadeset šest. Jedan se produkcijski redak dovršio 333 sata nakon svog dolaska — i dovršavao se iznova i iznova veći dio tog vremena.
Što je zapravo pokvarilo
Od tih dvadeset šest potrošača, četvorica akumuliraju umjesto da prepisuju. Ta četvorica su projekcije naplate koje čita stranica potrošnje, i ondje je stvar isplivala.
Imali su izričitu zaštitu baš od ovoga, napisanu nakon kolovoškog incidenta u kojem je jedan jedini događaj devet puta primijenjen na pokazatelj. Ta zaštita označava redak uspješnim prije obrade, kako bi ponovljena isporuka našla oznaku i stala. Oznaka se svaki put ispravno zapisivala. Sljedeće ju je preuzimanje zatim tražilo istim pokvarenim ključem i nije je nalazilo. To je jamstvo bilo prazno od dana kad je izdano.
Ono što ovo nije dotaknulo vrijedi precizno nabrojiti, jer „projekcija naplate" zvuči uznemirujuće, a činjenice nisu:
- Glavna knjiga bila je točna. Svaki AI poziv zapisuje točno jedan zapis naplate s vlastitom snimkom cijene, a ništa na ovom putu njih ne zapisuje.
- Nijedan se novčanik nije pomaknuo. Stanja dolaze iz knjige, nikad iz sažetka.
- Nijedna se gornja granica nije pomaknula. Čuvar proračuna čita zaseban brojač i on je jedini izvor istine o tome koliko si potrošio ovaj mjesec. Nitko nije prerano odsječen zbog napuhanog broja i nitko nije prošao tko nije smio.
Jedino što čita pogođeni sažetak jest povijest potrošnje na tvojoj vlastitoj stranici računa — tvoj zbroj i razrada na što je otišao. To je cijeli polumjer učinka, a sastojao se u tome da je pokazivao previše.
Polovica koju nitko nije gledao
Ista je pokvarena pretraga sjedila i na drugom mjestu: na putu koji se izvodi kad poruka padne. Ondje nikad nije nalazila redak koji je trebala ažurirati, a odmah ispod nje sjedila je grana s komentarom „u praksi nedostižno" — koja je pri svakom padu vraćala „ovaj je pad konačan".
Brojač ponovnih pokušaja tako nikad nije napredovao i nijedna otrovana poruka nikad nije stigla u red neisporučivih. Cijeli je sloj rukovanja pogreškama bio isključen, iza komentara koji je svakom čitatelju govorio da tu nema što provjeravati.
Ispravak
Obje pretrage sada ključ tvore po ključu poruke unutar skupine potrošača, što jedinstveni indeks ionako nameće. Ključevi poruka izvode se iz identifikatora onoga što ih je proizvelo, pa se po samoj konstrukciji ne mogu sudariti među najmoprimcima.
Dokumentacija koja je tvrdila to utiskivanje ispravljena je, a ne obrisana. Ta je rečenica bila zamka, a brisanje ne bi ostavilo ništa što kaže da je netko napokon provjerio.
Ponašanje pričvršćuje devet testova. Šest ih pada na starom kodu, što smo potvrdili vraćanjem starog koda.
Čišćenje poslije
Napuhani se reci popravljaju sami. Radnik svakih sat vremena iznova gradi dnevne sažetke iz glavne knjige, u prozoru od dva dana. Radio je cijelo vrijeme i gubio: jednom na sat protiv potrošača koji ponovno primjenjuje svakih četrdeset pet sekundi nije poštena utrka.
Kad potrošač više ne ponavlja, radnik pobjeđuje. Dva dana koja su još bila unutar tog prozora ponovno su izgrađena iz knjige čim je ispravak bio u produkciji. Dva ranija dana već su ispala iz prozora, a ono što na njima ostaje jest naš vlastiti račun plus jedna zanemariva razlika drugdje — pa smo ih ostavili, umjesto da zbog toga širimo prozor u produkciji. Činilo se to boljim od nagovještaja da je čišćenje bilo potpuno.
Što smo iz toga ponijeli
Ključ za uklanjanje duplikata mora zrcaliti ono što indeks stvarno nameće, a ne ono što kod vjeruje da je zapisano. Dvije polovice ovoga hranile su se iz različitih polja i nijedna nikad nije izmjerena naspram druge. Sada jesu obje, a test koji to dokazuje postaje crven naspram verzije koja je bila pogrešna.