vendredi 10 juin 2011

Proxychains ou l'art de proxifier

S'il vous est arrivé de vous retrouver dans l'impossibilité de sortir sur certains ports mais que vous avez su mettre en place un proxy socks ou http, il n'est pas forcément aisé de configurer les différents outils que vous utilisez.
Ce fut mon cas par exemple avec git. En effet, il est possible avec git d'utiliser un proxy socks, mais cela nécessite de créer un script basé sur netcat et d'affecter à la variable d'environnement GIT_PROXY_COMMAND le chemin associé à ce script. Bref, quelles que soient les commandes que vous utilisez vous aurez ou non la possibilité de configurer l'utilisation d'un proxy en modifiant un fichier de configuration, une variable de d'environnement etc...
L'intérêt de proxychains est que la configuration ne se fait qu'une seule fois, dans son propre fichier de configuration. Ensuite vous utilisez la syntaxe proxychains <command> <args> et votre commande utilisera le proxy spécifié dans le fichier de configuration de proxychains !

Voyons tout de suite un exemple :

git pull git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git master
git.kernel.org[0: 199.6.1.168]: errno=Connection timed out
git.kernel.org[0: 130.239.17.8]: errno=Connection timed out
git.kernel.org[0: 2001:6b0:e:4017:1994:313:1:2]: errno=Network is unreachable
git.kernel.org[0: 2001:500:60:10:1994:313:1:2]: errno=Network is unreachable
fatal: unable to connect a socket (Network is unreachable)

Maintenant en utilisant un proxy socks.

- Je mets donc en place mon proxy
ssh -fND localhost:10123 my_proxy

- J'installe proxychains et mets en place ma configuration
sudo apt-get install proxychains

- Voici mon fichier de configuration /etc/proxychains
strict_chain

# Quiet mode (no output from library)
quiet_mode

# Proxy DNS requests - no leak for DNS data
#proxy_dns

# Some timeouts in milliseconds
tcp_read_time_out 15000
tcp_connect_time_out 8000

[ProxyList]
socks5 127.0.0.1 10123

- Il ne reste plus qu'à tester proxychains pour proxifier le flux de ma commande git. Pour ce test j'ai commenté l'instruction quiet_mode dans le fichier de configuration de proxychains
proxychains git pull git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git master
ProxyChains-3.1 (http://proxychains.sf.net)
|S-chain|-<>-127.0.0.1:10123-<><>-130.239.17.8:9418-<><>-OK
From git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
* branch master -> FETCH_HEAD
Already up-to-date.

Comme on peux le voir on passe par le proxy socks local pour atteindre git.eu.kernel.org(130.239.17.8) sur le port git (9418)

Pour ceux qui veulent en savoir plus, le fonctionnement de proxychains est assez simple ainsi que son code (~ 1000 lignes). Il affecte à la variable d'environnement LD_PRELOAD le chemin de sa bibliothèque dynamique (libproxychains.so) surchargeant les fonctions réseaux de base que sont gethostbyaddr, getnameinfo, gethostbyname, connect


Lire la suite...

lundi 30 mai 2011

Télécharger vos patchs Oracle avec wget

Vous l'avez peut être remarqué, mais Oracle vous donne à présent la possibilité de télécharger les archives en utilisant wget. Pour cela, il vous suffit de choisir "WGET options" après avoir cliqué sur download et ensuite de choisir download.sh pour récupérer le script. Enregistrer le sous le nom wget-oracle.sh.

Pour le faire fonctionner il vous reste à :

- entrer vos informations SSO, par exemple :
SSO_USERNAME=cscetbon@lesdatabases.com
SSO_PASSWORD=Waou000p
- commenter les instructions wget qui ne concernent pas les fichiers que vous désirez télécharger
- remplacer le ligne
WGET=/usr/bin/wget
par
WGET='/usr/bin/wget --user-agent="Mozilla/5.0" '
- ajouter en début de script l'instruction
export LANG=C
- rendre exécutable le script
chmod u+x wget-oracle.sh

Maintenant rendez vous dans un répertoire disposant d'assez d'espace pour télécharger vos archives et lancez la commande :
/le/chemin/que/vous/aurez/choisi/wget-oracle.sh

lundi 16 mai 2011

jointures et collate

L'option COLLATE peut être utilisée dans l'ordre de création d'une table pour spécifier le comportement à observer sur les opérations liées à une colonne. Vous pouvez par exemple grâce à ce mot clé effectuer une recherche sur une chaîne de caractères en ne tenant pas compte des accents. Cependant, il est important de savoir que cela peut avoir un impact sur les performances de vos jointures SQL si vous n'utilisez le même COLLATE sur les colonnes servant à effectuer la jointure. Nous allons voir un exemple concret rencontré en production.
Cet exemple fait intervenir 2 tables dont une qui contient plus de 4 millions d'enregistrements :

mysql> SELECT count(*) FROM a1;
+----------+
| count(*) |
+----------+
| 4382124 |
+----------+
1 row in set (0.77 sec)

mysql> SELECT count(*) FROM a2;
+----------+
| count(*) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)

La jointure des tables n'est pas très lente mais est exécutée des millions de fois, ce qui fait que le processus global durait une dizaine d'heure. Voici le plan d'exécution utilisé par MySQL :

mysql>EXPLAIN SELECT count(*) FROM a1 JOIN a2 ON (a2.pka2=a1.pka1);
+----+-------------+-------+--------+---------------+---------+---------+------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+------+---------+--------------------------+
| 1 | SIMPLE | a2 | system | PRIMARY | NULL | NULL | NULL | 1 | |
| 1 | SIMPLE | a1 | index | NULL | PRIMARY | 27 | NULL | 4387787 | Using where; Using index |
+----+-------------+-------+--------+---------------+---------+---------+------+---------+--------------------------+
2 rows in set (0.00 sec)

Comme vous pouvez le voir, MySQL effectue un FULL SCAN de l'index (PK) de la table a1 qui contient ~ 4,4 Millions d'enregistrements. L'exécution de la requête dure 130ms.
En fait, ceci est dû au fait que les COLLATE des colonnes a2.pka2 et a1.pka1 sont différents

mysql> SELECT TABLE_NAME,COLUMN_NAME,CHARACTER_SET_NAME,COLLATION_NAME FROM information_schema.columns WHERE (TABLE_NAME='a1' and COLUMN_NAME='pka1') or (TABLE_NAME='a2' and COLUMN_NAME='pka2')\G
*************************** 1. row ***************************
TABLE_NAME: a1
COLUMN_NAME: pka1
CHARACTER_SET_NAME: ascii
COLLATION_NAME: ascii_bin
*************************** 2. row ***************************
TABLE_NAME: a2
COLUMN_NAME: pka2
CHARACTER_SET_NAME: latin1
COLLATION_NAME: latin1_swedish_ci
2 rows in set (0.00 sec)

Si on modifie le COLLATE de la colonne a2.pka2 pour qu'il soit celui de la colonne a1.pka1, MySQL n'a plus besoin de scanner entièrement l'index de la table a1.

mysql> ALTER TABLE a2 CHANGE COLUMN pka2 pka2 VARCHAR(25) NOT NULL COLLATE ascii_bin;
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql>EXPLAIN SELECT count(*) FROM a1 JOIN a2 ON (a2.pka2=a1.pka1);
+----+-------------+-------+--------+---------------+---------+---------+----------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------+------+-------------+
| 1 | SIMPLE | a2 | index | PRIMARY | PRIMARY | 27 | NULL | 1 | Using index |
| 1 | SIMPLE | a1 | eq_ref | PRIMARY | PRIMARY | 27 | a2.pka2 | 1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+----------------------------+------+-------------+
2 rows in set (0.00 sec)

Ceci a permis de ramener la durée du processus global à une vingtaine de minutes. Prenez donc garde à utiliser soigneusement la clause COLLATE, surtout quand vous effectuez des jointures !
Lire la suite...

dimanche 27 février 2011

Contraintes uniques et valeurs nulles

Vous risquez d'être surpris si vous utiliser une contrainte d'unicité sur un groupe de colonnes qui peuvent être nulles. En effet, il faut savoir que la contrainte d'unicité différencie par défaut les valeurs nulles. Ainsi si vous créez la table suivante :

mysql> CREATE TABLE t1(id1 int,id2 int,id3 int);
Query OK, 0 rows affected (0.00 sec)

mysql> ALTER TABLE t1 ADD UNIQUE KEY (id1,id2,id3);
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0

Vous pourrez ajouter les triplets (1,1,1), (1,2,1), (2,1,1) une seule fois. Par contre, vous pourrez ajouter autant de fois que vous le voulez les triplets (1,1,NULL), (1,NULL,1), (1,NULL,NULL), (NULL,NULL,NULL) etc...

mysql> INSERT INTO t1 VALUES(1,1,1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(1,2,1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(2,1,1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(2,1,1);
ERROR 1062 (23000): Duplicate entry '2-1-1' for key 'id1'

mysql> INSERT INTO t1 VALUES(1,1,NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(1,1,NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(1,NULL,NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(1,NULL,NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(NULL,NULL,NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES(NULL,NULL,NULL);
Query OK, 1 row affected (0.00 sec)

Apparemment, MySQL respecte le SQL 2003 qui spécifie que les contraintes d'unicité ne s'appliquent que sur les valeurs non nulles, ce qui ne semble pas très intuitif quand on choisi ce type de contrainte. Oracle ne prend pas en compte cette considération et vérfie l'unicité sur l'ensemble des valeurs, ce que nombre de personnes auraient certainement voulu retrouver chez MySQL...

Pour plus d'information, vous pouvez lire le bug report 25544

lundi 6 décembre 2010

Berkeley DB XML

Berkeley DB XML est une surcouche à Berkeley DB qui permet d'hériter de toutes ses fonctionnalités pour le stockage de documents XML.

En plus du stockage, BDB XML apporte les fonctionnalités suivantes :
- Indexation des noeuds, des attributs et des metadata
- Indexation au niveau noeud ou document
- QueryPlan
- Xquery 1.0
- Possibilité d'ajouter/requêter des metadata associées aux documents
- Documents modifiables (remplacer un sous ensemble du document peut être plus rapide)
- Validation (Il est possible de fournir un schema xsd qui est vérifié à l'insertion d'un document)
- Compression zlib par défaut (personnalisable)

XQUERY (XML Query Language) est disponible pour effectuer des recherches/modifications/ajouts de documents XML.
Il s'appuie sur Xqilla (bibliothèque C++) pour effectuer le parsing des requêtes Xquery/XPath.
Xqilla utilise Xerces (bibliothèque C++) pour effectuer le parsing et la validation des documents XML.
XQuery supporte les «Expressions FLWOR» (For/Let/Where/Order/Return) qui permettent d'effectuer toute sorte de traitements sur les données comme les boucles, les tris, le filtrage.
Il n'est pas encore possible de grouper les données avec XQuery 1.0, mais cette fonctionnalité founie par XQuery 1.1 sera certainement supportée dans l'une des prochaines versions de Berkeley DB XML. Pour plus d'information, vous pouvez vous rendre sur http://www.w3.org/TR/xquery.

Voyons quelques exemples simples de traitements qu'il vous est possible d'effectuer avec le language XQuery (dans l'ensemble des exemples qui suivent, vous pouvez utiliser l'interpréteur dbxml) :

- Pour ajouter un document au container test.dbxml
dbxml -h /var/tmp/
dbxml>createContainer c1.dbxml
dbxml> put p1 '<person name="pignon" age="32" Taille="160" genre="M">Charles</person>'
dbxml> put p2 '<person age="31" Taille="190" genre="M">Cyril Scetbon</person>'

Pour mettre à jour la valeur de l'attribut age du noeud person dont la valeur de l'attribut name vaut pignon
- replace value of node collection('c1.dbxml')/person[@name='pignon']/@age with '54'

Pour récupérer à partir du container c1.dbxml la valeur du noeud person dont la valeur de l'attribut Taille est supérieur à 180 et dont la valeur de l'attribut genre vaut M
- collection("c1.dbxml")/person[@Taille>180 and genre="M"]/text()
Cyril Scetbon

Pour compter le nombre de valeur distinctes de l'attribut Taille de tous les noeuds person contenus dans le container c1.dbxml
- count(distinct-values(collection("cont2.dbxml")/person/@Taille))
3

Et voici d'autres exemples utilisant des expressions FLWOR :

Pour afficher les chiffres de 1 à 10
- for $c in 1 to 3 return $c
1
2
3

Pour afficher le nom de tous les auteurs (valeur de l'attribut name du noeud author des documents du container authors.dbxml) qui ont écrit le livre dont le titre est 'MySQL 5 Administration et Optimisation' (vérification en faisant une jointure entre les containers books.dbxml et authors.dbxml sur la relation [valeur de l'attribut id du noeud /author = valeur de l'attribut id du noeud /book/author])
-for $book in collection("books.dbxml")/book[title='MySQL 5 Administration et Optimisation']
for $author in collection("authors.dbxml")/author[@id=$book/author/@id]
order by $author/name
return <author>{$author/name/string()}</author>
<author>Stephane Combaudon</author>
<author>Olivier Dasini</author>
<author>Cyril Scetbon</author>

Pour afficher l'ensemble des noeuds person du container c2.dbxml pour lesquels la valeur de l'attribut Taille est supérieur à 180 en les insérant entre des balises <response> </response>
- for $p in collection("c2.dbxml")/person[@Taille>180]
order by $p/@Taille descending
return <response>{$p}</respoonse>
<person genre="F" Taille="185">Brigitte Nielsen</person>
<person genre="M" Taille="182">Cyril Scetbon</person>


Dans un prochain article, nous verrons comment créer des index et vérifier que nos requêtes les utilisent bien.
Lire la suite...