Google+ Followers

woensdag 28 september 2016

Why I prefer PostgreSQL over MySQL: functions either work, or they

Today I got one of those WTF moments.

From the MySQL manual:

ALTER USER was added in MySQL 5.6.6. However, in 5.6.6, ALTER USER also sets the Password column to the empty string, so do not use this statement until 5.6.7.
That's MySQL speak for "yeah, we released something that can break your application and potentially open it up to hackers, so uhm... just don't use it, ok?It'll be fine..."

In PostgreSQL, features are either completed before release, or they are made safe by giving an error message when the incomplete part is accessed.

These hidden "features" that can cost hours of debugging and restoring and, potentially, a large data-leak, simply don't exist in PostgreSQL.

donderdag 25 februari 2016

Facebook... yeah...

So. Yeah... facebook eh?

You can use it to login on other websites.

Great!

Except... facebook has a bug in its authentication, which means that roughly 2% of times they don't send the required data in their response and sometimes they will send garbage instead.

But hey, it's facebook, good-old Zucky will fix it in no time, right?

Not really. Reports of this bug started back all the way to 2011. Five years now...


So, yeah. Facebook...

zaterdag 20 februari 2016

SQL: Wat te doen met een trage OR query?

Een query die in de WHERE clausule een OR gebruiken in de vorm van WHERE foo=1 OR foo = 5 worden vaak traag omdat de database geen index kan gebruiken om aan de OR te voldoen.

De query is wel snel  als je alleen zoekt naar foo=1 of alleen zoekt naar foo=5. De oplossing is dan ook simpelweg: UNION.

Een UNION voegt de uitkomst van twee queries samen en twee snelle queries samenvoegen gaat sneller dan één trage query uitvoeren.

Dus iets als dit:

SELECT * FROM foo WHERE bar=1 or bar=5;

wordt iets als:

SELECT * FROM foo WHERE bar=1
UNION
SELECT * FROM foo WHERE bar=5

woensdag 23 december 2015

Python multithreading in een notendop

Een van de vele redenen waarom ik PHP heb laten vallen voor Python is dat dingen die in PHP praktisch onmogelijk waren in Python een peulenschil zijn.

Een van die dingen is multithreading; een taak verdelen over meerdere CPU's.

Onderstaande is een tekstboek voorbeeld van multithreading in Python voor Python3. Knipt en plak en roep aan met python3.4 geplaktscript.py


#!python3

"""
Multi-threading werkt met een thread pool en een data-queue.
De thread pool wordt gevuld met een aantal 'workers' die het echte werk doen,
zij halen de data waar ze mee moeten werken uit een 'queue'.
De workers gaan door tot ze geen data meer in de queue zien en als alle workers klaar zijn
stopt het script.

Zoals het script nu is maakt het vier workers aan en vult het de queue met 20 items.
De 'do_work' method wacht .1 seconde dus de twintig items in de queue worden uitgevoerd in (20/4)*.1=0.5 seconde.
Verander het aantal workers van 4 naar 10 en zie dat het script nu klaar is in 0.2 seconde (20/10)*.1=0.2

Noot: je kunt dit niet ongestraft blijven opschalen. Als het werk dat je doet erg CPU-intensief is dan kun je niet meer workers opstarten
dan je cores hebt, anders gaan de taken alsnog elkaar in de weg zitten en dan helpt het niet meer.
Op een dedicated machine kun je gaan tot cores-1 workers, zodat er een één core vrij blijft voor het moederprocess en overige taken.

"""
import threading
from queue import Queue
import time

q = Queue()

# Maak een lock object om de uitvoer te synchroniseren, is alleen nodig voor dit voorbeeld.
lock = threading.Lock()


def do_work(item):
    """
    De do_work method simuleert het verwerken van de data uit de queue
    Hij wacht nu alleen 0.1 seconde.
    """
    time.sleep(.1)

    # Zorg dat de threads op elkaar wachten bij het printen.
    with lock:
        print(threading.current_thread().name, item)


def worker():
    """
    De worker method haalt data uit de queue en laat de data verwerken door de 'do_work' method,
    dat zou dus ook een method in een class van je applicatie kunnen zijn.
    """
    # Ga door tot de queue leeg is.
    while True:
        # Haal de volgende taak uit de wachtrij
        item = q.get()

        # Verwerk de data.
        do_work(item)

        # Sluit de taak.
        q.task_done()


def main():
    # Maak een thread-pool met een aantal threads die de 'worker' method gaan aanroepen.
    for i in range(4):
        t = threading.Thread(target=worker)
        t.daemon = True  # thread stopt als het moederproces stopt.
        t.start()

    # Vul de queue met data waar de workers mee gaan werken.
    for item in range(200):
        q.put(item)

    # Noteer de huidige tijd
    start_time = time.perf_counter()
    # Start de thread-pool
    q.join()
    # Print de verstreken tijd
    print('time:', time.perf_counter() - start_time)


if __name__ == '__main__':
    main()

woensdag 9 december 2015

CSV inlezen met Python

Python heeft natuurlijk CSV functies, maar in tegenstelling tot PHP heeft Python ook standaard een Sniffer class die het formaat van een CSV bestand kan analyseren. Dat betekent dat je voor goed-geformatteerde CSV bestanden niet meer hoeft op te  geven welke scheidingsteken en welke quote er wordt gebruikt.

Een voorbeeldje:

import csv


class MyCSVReader():
    @staticmethod
    def read(filename):
        with open(filename, 'r') as csvfile:
            # Detecteer het scheidingsteken en de gebruikte quotes.
            dialect = csv.Sniffer().sniff(csvfile.read(1024))

            # Breng de filepointer van het bestand terug naar het begin
            # zodat het niet opnieuw geopend hoeft te worden.
            csvfile.seek(0)

            # Maak een csv reader met het gevonden dialect
            my_reader = csv.reader(csvfile, dialect)

            # Sla de header over
            next(my_reader)

            # Lees de rest van het bestand.
            rows = []
            for row in my_reader:
                # Print de eerste twee kolommen en vat kolommen 3 t/m 6 (2 t/m 5) samen in een string die PostgreSQL begrijpt als een "int[][]".
                rows.append([row[0], row[1], '{' + ','.join(row[2:5]) + '}'])
            return rows


def main():
    print('-'*20)
    rows = MyCSVReader.read('komma_tick.csv')
    for r in rows:
        print(r)

    print('-'*20)
    rows = MyCSVReader.read('puntkomma_dubbelquote.csv')
    for r in rows:
        print(r)


# Vertel de commandline-interpreter van Python dat hij moet beginnen met uitvoeren bij de main() functie.
if __name__ == '__main__':
    main()


"""
Inhoud van "puntkomma_doublequote.csv":
"greeting";"test number";"value a";"value b";"value c";"value d"
"hello";1;7;9;6;4
"hi";1;5;8
"goodbye";87;8;7;9;2

Inhoud van "komma_tick.csv"
'greeting','test number','value a','value b','value c','value d'
'hello',1,7,9,6,4
'hi',1,5,8
'goodbye',87,8,7,9,2
"""

donderdag 12 november 2015

Why will OR DIE() not just... well, die?

"or die()" is possibly the stupidest thing in PHP. Every experienced PHP programmer will tell you that it's a bad idea and that PHP supports exceptions. Pretty much every other language has a structure for stopping a script with some sort of ternary OR operator, but you rarely see it used.

The problem with "or die()" is of course that 'die()' literally stops the script, right there and then. But in just about every case I've ever seen, the programmer did not want that, he wanted to stop further processing.

What's the difference? When you "stop further processing", you simply skip that part of the script that you cannot execute because of the error. All the code that is not affected by the error is still executed. This is important because "the rest of the code" includes the code that reports the error that was encoutered. If you just stop the script then no output is generated and your visitor will see a blank page that does not even tell him that there was an error. He cant report the error to you, he can't continue to a different page, he will press F5 to see if the page just failed to load, and when it doesn't he will go to a different website.

Not stopping the script also prevents you from corrupting your data. For example; if your script opens a file to write new content to it, you will typically (not correctly, but typically) open the file, reset it and then start processing and writing new content. If the orocessing fails and you stop the script outright, you will be left with an incomplete file, which breaks other processes. Brilliant.

When you stop further processing, you abort the processing and writing of the new content, and you will logically come to a point where you wonder ök, so what do I do with the fileif I can't update it?" and then you take the proper route where you write the new content to a new file and do a delete/rename once you have confirmed that the new file is correct.

There is no need for or-die() and it really doesn't matter if you think that it's easy to sue during debugging, because you *will* forget a couple of these statements and the *will* break your data.

Yeah, it's won't hapen to you, of course not, untill it does.

Use exceptions. Start your script with proper error handling, it will save your bacon.

woensdag 11 november 2015

PHP forums, worden ze ooit volwassen?

PHPHulp.nl:

Iemand stelt een vraag: "Als ik een selectbox maak met de namen van mijn gebruikers erin dan worden de spaties vervangen door underscores, waardoor ik na het submitten de namen niet meer kan terugvinden in de database. Hoe komt dat?"

De oplossing die het PHP forum voorstelt: "dan moet je het id gebruiken, niet de naam".

Huh? Als spaties niet goed doorkomen moet je iets gebruiken dat geen spaties bevat? Waarom niet gewoon uitzoeken naar waarom die spaties worden vervangen? Want dat is blijkbaar niet verwacht en niet gewenst. *Dat* is dus het probleem.


Maar moet je opletten wat er wordt gezegd als ik dat voorstel: "dankzij de tips die hij van ons heeft gekregen is hij nu wel een stuk verder".

Ja, een stuk verder in de problemen want de data wordt nog steeds verkeerd doorgegeven en de enige indicatie die hij had dat het *fout gaat* is door jullie brilliante voorstel nu onzichtbaar geworden. Goed werk hoor!


Dit oogkleppen, kop in het zand, als ik het niet zie dan is het er niet, gedrag is kenmerkend voor de PHP community. Nadenken lijkt verboden, oorzaken mogen niet worden aangepakt, elk probleem moet worden opgelost via een vorm van symptoombestrijding. Heb je een undefined-variable warning? Dan zet je gewoon je error_reporting lager, want wat voor kwaad kan een undefined variable nou helemaal? Kan je datase iets niet? Dan doe je het gewoon in een scriptje, dat is toch sneller.


"Werken met het id is de netste manier".

Nee, het is de manier die jij hebt aangeleerd gekregen van iemand anders die het zo deed. Een natural-primary-key is het netst en dat deed de poster ook; hij werkte een gebruiker bij aan de hand van zijn gebruikersnaam, zodat je altijd zeker weet dat je de juiste gebruiker aanspreekt. Een surrogate primary key kan op elk moment veranderen, zie de boeken van Joe Celko. Ik heb dat zelf ook meegemaakt bij webshop waarvan een van de leveranciers die een dump/restore had gedaan van zijn database, en bij de restore  de id kolom van de producttabel had overgeslagen zodat alle producten een nieuw id kregen. Gelukkig synchroniseerde de webshop op EAN code plus SKU, maar blijkbaar hebben andere webshops dagenlang de producten verkocht tegen verkeerde prijzen.


Natuurlijk, een surrogate primary key heeft zo z'n voordelen en in de alledaagse praktijk van de gemiddelde website zul je er geen last van hebben, maar *dat was het probleem niet*.

Als je auto niet wil starten als het koud is dan ga je toch ook niet met de fiets tot het weer zomer is?

Klinkt dit gefrustreerd? Vastwel, want ik wordt kenttergek van die houding.

Tuurlijk PHP is bij uitstek een taal voor beginners, maar als je constant wordt omringd door mensen die het zelf ook niet begrijpen en je voorzien van de domste adviezen, dan kom je ook nooit ergens.