martedì 23 dicembre 2008

database, thread e transazioni

Proprio in questi giorni a lavoro mi sono imbattuto in un problema non banale riguardo le transazioni dei database.
Avevo 2 thread A che scriveva su db e B che doveva leggere il dato.
Nel mio flusso ideale la cosa avrebbe dovuto funzionare così


invece dopo qualche ora spesa a cercare di capire come mai B fallisse sempre a leggere i dati e dopo aver controllato l'access log mi sono accorto che B leggeva i dati prima che A li avesse scritti!

quindi succedeva che


Visto che l'ordine di esecuzione non poteva essere previsto ho dovuto creare in B un meccanismo che tentasse di leggere il dato più volte o fino a soddisfare la lettura, o fino a raggiungere un numero massimo di tentativi ed uscire con errore.

quindi ho fatto una cosa del genere


Ma qui sono successe cose sinistre.

Infatti dall'access log risultata che A aveva scritto il dato mentre B che aveva iniziato a leggerlo precedentemente falliva anche quando A aveva terminato la sua scrittura.
Grazie all'aiuto dei miei colleghi più esperti mi è stato spiegato che B iniziava la sua transazione prima della fine della scrittura da parte di A e B, per consistenza, manteneva lo stato iniziale di fallimento di ricerca del dato. Quindi anche se avessi letto quel valore per ore dopo che A lo aveva scritto avrei sempre trovato la situazione iniziale della base dati ovvero nessun dato trovato!



Armato di sana pazienza e spulciando la documentazione del DB (mysql in questo caso ma tutti i moderni db con supporto alla transazioni hanno l'equivalente istruzione) ho trovato alla fine il comando che disabilita questo comportamento ovvero:

set transaction isolation level read committed;

Con questo comando B è stato in grado di accorgersi quando il dato è divenuto disponibile ed io ho risolto il mio gravoso problema.

lunedì 13 ottobre 2008

mysqldump singole insert

Le versioni più recenti di mysqldump creano una insert multipla per gruppi di insert simili. Ad esempio con mysqldump si ottiene:


INSERT INTO `events` VALUES (6582,11,'1223390359:164321',6285,'bla bla bla',10,'2008-10-07 16:39:21','bank_id=60702&charge_id=079208%409001914','BA8CF7E8-947D-11DD-8B00-488D047A8996',NULL,NULL,'079208@9001914'),(6583,11,'1223390362:164327',NULL,'bla bla bla',10,'2008-10-07 16:39:22','bank_id=60702&charge_id=079208%409001914','BB3FCB48-947D-11DD-9E00-488D047A8996',NULL,NULL,'079208@9001914'), ...

aggiungendo il parametro

--extended-insert=false si ottiene

INSERT INTO `events` VALUES (6582,11,'1223390359:164321',6285,'bla bla bla',10,'2008-10-07 16:39:21','bank_id=60702&charge_id=079208%409001914','BA8CF7E8-947D-11DD-8B00-488D047A8996',NULL,NULL,'079208@9001914');
INSERT INTO `events` VALUES (6583,11,'1223390362:164327',NULL,'bla bla bla',10,'2008-10-07 16:39:22','bank_id=60702&charge_id=079208%409001914','BB3FCB48-947D-11DD-9E00-488D047A8996',NULL,NULL,'079208@9001914');
INSERT INTO `events` VALUES (6584,11,'1223390943:167153',NULL,'bla bla bla',10,'2008-10-07 16:49:04','bank_id=60702&charge_id=079208%409001914','15DA559A-947F-11DD-B959-D093047A8996',NULL,NULL,'079208@9001914');

venerdì 3 ottobre 2008

Perl Oneliner

Talvolta mi capitano log del tipo:

2008-10-03 09:28:55,204 [10544] Dada::Rufus INFO final status: @101: uid:'+919791100668' appid:'122301909010729816767' : 'book' -> 'ACK' in 1.120 s.
2008-10-03 09:28:56,320 [9779] Dada::Rufus INFO final status: @56: uid:'+557988275004' appid:'122301908814163698835' : 'book' -> 'NACK' in 4.345 s.
2008-10-03 09:28:57,572 [10551] Dada::Rufus INFO final status: @101: uid:'' appid:'122301909010729816767' : 'capture' -> 'NACK' in 2.106 s.

supponiamo che voglia sapere quali diversi tipi di occorrenza del tipo '@...' ci siano.

In questo caso è semplice perché ce ne sono 2

@101 e @56

ma se il log è lungo parecchie centinaia di mega la cosa non è semplice.

Questa cosa, con le coreutils di linux si possono compiere velocemente, ancor più velocemente usando perl.

per estrarre dalle righe le varie etichette eseguo per da riga di comando facendo tornare con un'espressione regolare le parole che iniziano con '@':

perl -ne ' print "$1\n" if ($_=~/@(\w+)/)'

quindi supponendo che queste righe siano nel mio file di log utilizzo

cat filelog| perl -ne ' print "$1\n" if ($_=~/@(\w+)/)'

però così facendo ottengo una serie di

@101
@56
@101
@...

io voglio conoscere le occorrenze uniche e per questo ecco 2 utilità che tornano alla mano sort e uniq

quindi il mio comando diviene

perl -ne ' print "$1\n" if ($_=~/@(\w+)/)' | sort | uniq

e ottengo la lista di occorrenze uniche ricercata

N.B. per ottenere questo risultato non è necessario ricorrere al perl infatti spesso viene utilizzato awk. Per me l'utilizzo di awk se non per le cose più semplici è molto criptico e, conoscendo perl, preferisco utilizzarlo al suo posto.

martedì 30 settembre 2008

virtualbox: come duplicare un hard disk virtuale

VirtualBox è un virtualizzatore di macchine tipo VMWare.
Talvolta è necessario duplicare un hard disk virtuale ma ciò non si può fare come sembrerebbe ovvio con un 'cp nomehd nomehdnuovo' poichè ciò creerebbe due hard disk con lo stesso UUID e virtualbox non aggiungerebbe la nuova periferica virtuale al parco hard disk.
Ecco invece come si deve fare:

VBoxManage clonevdi /home/valerio/.VirtualBox/gentoo.vdi /home/valerio/.VirtualBox/backup-gentoo.vdi

avendo cura di digitare il percorso completo degli hard disk virtuali altrimenti non funziona.

mercoledì 24 settembre 2008

Perl e lo zen

Di lavoro faccio il programmatore e ogni tanto mi balocco, nel tempo libero, con il mio coltellino svizzero: perl.

Oggi voglio mostrare come sia possibile con un istruzione piuttosto semplice fare una cosa che nei linguaggi tipo Java o C/C++ è molto tediosa.

Supponiamo di dover generare una sequenza di numeri del tipo

1 0
2 1500
3 3000
4 6000
5 15000
6 30000
7 50000
8 100000
9 200000
10 300000
11 400000
12 500000
13 600000
14 700000
15 800000
16 900000
17 1000000
18 1100000
19 1200000
20 1300000
21 1400000
22 1500000
23 1600000
24 1700000
25 1800000
26 1900000
27 2000000
28 2100000
29 2200000
30 2300000
31 2400000
32 2500000
33 2600000
34 2700000
35 2800000
36 2900000

(chi indovina cosa potrebbero rappresentare? )

cerchiamo di capire come potrebbe essere risolto. Innanzi tutto la struttura più adatta che viene in mente è quella di un array in cui in ogni posizione è occupata dal valore sulla seconda colonna.

Una soluzione banale è dichiarare un array che contenga tutti i valori.

@array=(0, 1500, 3000, 6000, 15000, 30000, 50000, 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000, 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000)

ma la cosa oltre che essere lunghina è poco perlosa.

Ecco come potremmo procedere dopo aver fatto la seguente osservazione: la sequenza che va da 0 fino al valore 100000 è irregolare mentre successivamente raddoppia sempre.

Dividiamo quindi il problema in 2 sotto problemi la prima e la seconda parte della stringa

@prima=(0, 1500, 3000, 6000, 15000, 30000, 50000)

e

@seconda=(100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000, 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000)

per la prima parte c'è poco da fare però può essere scritta nella forma

@prima=qw(0 1500 3000 6000 15000 30000 50000)

per risparmiare leggermente qualche digitazione (e per scriverla anche in modo un po più leggibile) altrimenti addirittura

@prima=map{$_*100} qw(0 15 30 60 150 300 500)

in cui si scrivono ancora meno caratteri grazie alla funzione map che consente di eseguire un'operazione su ogni elemento di una stringa e in questo caso moltiplica ogni elemento generato da qw(0 15 30 60 150 300 500) per 100.

Sfruttando quest'ultima peculiarità basta osservare che l'array @seconda non è altro che una successione di valori da 1 a 29 moltiplicati per 100000.
quindi posso scrivere

@secondo=map{$_*100_000} (1..29)

ed anche il secondo elemento è generato.

Quindi per concludere la lista dei valori viene generata da questa unica istruzione:

@array=(map{$_*100} qw(0 15 30 60 150 300 500),map{$_*100_000} (1..29))

un bel risparmio ...

venerdì 19 settembre 2008

Quarto Perl Workshop Italiano

Anche se l'argomento non riguarda esplicitamente il mondo linux mi sento in dovere di recensire l'incontro che si è tenuto il 18 e il 19 Settembre a Pisa incentrato sul linguaggio perl. Per chi non lo conoscesse il perl è un linguaggio interpretato nato quasi alla fine degli anni 80 che permette con il suo zucchero sintattico molto sintetico di scrivere programmi molto compatti.
Questa sua caratteristica è sia una cosa positiva che negativa infatti se utilizzata in maniera errata permette di scrivere programmi incomprensibili.
Questo linguaggio è stato definito superficialmente un linguaggio bash con gli steroidi.
In realtà per me il perl è "Il linguaggio di programmazione" perchè l'ho imparato dopo aver utilizzato il C, trovandomi con un linguaggio di una potenza espressiva senza paragoni; come se avessi comprato una smart e uscissi dal concessionario con la mach 5.
Questo workshop ha riacceso in me la passione che avevo quasi dieci anni fa per questo linguaggio e questo è un gran merito agli organizzatori dell'evento.
Oggigiorno il perl è considerato un linguaggio "obsoleto" perché poco leggibile e manutenibile rispetto a Python e soprattutto Ruby, ma mi è bastato seguire un talk intitolato "beautyful perl" per ottenere ottime argomentazioni al prossimo che mi dirà che il perl è "read only".
E poi in realtà non sono il difensore di nessun linguaggio perché c'è più differenza tra un buon programmatore e un cattivo programmatore che tra un buon linguaggio e un cattivo linguaggio di programmazione.
Un grazie di cuore agli organizzatori che hanno ridestato il cammello che è in me.

mercoledì 10 settembre 2008

Maledetti "a capo"

Usate unix e volete avere gli "a capo" in stile unix? Con il vi è facilissimo basta usare il comando

:set fileformat=unix

e se volete fare il viceversa (ovvero convertire dallo stile dos allo stile unix)

:set fileformat=dos

per maggiori informazioni

:help fileformat

un ringraziamento a http://diteloconunatorta.blogspot.com/ per aver sollevato il problema

martedì 15 luglio 2008

History non deve salvare

Proprio oggi mi è capitato di rilanciare l'ultimo comando eseguito per ricreare l'installazione dai sorgenti della mia applicazione enterprise quando, a sorpresa, ho invece lanciato un commit del mio sistema di versionamento.
Purtroppo il mio comando precedente era per l'appunto un commit invece che una ricompilazione come avevo creduto.
Come risultato mi sono trovato una riga di log e un commit non voluto sul server dei sorgenti.
Mi sono quindi chiesto esiste un modo per far si che la history di linux si dimentichi alcune cose?
Dopo aver tribolato sui motori di ricerca per formulare quello di cui avevo bisogno, poichè i vari 'linux history' da me digitati tornavano morte e miracoli sulla storia di linux ma non sul comando history, ho trovato il modo per risolvere il problema.

In pratica basta aggiungere nel .bashrc la variabile di ambiente HISTIGNORE con il contenuto da ignorare. Alla fine


export HISTIGNORE="svn ci*"

mi ha risolto la giornata e il mio 'invio' compulsivo.