Javascript

Nous utilisons sur critiqueslibres.com (pour l’instant, ça pourrait changer), un framework javascript populaire et puissant  qui s’appelle extjs (http://www.sencha.com/products/extjs).

Ce framework a été massivement revu (pour le mieux) lors de son passage de la version 3 à la version 4, et nous autres, sur critiqueslibres, avons décidé de suivre les évolutions.

Le langage javascript était au départ prévu pour animer des pages web, ou faire de simples validations de formulaires, il avait une mauvaise réputation et était regardé avec dédain par les programmeurs chevronnés. Mais le web domine le paysage et ça ne fait que s’accentuer (cfr le nouveau « hype » du cloud),  et pour programmer sur le web il n’y a quasi pas d’alternatives. Cela en conjonction avec le fait que quelques personnes très intelligentes (Douglas Crockford est souvent cité, il a écrit un livre un peu indigeste mais qui a fait référence), ces personnes ont re-découvert ce langage, dévoilant sa puissance (bien cachée). Et bien tout cela fait que le javascript est devenu le standard de-facto en terme d’interface utilisateur.

Voir cet article intéressant (en anglais) qui explique ce qu’est une ‘disruptive technology’ et pourquoi javascript pourrait bien en être une, écrit en 2007 :

http://www.jamesshore.com/Blog/HTML-and-the-Innovators-Dilemma.html

Le suivant est plus récent :

http://www.richardrodger.com/2011/04/05/the-javascript-disruption/#.Ujth_xYfy2w

A l’époque (il y a six ans), j’avais choisi extjs (c’était la version 2) parce que je travaillais dans une boite qui utilisait ce framework. La boite ou je travaille maintenant utilise angular.js (c’est Google derrière), et ça a l’air bien supporté et moins lourd que extjs. Par contre le gros avantage de extjs c’est qu’il offre une grande palette de composants « interface utilisateur » out-of-the-box, ce qui pour un non spécialiste du web design est un avantage énorme.

MySQL slow performance with updates

This week-end I moved the site to a new server at my hoster (www.ovh.net). After five years, it was a serious hardware upgrade, going from 2G of RAM to 16G of RAM for example and from a pentium dual core to a i3 quad core machine. But I was very disappointed by the performance of the new server and by looking in the slow query log I could see that MySQL was the culprit.

By configuration, it is possible to tell MySQL to log all SQL taking more than x second (or microsecond), which is very useful to diagnose performance. So in the /etc/my.cnf file, I added this

slow_query_log=1
long_query_time=1
slow-query-log-file=/data2/logs/slow_queries.log

I could immediately see that quite regularly, a very simple update statement (used to increment a counter based on a primary key) was taking more than 1 second, which pointed to a disk issue. It was also obvious that the issue was related to write, because the log was full of update/insert statements. The new server has two disks mirrored at the software level (RAID SOFT). I started to search for disks performance issue with MySQL, and decided to test with various documented parameters.

To do so, I did a small benchmark. I created a  file with 5000 updates, executed it with mysql while spooling to a file, then I parsed the generated file to extract the execution time of each update.

Concretly, I created a file called testperf.sql containing


\T perf.log
update test_table set col1 = col1+1,col2=col2+1 where id=29890;
update test_table set col1 = col1-1,col2=col2-11 where id=29890;
etc... 5000 times
quit

I started mysql and sourced the sql fie


$mysql -u <user> -p <password> <dbname>
mysql>source testperf.sql
mysql>quit

then in the shell, I parsed the generated perf.log file, extracting each execution time and making the sum


$ grep "Query OK" perf.log | sed -e "s/.*(\(.*\)sec)/\1/" | sort -n > t.log
$ ( echo 0 2k ; sed 's/$/ +/' t.log ; echo p ) | dc

The result was stunning.

While on the old server I had something like 170 seconds, on the new server it was more than the double.

First I tried to change the parameter innodb_flush_method. On the old server, it made a very big difference;

with the default value: 170 seconds
with O_DSYNC: 128 seconds.
with O_DIRECT: 86 seconds

But on the new server, changing this parameter made the performance only worst.

Then I altered the table to be MyISAM instead of InnoDB (alter table test storage MyIsam) and re-run the performance test: the performance was now excellent. So I got the indication that the performance problem was caused by the transaction. And indeed, if I turned off the autocommit (set autocommit = 0), I got good performance with the InnoDB engine as well. Now it was clear that the problem was due to the writes to disk of the log buffer (which happens every time a transaction is committed), with the behavior that from time to time very simple updates were hanging for 1 or 2 seconds (probably waiting for the OS to complete the write).

Luckily there is a way to tell MySQL that it does not need to wait for the write to be complete every time there is a commit: innodb_flush_log_at_trx_commit. By setting this parameter to 0, you are not completly protected against data loss in case of an outage because the latest committed transactions might not have been written to disk. According to the documentation, MySQL flushes anyway the commited transactions to disk every second, so the risk of data loss is not very big: you could lose 1 second of transaction at worst (which is absolutely not an issue in my case).

With this parameter, the DML statements are flying, in fact the whole test runs in less than a second which is 1000 times faster!!

Below are the parameters related to performance in my ini file. I want to do more testing, because I am not completly satisfied with this option. I want to test the disks with fio, which looks like a nice tools to test disks performance. But in the meantime, my database is now flying and I am very happy about that!


key_buffer_size=1024M
innodb_buffer_pool_size=4096M
#This setting makes a huge performance difference on this system !!
innodb_flush_log_at_trx_commit=0
innodb_log_file_size=250M
innodb_log_buffer_size=16M
slow_query_log=1
long_query_time=0.5
slow-query-log-file=/data2/logs/slowqueries.log
general_log=0
general_log_file=/data2/logs/mysql_generalquery.log

Migrer un site de Latin-1 vers UTF-8

Ça y est, j’ai finalement pris la décision de migrer critiqueslibres.com de Latin-1 vers UTF-8. Il y a beaucoup de bonnes raisons, la dernière en date étant que je voudrais intégrer les notices bibliographiques de la Bnf dans notre base, et que certaines informations peuvent être dans des langues non-européenne (par exemple, le nom d’un auteur japonais, le titre original d’un livre de Dostoïevski),…

J’ai « googlé » pas mal, j’ai trouvé beaucoup de tutoriels et d’articles, mais mes premiers essais ont été des échecs. Finalement, j’ai pris l’approche de celui-ci, que j’ai adapté un peu : Getting out of MySQL Character Set Hell.

La procédure que j’ai suivie contient les étapes suivantes :

  • Prendre un backup !!!
  • migrer la base de données : c’est la partie difficile
  • adapter le code PHP : utilisation de la librairie mbstring et changer les fonctions qui sont sensibles au jeu de caractères (dans mon cas, il s’agit de htmlentities), indiquer à mysql qu’on est en utf-8 lors de la connection
  • changer le header dans les pages HTML et adapter le httpd.conf

La base de données

Pour la base de données, voici ma stratégie retenue. Il y en a d’autres, mais celle-ci a marché chez moi, tandis que les autres ont échouées.

1. J’ai créé trois scripts SQL. Le premier qui contient les définitions de tables, index et contraintes d’intégrité. Un deuxième qui contient les commandes de création des fonctions et un troisième qui contient les commandes de création des triggers. C’est de toute façon une bonne pratique de maintenir ces scripts et de les « versionner » dans l’outil de « source control ».
Note : il est facile de créer les scripts si on ne les a pas encore : il suffit de faire un export de la base de données avec sqldump et l’option –no-data, ensuite d’éditer le fichier généré (supprimer toute les commandes inutiles ajoutées par mysql, séparer les tables des triggers et des fonctions,…).

A chaque commande « create table » dans le script, j’ai ajouté l’option UTF8. Par exemple

CREATE TABLE mytable (
  id INT NOT NULL,
  ...
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;

2. J’ai créé un petit script shell qui exporte le contenu de chaque table dans un fichier. Par exemple,

mysqldump -umonuser -pmonpassword--skip-set-charset \
    --extended-insert=false --no-create-info \
    --triggers=FALSE mabase latable1 > latable1.sql

.. etc pour chaque table de la base de données.

Il faut vérifier que le fichier généré est bien en UTF-8, avec la commande linux « file ». Dans mon cas, vu que mon serveur est linux avec comme locale utf-8, mysql a bien créé un fichier en utf-8. Si ce n’était pas le cas, alors j’aurais probablement du faire la conversion moi-même (commande linux iconv).

3. Editer le fichier de config de MySQL.

Dans le fichier /etc/my.cnf, changer les lignes suivantes :

[client]
default-character-set=utf8
...
[mysqld]
default-character-set=utf8

Il faut ensuite redémarrer MySql.

Pour vérifier que tout est en ordre, se connecter en mysql, et exécuter le SQL suivant :

mysql>show variables like 'char%'. 

Problème : en production (plus vieille version de mySQL), le show variables like ‘char%’ montrait que certaines variables étaient toujours en latin1 si je n’étais pas connecté avec root (avec root, pas de problèmes). Je n’ai pas investiger plus en détail, j’ai juste éditer le fichier my.cnf et j’ai ajouté dans la section [mysqld]

init-connect='SET NAMES utf8' 

Ensuite, je droppe la base de données, et je la recrée (Avoir un backup !!!).

drop database mabase;
create database mabase 
default character set 'utf8' default collate 'utf8_general_ci';

Ensuite, je recrée les tables à partir du script dont il est question plus haut. Il faut créer les tables (et les index + contraintes) uniquement (pas encore les fonctions et les triggers, ce sera fait après l’import des données). Et il suffit maintenant d’importer les fichiers générés plus haut, au moyen d’un script shell.

mysql -umonuser -pmonmotdepasse mabase < matable1.sql 

pour chacune des tables de la DB.

Il faut ensuite recréer les fonctions et les triggers.

PHP

Il faut installer le paquet php-mbstring si nécessaire.

yum install php-mbstring.

Editer le php.ini, et ajouter ceci :

mbstring.func_overload = 7

Il faut changer les appels à la fonction htmlentities, pour fournir le paramètre "utf-8".

htmlentities($message)

devient

htmlentities($message,ENT_COMPAT,"UTF-8") 
 

Dans mon site, j'avais quelques appels à decode_utf8 et encode_utf8, ils doivent maintenant être supprimés.

Au moment de la connexion à la base de données, il faut ajouter ceci :

mysql_set_charset('utf8',lelink);
ou bien
mysql_query("SET NAMES 'utf8'");

Apache et le HTML

Pour apache, il suffit d'éditer le fichier httpd.conf, et dans la section "virtual host" correspondante à votre site, ajouter ceci :

AddDefaultCharset UTF-8

Dans la page html, il faut changer le tag meta http-equiv, dans l'attibut content indiquer le charset UTF-8.

Conclusion

Il y a pas mal de techniques décrites sur le WEB pour migrer un site de jeu de caractères. Il faut bien tester, et choisir celle qui convient. Il est évidemment indispensable de prendre un backup avant !

Une fois que le site est migré, il faut s'assurer que son environnement de développement est en UTF-8 aussi, ce qui est le cas avec eclipse et linux. En eclipse, s'assurer que le caractère de l'éditeur est utf-8.