Tehnički članak

HotXLS Deep Recalc auditira kešove formula

HotXLS odgovara na pitanje koje svaki spreadsheet pipeline prije ili kasnije mora postaviti, a to je slažu li se brojevi pohranjeni u radnoj knjizi još s formulama koje su ih proizvele. CalculateAndVerify preračunava cijeli dependency graf u izolirani overlay, svaki rezultat uspoređuje s keširanom vrijednošću koja je već u ćeliji, i izvještava o neslaganjima. Po defaultu ne mijenja ništa

Razlog zašto je to bitno je da spreadsheet datoteka po formulske ćeliji sprema dvije stvari: formulu i zadnju vrijednost koju je netko za nju izračunao. Excel ih drži u sinku. Sve ostalo na svijetu ne mora. Datoteka koja je prošla kroz stariju biblioteku, djelomični preračun, ručno uređen XML dio ili alat koji je zapisao vrijednosti bez ponovnog računanja slobodno će prikazati zbroj koji više ne slijedi iz svojih ulaza, i ništa u file formatu to ne označava

Zašto je keširana vrijednost koja odstupa od formule tako opasna?

Jer je nevidljiva u svakom uobičajenom putu čitanja. Otvorite datoteku u vieweru, pročitajte ćeliju kroz API, izvezite je u CSV ili PDF, i dobijete keširani broj. Formula je odmah tu u istoj ćeliji, i nitko ih ne uspoređuje. Neslaganje ispliva tek kad netko otvori radnu knjigu u Excelu, koji pod većinom postavki preračunava pri učitavanju, i odjednom izvještaj potpisan prošli kvartal pokazuje druge zbrojeve

Audit postoji da tu usporedbu učini namjernom, zakazanom operacijom umjesto nesreće. To je spreadsheet pandan provjeri checksuma: dovoljno jeftin da trči u intake pipelineu, i jedina stvar koja tihi problem integriteta podataka pretvara u izvještaj na kojem možete djelovati

var
  Book: TXLSWorkbook;
  Options: TXLSRecalcAuditOptions;
  Report: TXLSCalculationAuditReport;
  I: Integer;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('quarterly-close.xls');
    Options := TXLSRecalcAuditOptions.Default;
    Options.MaxIssues := 500;
    Report := Book.CalculateAndVerify(Options);
    try
      for I := 0 to Report.Count - 1 do
        if Report[I].Kind = xlcaiCacheMismatch then
          Writeln(Report[I].SheetName, '!',
                  Report[I].Row, ':', Report[I].Col, '  ',
                  Report[I].Formula,
                  '  cached=', VarToStr(Report[I].Actual),
                  '  recomputed=', VarToStr(Report[I].Expected));
      if Report.Truncated then
        Writeln('issue budget reached, raise MaxIssues');
    finally
      Report.Free;
    end;
  finally
    Book.Free;
  end;
end;

Postoje tri overloada i odgovaraju na tri različita pitanja. Bezparametarski CalculateAndVerify vraća broj neslaganja, a to je sve što health check treba. Overload s out arrayom neslaganja daje vam ćelije. Overload koji uzima TXLSRecalcAuditOptions vraća puni TXLSCalculationAuditReport, i to je onaj za koji se poseže kad trebate znati ne samo da vrijednost ne slaže nego i zašto audit nije mogao nešto evaluirati

Overlay, i zašto audit ne piše

Svaka preračunata vrijednost slijeće u overlay umjesto u ćelijski keš, i overlay je ubačen na sam prednji kraj cell-read callbacka u oba workbook enginea. Taj smještaj je ono što audit čini samokonzistentnim: kad se B1 preračuna i C1 ovisi o B1, C1 vidi vrijednost iz ovog audit prolaza, ne zastarjelu keširanu. Bez toga, jedna uzvodna greška bi se javila jednom i bila apsorbirana, i svaka nizvodna ćelija bi izgledala da se slaže s krivim ulazom

Ćelije čija preračunata vrijednost odgovara kešu uopće ne ulaze u overlay. To nije mikrooptimizacija, to je ono što audit drži pristupačnim. Čista radna knjiga sa sto tisuća formula izvodi nula overlay zapisa i prolaz ostaje unutar 1.35x budžeta naspram punog preračuna, a to je razlika između nečega što možete pokrenuti na svaki intake i nečega što pokrećete jednom u kvartalu

HotXLS deep recalc audit pipeline: radna knjiga se učitava s netaknutim keševima, svaki dependency node označi se kao dirty i evaluira jednom u topološkom redu, preračunate vrijednosti slijeću u izolirani overlay koji cell-read callback u oba enginea konzultira prvi, rezultati se uspoređuju s keširanim vrijednostima, klasificiraju kroz CalculateAndVerify u TXLSCalculationAuditReport, i ništa se ne piše na disk
Preračunate vrijednosti slijeću u overlay ispred cell-read callbacka, odgovarajuće ćelije ga nikad ne dotaknu, i radna knjiga na disku ostaje netaknuta dok ApplyResults ne komitira potpuno čist prolaz

Evaluacija slijedi serijski topološki red izveden iz dependency grafa, sa svakim nodeom prvo označenim dirty, pa se svaka ćelija računa točno jednom nakon svojih ulaza. Ako želite inkrementalnu mašineriju koja živu radnu knjigu drži ažurnom umjesto da auditira pohranjenu, to je drugi mehanizam, opisan u inkrementalnom preračunu i dependency grafu

Neuspjesi su klasificirani, ne gurani u jednu hrpu

Ćeliju koju audit ne može evaluirati nije isti nalaz kao ćelija čija se vrijednost ne slaže, i TXLSCalculationAuditIssueKind drži kategorije razdvojene. xlcaiCacheMismatch je neslaganje vrijednosti. xlcaiMissingFunction i xlcaiMissingName kažu da je evaluator sreo nešto što ne implementira ili ne može razriješiti. xlcaiUnsupportedArguments pokriva oblike argumenata izvan podržanog podskupa. xlcaiExternalReferenceDenied i xlcaiExternalReferenceMissing razdvajaju odbijanje politikom od odsutne radne knjige. xlcaiCircularReference, xlcaiDataTableSkipped, xlcaiParseFailure, xlcaiCancelled i xlcaiInternalFailure zaokružuju skup

Klasifikacija audit issueja u HotXLS-u: TXLSCalculationAuditIssueKind razdvaja neslaganje vrijednosti javljeno kao xlcaiCacheMismatch od vrsta neuspjeha evaluacije poput xlcaiMissingFunction, xlcaiMissingName, xlcaiUnsupportedArguments, para xlcaiExternalReferenceDenied naspram xlcaiExternalReferenceMissing i xlcaiCircularReference, dok pozitivni Excel error code računa kao rezultat a ne kao neuspjeh
Jedna vrsta javlja neslaganje vrijednosti a ostale javljaju zašto evaluator nije mogao presuditi ćeliju; Excel error vrijednost je izračunat rezultat, pa namjerne error ćelije proizvode nula nalaza

Jedna razlika vrijedi da se izriče jer okreće uobičajenu pretpostavku. Pozitivni Excel error code je rezultat, a ne neuspjeh. Ćelija koja legitimno evaluira u #DIV/0! izračunala je ispravno, pa audit tu grešku sprema u overlay i uspoređuje je s kešom kao bilo koju drugu vrijednost. Radna knjiga puna namjernih error ćelija proizvodi nula nalaza, a radna knjiga u kojoj je greška od kad su vrijednosti keširane nestala ili se pojavila proizvodi točno nalaze koje želite

Kružne reference dobivaju vlastiti tretman. Nodeovi u ciklusu nikad ne ulaze u topološki red, pa se svaki pojedinačno javlja kao xlcaiCircularReference, i audit ne pokreće iterativni solver. To je namjerni read-only ugovor: to je li iteracija omogućena utječe na to kako se result code treba tumačiti, ne na to što audit radi. Mehanika iterativne evaluacije pokrivena je odvojeno u iterativnom računanju i kružnim referencama

Čitanje lanca neuspjeha

Kad formula ne uspije evaluirati, znati koja je ćelija pala rijetko je dovoljno, jer je neuspjeh obično tri razine niz lanac referenci. Svaki issue zato nosi Stack string renderiran s krajnje vanjskim frameom prvim, u obliku Sheet1!A1 > Sheet1!B2 > Data!C7, pa izvještaj pokazuje na ćeliju koja je stvarno pukla umjesto na ćeliju koju ste slučajno gledali

Rekorder je ograničen. MaxStackFrames je po defaultu 64 s podom od 8, i zadržava se najdublji lanac koji je pao: unutarnji frame zabilježi lanac kad tamo neuspjeh nastaje, a vanjski frameovi koji se odmotavaju poslije njega ne prebrišu. Ako je ijedan lanac prešao budžet, postavlja se Report.StackTruncated, što vam kaže razliku između kratkog lanca i lanca kojega niste vidjeli cijeloga

Lanac neuspjeha audita u HotXLS-u: kad formula tri reference niz pukne, Stack renderira krajnje vanjski frame prvi, Sheet1!A1 pa Sheet1!B2 pa Data!C7, najunutarnji frame zabilježi lanac i vanjski frameovi koji se odmotavaju ga ne prebrišu, MaxStackFrames je po defaultu 64 s podom od 8, a Report.StackTruncated označi lanac kojega niste vidjeli cijeloga
Stack renderira krajnje vanjski frame prvim pa izvještaj pokazuje na ćeliju koja je stvarno pukla, zadržava se najdublji pali lanac, a StackTruncated razdvaja kratke lance od skraćenih
// Read-only po defaultu. ApplyResults komitira overlay tek nakon
// potpuno uspješnog audita, pod write guardom koji odbija commit
// ako se struktura radne knjige mijenjala dok je audit trčao
Options := TXLSRecalcAuditOptions.Default;
Options.ApplyResults := True;
Options.AbsoluteTolerance := 0;     // točna usporedba, izvlači drift
Options.RelativeTolerance := 0;
Options.OnProgress := HandleProgress;

Report := Book.CalculateAndVerify(Options);
try
  if Report.Applied then
    Book.SaveToFile('quarterly-close-repaired.xls')
  else
    Writeln('not applied: ', Report.Count, ' issues blocked the commit');
finally
  Report.Free;
end;

procedure THarness.HandleProgress(ASender: TObject;
  ACurrent, ATotal: Integer; var ACancel: Boolean);
begin
  ACancel := FUserRequestedStop;   // audit staje na sljedećoj granici nodea
end;

Kad pustiti audit da popravi radnu knjigu?

Tek kad je audit vratio potpuno čist od issueja klase neuspjeha, što je točno uvjet koji ApplyResults za vas nameće. Commit se odvija nakon potpuno uspješnog prolaza, nije otkazan, i prolazi strukturni guard: binary engine pazi na identifikator promjene radne knjige, OOXML engine snapshotira generaciju strukture po listu. Ako se išta pomaklo dok je audit trčao, rezultati opisuju radnu knjigu koja više ne postoji i commit se odbija

Primijetite namjernu asimetriju. Cache neslaganja ne blokiraju primjenu, jer su točno ono što commit postoji da popravi. Issueji klase neuspjeha je blokiraju, jer bi radna knjiga u kojoj neke formule nisu mogle biti evaluirane bila napola popravljena, a napola popravljena radna knjiga je gora od nepopravljene za koju znate da joj ne smijete vjerovati

Tolerancija je policy odluka, ne default

Defaultna usporedba je apsolutna tolerancija 1E-6 s isključenom relativnom tolerancijom, što čuva klasično ponašanje i tiho prihvaća drift od 4E-7. To je obično pravo: razlike u redu floating-point evaluacije između onoga što je proizvelo datoteku i trenutnog evaluatora proizvest će razlike te veličine na dugim zbrojevima, i javljati ih kao nalaze integriteta je šum

Postavite obje tolerancije na nulu kad je pitanje drugačije, kad pokušavate saznati je li evaluator promijenio ponašanje između verzija, ili treba li third-party alat prepisivati vrijednosti na suptilno drugačiji način. Na nuli isti taj drift od 4E-7 postane vidljiv, i sve ostalo također. Odaberite toleranciju prema pitanju koje postavljate, i zabilježite izbor uz izvještaj, jer izvještaj bez svoje tolerancije nije interpretabilan

Dvije susjedne mogućnosti zaokružuju sliku. Kad želite znati zašto jedna formula proizvodi vrijednost koju proizvodi, korak-po-korak pogled u traceru evaluacije formula je pravi alat. Kad namjerno želite da se keširane vrijednosti poštuju bez ikakva preračuna, primjerice na intake putu koji mora reproducirati datoteku točno onakvom kakva je stigla, taj je način opisan u čitanju keširanih vrijednosti formula bez preračunavanja. Audit je ono što stoji između ta dva: kaže vam je li vjerovati kešu sigurno. Isporučuje se uz HotXLS Delphi spreadsheet komponentu za binary i OOXML enginee