Category: Technology

Posted on in Technology, NoSQL

Näyttää siltä, että W3C on tappanut Web SQL Database -standardin. Syy on se, että kaikissa selaimissa on käytetty samaa SQLite-backendia, mutta standardi ei voi pohjautua ainoastaan yhteen implementaatioon.

Minusta tämä on hyvä asia. SQL:llä on historiallista arvoa datankäsittelykielenä, mutta laajemman kokonaisuuden kannalta olisi parempi, että siitä päästäisiin eroon. Kokemus on osoittanut, että erilaiset ORM-lähestymistavat datamalleja käyttäen mahdollistavat selkeämmän ja turvallisemman kehityksen. SQL:n antaminen aloittelevan kehittäjän käyttöön on vähän niinkuin antaisi tulitikkurasian lapselle.

Olisi parasta, jos standardi tapa tallentaa dataa selaimiin olisi dokumenttipohjainen, kuten CouchDB:n ja MongoDB:n yksinkertaiset ja joustavat JSON-datamallit ovat. Ongelmaksi jääkin lähinnä se, miten kyselyt pitäisi tehdä. MongoDB:llä on oma JSON-pohjainen kyselykielensä, mutta se ei ehkä ole ihan kypsä standardiksi vielä.

Posted on in Technology

Tämä blogi pyörii nyt Slicehostilta vuokraamallani virtuaalipalvelimella. Käyttöjärjestelmä ja ympäristö ovat samat kuin ennenkin (Ubuntu 10.10 ja Django 1.2.3), mutta taas kerran saa mielellään kommentoida, jos jotain on mennyt rikki.

Olen käyttänyt Slicehostia parin kuukauden ajan muihin privaattiprojekteihin ja voin suositella sitä erityisesti "kotikäyttöön". Edullisin slice (256MB RAM, 10GB HD) maksaa $20/kk eli noin 14.60 EUR/kk. Itselläni on nykyisin 512MB RAM, 20GB HD. Päivitys suurempaan sliceen sujui automaattisesti ja ongelmitta Ubuntun kanssa.

Slicehostin idea on, että asiakas saa virtuaalipalvelimen täysin omaan hallintaansa. Sliceen asennetaan haluttu käyttöjärjestelmä, jota voi päivitellä ja räätälöidä vapaasti. Omaani asensin alkujaan Ubuntu 10.04:n ja päivitin sen myöhemmin 10.10:een. Palomuurin ja haluamansa ohjelmistot voi luonnollisesti konfiguroida omien tarpeiden mukaan.

Slicehostiin voi avata tilin käyttäen referral-koodiani klikkaamalla tästä. Minä saan siitä aikanaan jotain bonusta.

PS. Slicehost on Rackspacen tytäryhtiö, joten sillä on tiettyä luotettavuuttakin taustalla.

Posted on in Technology

Computerworld UK raportoi, että Web Services Interoperability Organization lopettaa toimintansa. Jutussa viitataan siihen, miten SOAP ei ole koskaan kunnolla sopinut webin toimintaperiaatteisiin. Se on ollut enemmänkin keino yrittää tuoda vanhoja CORBA-konsepteja väkisin Internetiin ja luoda erilaisia vendor-lockin-riippuvuuksia epäyhteensopivien implementaatioiden myötä.

Toivottavasti SOAP kuolee pian lopullisesti ja voidaan siirtyä yksinkertaisempiin, tehokkaampiin ja moderneihin REST-rajapintoihin.

Posted on in Technology, Django

Blogini pyörii nyt Nginx- ja Gunicorn-palvelinten päällä entisen Apachen ja mod_wsgi:n sijaan. Sovellus on edelleen Django-pohjainen. Periaatteessa mitään eroa ei pitäisi näkyä, mutta saa tietysti mielellään kommentoida, jos jotain outoa esiintyy.

Tarkoitus on hankkia kokemuksia siitä, onko Nginx+Gunicorn tehokkaampi ja ketterämpi ympäristö pyörittämään Django-sovelluksia kuin Apache. Apachella on melkoinen kasa historiallista painolastia kannettavana, eikä sen arkkitehtuuri ole enää kovin moderni.

Nginx on erittäin tehokas, event-pohjainen HTTP-palvelin. Sen tarkoitus on tässä yhteydessä tarjoilla staattiset tiedostot sekä proxyttaa Django-sovellukseen kohdistuvat pyynnöt Gunicornille.

Gunicorn on Python WSGI -palvelin jonka luvataan olevan kevyt ja nopea. Se pyörittää joukkoa valmiiksi forkattuja prosesseja, jotka käsittelevät Django-sivupyynnöt. Gunicorn tarvikseen avukseen Nginxin, joka huolehtii esimerkiksi HTTP-liikenteen puskuroinnista hitaille asiakkaille.

Gunicorn on helppo virittää Upstart-jobiksi, jolloin se käynnistyy Ubuntun bootissa automaattisesti. Oma jobini näyttää tältä:

description "kfnet gunicorn server"
author      "kennu"
 
start on (local-filesystems and net-device-up IFACE=eth0)
stop on shutdown
expect fork
respawn 
respawn limit 10 5
 
script
    HOME="/var/www/kfalck.net/kfnet"
    CONF="$HOME/conf/gunicorn.conf"
    cd "$HOME"
    exec /usr/bin/gunicorn_django -c "$CONF"
end script

Olen havainnut Upstartin olevan paljon kätevämpi tapa toteuttaa palvelinsovelluksia kuin perinteiset rc.d-skriptit. Jobi laitetaan vain esimerkiksi tiedostoon /etc/init/gunicorn-kfnet.conf ja sen jälkeen palvelimen voi käynnistää sanomalla

start gunicorn-kfnet

Tai pysäyttää sanomalla:

stop gunicorn-kfnet

Bootissa se käynnistyy automaattisesti sitten, kun tiedostojärjestelmät ja verkko ovat käytettävissä.

Posted on in Technology, Django

Auto-increment-kentät ovat perinteinen tapa saada tietokantaan juokseva laskuri. Yleensä niitä käytetään primary key -sarakkeen arvona. Samalla ne kuitenkin muodostavat tietokannan skaalautuvuuteen pullonkaulan, jota on todella vaikea korjata jälkeenpäin. Lisäksi ne monimutkaistavat sovellusta tarpeettomasti.

Niin millä tavalla monimutkaistavat? Yleisin ongelmatilanne on sellainen, että vaikkapa blogiartikkeliin liittyvät kuvatiedostot pitäisi saada tallennettua levylle. Niiden tiedostonimessä pitäisi käyttää artikkelin ID:tä, jotta tiedosto ei mene päällekkäin muiden kanssa. Mutta silloin blogiartikkeli täytyy ensin tallentaa tietokantaan ilman kuvia, jotta sen auto-increment-ID saadaan selville. Vasta sitten kuvat voidaan tallentaa levylle sekä päivittää niiden tiedostonimet artikkeliin tietokannassa.

Monimutkaisemmissa järjestelmissä näitä tilanteita tulee vastaan vähän väliä. Auto-incrementin käyttö "pakottaa" tekemään aina ensin INSERT-lauseen päätietokantaan, jotta uusi ID saadaan selville. Kun tämä INSERT ei voi vielä sisältää kaikkea tarvittavaa tietoa, joudutaan tekemään jälkeenpäin turha UPDATE, jolla päivitetään puuttuvat tiedot.

Skaalautuvuuten kannalta auto-increment muodostaa "single point of failure" -pisteen. Koska numeron pitää olla juokseva ja uniikki, se on pakko generoida aina samalla palvelimella. Tätä voi yrittää kiertää käyttämällä esimerkiksi segmentointia (toisella palvelimella auto-increment-arvot alkavat 1 miljardista) tai jakojäännöksiä, mutta tällaiset ratkaisut ovat aina hankalia ja vaativat virittelyä, jos palvelimia lisätään.

UUID tulee apuun

Olen itse tullut siihen tulokseen, että auto-increment-kentistä kannattaa luopua ja siirtyä kokonaan UUID:den käyttöön. UUID on 128-bittinen numero, joka on yleensä helpointa tallentaa 32-merkkisenä heksakoodina, kuten esimerkiksi "57a0809876974df79983f8a97b260607".

Viralliseen määritelmään kuuluu myös muutama tavuviiva, jolloin UUID esitetään muodossa "57a08098-7697-4df7-9983-f8a97b260607". Niitä on kuitenkin turha tallentaa tietokantaan. Viivat voi aina lisätä tilanteissa, joissa jokin protokolla niitä edellyttää. URLeissa viivaton muoto on omasta mielestäni siistimpi ja kompaktimpi. Se on myös ihan käytännössä helpompi copy-pasteta, koska viivattoman arvon saa valittua tuplaklikkaamalla. ;-)

Pythonissa UUID:t voi generoida uuid-moduulilla. Itse käytän tavallisesti uuid.uuid4()-funktiota, joka arpoo täysin satunnaisen luvun. Tällaisessa UUID:sä ei ole mukana esimerkiksi kellonaikaa tai koneen MAC-osoitetta tai muita identifioivia lähdearvoja. Kahden satunnaisen UUID:n päällekkäisyysriski vuositasolla on sama kuin putoavan meteoriitin alle jääminen, jos vuodessa generoidaan kymmenen tuhatta miljardia UUID:tä. Itselleni tämä varmuus riittää, kun en työskentele pankissa.

Tietokantojen yhteydessä UUID voidaan generoida etukäteen ennen datan tallentamista tietokantaan. Dataobjektia voidaan siis käsitellä muistissa niinkuin se olisi jo olemassa. Kuvatiedostot voidaan nimetä UUID:n mukaan valmiiksi oikein ja mahdolliset viittaukset muihin järjestelmiin (esimerkiksi NoSQL-kantoihin) voidaan valmistella hyvissä ajoin. Sitten valmis objekti INSERTataan kerralla päätietokantaan ja voidaan olla varmoja siitä, että se on heti käytettävissä kaikkine oheisriippuvuuksineen.

Skaalautuvuuden kannalta tällainen UUID-perusteinen dataobjekti voidaan tallentaa mihin tahansa tietokantaan, jos käytössä sattuu olemaan useita rinnakkaisia palvelimia. Tämä helpottaa esimerkiksi horisontaalista partitiointia ja master-master-replikointiin perustuvia järjestelmiä. Objekteja voidaan myös siirrellä tietokannasta toiseen ilman riskiä ID:n päällekkäisyydestä.

Auto-increment vs. UUID

Tietokannan suunnittelussa täytyy huomioida se, että UUID-primary-key-sarakkeet eivät ole enää numeerisessa järjestyksessä. Sen vuoksi tarvitaan aina erillinen "pub_date"-kenttä, jonka perusteella rivit voi järjestää. Päivämääräkentän käyttäminen sorttaamiseen on muutenkin siistimpää kuin auto-increment-kenttiin luottaminen, sillä primary keyn arvoa on jälkeenpäin vaikeaa tai mahdotonta muuttaa.

UUID-kenttien käyttö tuo tietokantaan myös uuden tietoturvakerroksen. Juoksevat auto-increment-arvot tekevät yleensä helpoksi arvata muita ID:itä tai käydä läpi esimerkiksi kaikki ID:t ykkösestä miljoonaan. UUID:itä taas on käytännössä mahdoton arvailla, eikä sillä ole mitään merkitystä, vaikka olisikin nähnyt yhden.

URLeissa UUID on tietysti vähän rumempi kuin perinteinen juokseva auto-increment-numero. Mutta käyttäjille näkyvät URLit pitäisi muutenkin suunnitella niin, että niissä näkyy joku merkitykselinen tunniste. Esimerkiksi omalla webbisaitillani URLeissa käytetään aina päivämääriä, otsikoita tai kategorioiden nimiä, joten taustalla käytetyt ID:t eivät vaikuta mitään.

UUIDField Djangossa

Lopuksi vielä esimerkki UUIDField-kentästä Djangolle. Tätä kenttää voi käyttää omien tietomallien primary key -sarakkeena. Alkuperäinen implementaatio on kopioitu jostain päin nettiä ja sitä on hiukan viritelty.

from django.db import models
import uuid

class UUIDField(models.CharField):
    """A UUID based primary key field."""
    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = kwargs.get('max_length', 64)
        kwargs['blank'] = True
        return super(UUIDField, self).__init__(*args, **kwargs)
    
    def pre_save(self, model_instance, add):
        if add:
            value = getattr(model_instance, self.attname) if hasattr(model_instance, self.attname) else None
            if not value:
                value = uuid.uuid4().hex
                setattr(model_instance, self.attname, value)
            return value
        else:
            return super(models.CharField, self).pre_save(model_instance, add)

Oma tietomalli määriteltäisiin sitten näin:

class Article(models.Model):
    uuid = UUIDField(primary_key=True, editable=False)
    title = models.CharField(max_length=254)
    # ...

Django huomaa automaattisesti, että mallille on määritelty primary_key, eikä se silloin lisää tauluun oma id-kenttäänsä. Periaatteessa kentän nimeksi voisi antaa myös "id", mutta itse olen käyttänyt nimeä "uuid" sen takia, että muistan ohjelmoidessa helpommin, minkä tyyppinen kenttä on.

Kokemusteni perusteella kaikki Django 1.2.x:n mukana tulevat sovellukset toimivat ongelmitta UUID-kenttien kanssa. Erityisesti Djangon sisäänrakennettu admin-käyttöliittymä toimii täydellisesti. Joissakin 3rd party -sovelluksissa voi teoriassa olla ongelmia, jos ne olettavat (tyhmästi), että primary key on integer-tyyppinen. Vielä tällaista ei ole kuitenkaan tullut vastaan.

Posted on in Technology, Git

Windows-käyttäjiä saattaa kiinnostaa, että TortoiseGit-projekti alkaa vaikuttaa aika toimivalta GUI-ratkaisulta. Se integroituu Windows Exploreriin samaan tapaan kuin sen Subversionia käyttävä vastine TortoiseSVN.

TortoiseGitin avulla Git-projekteihin saadaan mukaan sellaisetkin Windows-käyttäjät, jotka eivät tunne oloaan kotoisaksi komentorivin ja msysgitin parissa.

Peruskäyttö vaikuttaa hyvin samankaltaiselta kuin TortoiseSVN:nkin. Etärepository kloonataan omalle koneelle valitsemalla kontekstivalikosta "Git Clone". Tehdyt muutokset kommittoidaan valitsemalla samaisesta valikosta "Git Commit" ja klikkaamalla vielä commitin jälkeen Push, jolloin ne tallentuvat palvelimellekin. Etärepositoryn muutokset saa helposti päivitettyä omalle koneelle valitsemalla silloin tällöin valikosta "Pull". Oman työhakemiston branchiakin on helppo vaihtaa "Switch/Checkout"-valikosta, joka näyttää luettelon käytettävissä olevista haaroista.

Työskentelyn helpottamiseksi kannattaa luoda PuTTYlla ssh-avainpari ja lisätä julkinen avain Git-repository-palvelimelle ~/.ssh/authorized_keys-tiedostoon. Salaisen avaimen voi sitten määritellä TortoiseGitin asetuksiin, jolloin salasanaa ei tarvitse enää näpytellä joka kerta pushatessa tai pullatessa.

Posted on in Technology, Business

Osallistuin eilen eReading-seminaarin, jossa käsiteltiin sähköisen lukemisen nykytilaa ja tulevaisuuden suuntia Suomessa. Eräs mieleenpainavimpia lausahduksia oli erään kirjakustantajan edustajan lausunto, joka meni suunnilleen näin:

"Haluamme välttää musiikkiteollisuuden virheet. Siksi kaikki sähköiset kirjat pitää suojata DRM:llä."

Toisin sanoen kirjamiehet ovat onnistuneet ymmärtämään täysin päälaelleen sen, mikä musiikkibisneksessä mokattiin. Siellähän yritettiin monen vuoden ajan käyttää DRM:ää, ja juuri se sai ihmiset kääntymään laittomien lähteiden puoleen, koska käyttökokemuksesta tuli niin huono.

Kokeilin ostaa Akateemisen kirjakaupan uudesta latauspalvelusta sähköisen kirjan.

Kokeilu 1: Osto iPadilla. Tilaaminen ja maksaminen onnistui, mutta kun kirjaa oli tarkoitus alkaa lataamaan iPadiin, tulikin vain virheilmoitus, että tiedostomuoto on tuntematon. Ei minkäänlaisia varoituksia tai ohjeita, miten pitäisi toimia. Eikä kirjaa.

Kokeilu 2: Osto tietokoneella. Olin jo maksanut kirjan, joten sen latauslinkki oli valmiina henkilökohtaisessa kirjastossani. Tällä kertaa lataus pullautti minulle tietokoneeseeni tiedoston nimeltä URLLink.acsm. Se sisältää mystistä dataa, allekirjoittajana SecuryCast Oy. Ei kirjaa.

Kokeilu 3: Koska olen jonkin verran kokenut käyttäjä, tajusin, että tarkoitus on asentaa Adobe Digital Editions -sovellus, joka pystyy sitten avaamaan nuo .acsm-tiedostot ja lataamaan varsinaisen kirjan Akateemiselta. Ensin pitää tietenkin rekisteröidä oma Adobe ID ja kirjautua sillä sisään ja niin edelleen.

Kaiken tämän jälkeen kirja vihdoin aukesi tietokoneeseeni (iMac), mutta vain Adobe Digital Editionsin sisällä. Ei mitään mahdollisuutta siirtää kirjaa iPadiin tai Kindleen.

Tässä ollaan nyt täsmälleen samassa tilanteessa kuin musiikkibisnes oli jokunen vuosi takaperin, ja ollaan toistamassa täsmälleen samat virheet kuin silloinkin.