2014-03-01

Tägäyksen tasoista

Vähän aika sitten IETF julkaisi Standards Track RFC:nä jälleen yhden TLV-tyylin metaformaatin, tällä kertaa nimeltään CBOR. Tuo tuli vähän yllättäen, kun konsensus on ollut ettei mun-omia-formaatteja enää suosittaisi, ja lähestymistapa iskee niinkin lähelle ASN.1:tä. Mutta onhan tuo kaunis formaatti ja varsinkin helppo koodata. Ehkäpä sillä todella on valoisa tulevaisuus varsinkin sulautetulla puolella.

Oli miten oli, CBOR palautti minut vanhoihin mietteisiini tiedostoformaateista, ja tulipa sitten luettua jopa ASN.1-speksit pitkästä aikaa läpi. Suureksi yllätyksekseni tajusin BER:in implisiittisistä tageista ensimmäistä kertaa mistä niissä itse asiassa on kysymys. Kun tuo hämmennys on kypsynyt nyt pari viikkoa, tekee mieli sanoa muutama sana self-describing -metaformaateista ylipäänsä. Eli lähdetäänpä liikkeelle vaikkapa siitä mitä niillä oikeastaan tavoitellaan ja mitkä niiden vaihtoehdot tavallisesti ovat. Yleisesti listattuja hyötyjä ovat ainakin:

  • laajennettavuus ja laajennosten yhteensopivuus
  • geneeristen kirjastojen, parserityökalujen ja vastaavien mahdollisuus
  • standardoinnin ja kontrolloitujen sanastojen yhteensopivuushyödyt kun laajennoksia kuitenkin tehdään
  • hallitun redundanssin tuoma vikasietoisuus
  • pointteriformaattien ja skippaamisen tehokkuusedut

Muuten hyvä, mutta kaikki nuo hyödyt joko ovat hieman kyseenalaisia, tai ne voisi toteuttaa helpomminkin, tai ne ovat puolitotuuksia. Ongelma näkyy parhaiten kun ajattelee yhtä kolmesta asiasta: sitä mitä parseri tarvitsee ymmärtääkseen formaatin ja mitä ei, sitä mitä kaikkea ainakin joissain tämän sortin standardeissa on voitu jättää pois ilman että ne lakkaavat palvelemasta tarkoitustaan, ja sitä mikä mahtaisi olla se sovellus jota varten tietty ominaisuus on rakennettu formaattiin sisään. Järjestyksessä, nuo syyt ovat hataria koska:

  • Laajennettavuus hoituisi ihan vain versioimalla formaatti. Yhteensopivuus onnistuisi jo sillä että jokainen laajennos vain tulisi tiettyyn tunnettuun paikkaan edellisiin nähden, eli siis luultavimmin tietorakenteen perään. Niin kauan kun yksikään palasista ei luottaisi siihen että jokin tietorakenteen osa rajautuu tiedoston loppuun, parseri kyllä tietäisi mistä seuraava pala alkaa.
  • Geneerisyydessä on kyllä hyötynsä eritoten debuggauksessa, mutta siihen se miltei sitten jääkin. Kirjastotasolla itse asiassa olisi usein helpompaa vain standardoida käytetyt datatyypit mutta jättää TLV-kolmikosta tyyppi ja pituus implisiittisiksi. Meinaten jos parseri on osa ohjelmaa joka tekee datalla jotain hyödyllistäkin, sen pitää joka tapauksessa ymmärtää geneerisen formaatin lisäksi myös tiedon semantiikka jota geneerisellä parserilla ei voi olla täysin hallussaan olematta jo yhdenlainen erikoistunut sovellus. Poikkeuksena tähän juuri ja juuri voisivat olla erilliset skeemakielet kuten XSchema, DFDL ja vastaavat, mutta niidenkään ontologinen anti ei ulotu yleensä läheskään riittävän syvälle että geneerinen parseri voisi kertoa paljon enemmän kuin lisätä tietorakenteeseen kentännimet ja kommentteja. Nuo hyödyt sitten toteutuvat jo sinällään esim. tekstipohjaisissa formaateissa, ja olisivat toteutettavissa täysin niinkin että geneerinen parseri lukee geneeristä kielioppia joka ei tiedä yhtikäs mitään TLV-rakenteista; vrt. ohjelmointikielten kieliopit ja parserigeneraattorit, kun eihän esim. C-sorsassa mitään TLV-rakennetta ole.
  • Yhteensopivuushyödyt taas menisivät standardoimalla tyypit ja rakenteet, koodaamatta niitä itse tietomuotoonkin. Itse asiassa jos teet molemmat, yleensä tulee jossain se kulmatapaus vastaan että joudut kuitenkin laajentamaan tyyppivalikoimaa, niitä varten sisällytät koodaukseen catch-22 -palikan joka on ihan vain tyypitön tavujono, ja yhtäkkiä oikeastaan kaikki voitaisiin yhtä hyvin hoitaa ihan vain tuota kaikenkattavaa poikkeusta hyödyntäen. Tuolla argumentilla esim. ei ole mitään järkeä tyypittää mitään muuta kuin ASN.1 BER:in ylin taso, ja mennä siitä alaspäin implisiittisillä tageilla; hyvässä tyylissä ei edes niillä koska jos (miltei) kaikki on kuitenkin vain tavujonoja, (miltei jokainen) tyyppikenttä tulee hyödyttömäksi, kuten suurin osa pituuksistakin.
  • Vikasietoisuuden voi toteuttaa paljon säännöllisemminkin ja tehokkaamminkin, yhdistellen virheenkorjaavia koodeja ja synkronisaatiolippuja järkevällä tavalla. TLV-rakenteet kun sinällään ovat varsin herkkiä virhebiteille, eritoten pituuskentissä.
  • Datan valikoivassa lukemisessa on tietty pointtinsa, mutta itse asiassa paras tapa toteuttaa sen edut ei suinkaan ole mahdollistaa skippaus seriaalisessa datavirrassa, vaan keskitetty hakemisto josta näkyy suoraan minne pitää seekata myöhempienkin datalohkojen kohdalla. Skippauksen kuluhan kuitenkin varsinkin isoissa tiedostoissa on seek-latenssia, ei bandwidth-limittiä, jolloin hajautettujen pointteriketjujen (mitä TLV-rakenteen pituuskentät ovat; ne ovat pohjimmiltaan epätasainen skip-lista) seuraaminen aiheuttaa hakemistoon/indeksiin nähden vältettävissä olevia viiveitä.
  • Jotta datan koodaukset jotka eivät ole suorastaan monimerkityksisiä, mutta näyttävät alkuun—siis vilkuilematta pidemmälle tietojonossa—siltä, voitaisiin saattaa helposti parsittaviksi, usein tekee mieli laittaa ennakoiva merkki jonoon joka vähentää lookaheadin tarvetta. Vaan todellisuudessa kaikki moiset tietomuodot voidaan myös koodata niin että tällaista tilannetta ei tule eteen, ihan vain muuttamalla tietomuodon kuvausta ja siihen liittyvän parserin rakennetta.

Todellisia argumentteja siis oikeastaan ovat vain:

  • Kompositionaalisuuden edut eritoten rikkaan sisällön kanssa. Joo-o, on se parempi että softan voi jakaa pieniin erikoistuneisiin palikoihin ja geneeriseen multiplekseriin joka yhdistää ne, jotta kaiken softan ei tarvitse ymmärtää kaikkea samaan aikaan, ja palikoista voi kasata näppärästi domain-spesifisiä kokonaisuuksia. Mutkunmutkun, ne OpenDocit, COM/OLE:t ja muut eivät kyllä nekään koskaan sillä softapuolella ottaneet riittävästi tulta, eikä tuon sortin tagaus missään yleisesti käytössä olevassa container-formaatissa edellytä kuin yhden ainoan tason, syvien TLV-rakenteiden asemesta...
  • Se että sattumalta käteenosuvasta esimerkistä voi oppia ilman dokumentaatiota ja sen sisäistämistä, kun osa skeemasta on työnnetty siihen väkisin sisään. Tämä on todellinen hyöty kuten HTML:n historiasta tiedetään, mutta samalla aika kova hinta siitä että pahimmillaan kymmeniä prosentteja kokonaistilasta menee toistuvasti saman rakenneinformaation kantamiseen...
  • Sekä se että tietyissä rajatuissa tapauksissa tyyppipolymorfismi edellyttää valintojen eksplisiittistä tagausta. Tämä on haudanvakava juttu, mutta yllättävänkin harvinainen. Joo-o, niitä esimerkkejä on joissa vaikkapa ASN.1:n CHOICE-rakenne on välttämätön ja se pitää väkisin kääntää tägätyksi unioniksi koodauksessa. Mutta moniko oikeasti vapaaehtoisesti työntää formaattinsa yhdenkään tällaisen rakenteen, pakonkaan edessä mielellään alistaisi koko formaattinsa koneiston sille, tai törmää siihen edes 1/20 ajasta?

Eli siis oikeastaan voitaisiin ehkä jopa väittää, että kaikki ne normaalit syyt mennä TLV-formaatteihin ovat jälkikäteisoikeutusta, ja todellinen syy on ihan vain abstraktissa kauneudessa tai standardoijan hybriksessä. No kidding, ja sanon näin tyyppinä joka kärsii siitä kaikkein yleisimmästä ja hirveimmästä formaattisuunnittelupakkomielteestä.

Kun nyt sitten kuitenkin kärsin tuosta taudista, mikähän tästä kamaluudesta olisi ehkä sittenkin pelastettavissa, missä tilanteessa, ja miksi? Onko sittenkin jokin sovellus, syy tai arkkitehtuuri jossa jonkinlainen TLV-ajattelu on aidosti oikeutettua? Jos on, minkälainen muoto sen pitäisi ottaa selvitäkseen ylläolevasta kritiikistä? Vihjaako tuo jotain laajempaakin sellaisen formaatin vaatimista asioista, eli siis lisäyksiä koodausmalliin?

Muutamia ennakkoehtoja nähtävästi ainakin olisivat:

  • Koodatun tiedon pitää palvella useita eri tarkoituksia samaan aikaan. Jos näin ei ole, kiinteä parseri ratkaisisi jo ongelman täysin. Container-formaatit ovat tästä yksi esimerkki, kun niidenhän on tarkoitus ratkaista se yleisempi ongelma joka liittyy synkronisoituun multimediaan yli tietyn yksittäisen sovelluksen. Ne ratkaisevat pohjimmiltaan arvaamattomien tietojen multipleksauksen yhteen datavirtaan, ajassa.

    Tämä kohta myös vihjailisi, että formaatin pitäisi kenties ratkaista hieman yleisempiäkin ongelmia kuin vain tuo aika, jos se ei halua keksiä container-pyörää uusiksi.

  • Sovelluksen pitää olla sellainen että tieto voitaisiin järjestää eri tavalla tai sen tarkka alajoukko valita toisin hyvästä syystä ja riippumattomasti alkuperäisestä sovelluskehittäjästä. Tästä ehkä parhaana esimerkkinä ovat relaatiotietokantojen käyttämät levyformaatit: kyseisen mallin pyrkimys loogisen ja fyysisen datamallin eroon vaatii, että data on uudelleenjärjestettävissä ilman että se menettää merkitystään, jolloin käytännössä kaikesta mitä levyllä näkyy tulee unioneita, ja rakenteet on pakko tagata eksplisiittisesti jotta tiedettäisiin missä järjestyksessä-muodossa ne kulloinkin ovat.

    Tämä kohta kuitenkin viittaa myös, että moni tuon looginen–fyysinen -eron ongelmista pitäisi ratkaista koodauksen sisällä, ja sitähän ei tee kunnolla edes itse relaatiomalli. Se vihjaa myös sellaisia paljon vaikeampia kysymyksiä kuin että kuinka pitkälle koodausmetologian oikeastaan pitäisi sanella yksittäistä koodausta, mitkä ne mahdolliset ratkaistavat ongelmat kokonaisuudessaan olisivat, miten tuoda framework-kehitys mukaan tietomalliin esim. käyttötapauksina, missä menevät ennustettavan rajat vaikkapa tiedon- vs. tietämyksen esityksessä, kuinka vahvoja parserimalleja yleensä voidaan käyttää (Turing-täydellisyyshän ainakin tarkoittaisi että ympätään sovellus mukaan dataan...), jne, ad nauseam...

  • Jos halutaan aina väkisin opettaa esimerkistä, miksei sitten mennä tekstipohjaisella formaatilla ja kompressiolla? Tai ainakin jos mennään formaalimmalla skeemalla, halutaanko se nyt kuitenkaan ympätä datan sekaan vai olisiko parempi erottaa se? Ja jos se erotetaan, no eikö sitten pitäisi katsoa onko niin tehty jo vaikka RDF:ssä, ProtocolBuffereissa, DFDL:ssä ja muissa paremmin? Mitä lisäarvoa me nyt oikein haetaan tällä kaikella?

Niinpä olen itse vähitellen taipunut siihen suuntaan, että mennääs takaisin fundamentteihin ja yritetään ratkaista se kaikkein isoin ja yleisin ongelma kerralla, joskin tietty pala palalta. Hyvä lähtökohta tuolle nähdäkseni olisi relaatiomalli, johon sitten yhdistettynä ainakin huolella harkittu loogis-semanttinen tietomalli à la RM/T ja RDF, erotetut skeemat jotka kulkevat datan mukana, tai siis itse asiassa voidaan korvata myös yksikäsitteisillä viitteillä online-skeemoihin, nimiavaruushallinta osana tuota, kohtuullisen laaja kirjo perusdatamalleja ja käyttötapauksia joita koodaus tukee—esim. mikään geneerinen koodaushan ei nyt tue kunnolla yleisiä graafeja—järkevät tyyppi- ja koodausontologiat niin että automaattinen prosessointi tulee mahdolliseksi ylätyyppien tasolla vaarantamatta ilmaisutarkkuutta, ja niin edelleen, virheenkorjaukset, hakemistomekanismit, deltakoodaukset ja vastaavat toteutettuna yleistasolla, ja niin edelleen. Kuitenkin muodossa joka sallii aina lyhentämisen ja pakkauksen myös.

Toisin sanoen, tehdään se kerralla kunnolla ja koodataan se myös niin ettei kenelläkään taatusti ole valittamista tietomalli- kuin ohjelmistotuestakaan, toisin kuin nyt. Tuollainen geneerinen dataformaatti olisi hyvin eri näköinen kokonaisuudessaan kuin nykyiset yritelmät, ja aivan saatanallisen paljon laajempi kokonaisuus kuin mikään mitä edes mikkihiireläiset ovat koskaan yrittäneet. Tuntuvasti häijympi kokonaissysteemi valmistuttuaan kuin edes CORBA Persistence.

Mutta mikään mahdottomuus se ei olisi, jos hommaa vain jaksaa ajatella tarpeeksi pitkään—sen parikytävuotta ainakin mitä minä jo, tai ehkä tuplasti sen. Tai ainakin jaksan edelleen uskoa niin.

No comments:

Post a Comment