22 déc. 2010

Centos utilise 100% du CPU sous VirtualBox

Comme beaucoup de développeurs, j'utilise très souvent une machine virtuelle pour tester mes développements en local. J'ai une préférence particulière pour Centos, qui reste très proche des serveurs Red Hat sur lesquels j'interviens, et pour VirtualBox comme solution de virtualisation (de part sa gratuité et sa simplicité d'utilisation). Malgré mon aisance grandissante avec les systèmes Unix, je n'ai toujours pas migré ma station de travail sur un OS comme Fedora ou Ubuntu, et je continue à utiliser une version vieillissante d'XP (pour des raisons professionnelles surtout...). Après l’installation de Centos 5.5 (et même sur les versions précédentes) sur une nouvelle machine virtuelle, j'ai souvent constaté que l'utilisation du processeur faite par virtualbox.exe était en permanence de 50% (j'ai un processeur double coeur), et ce même quand la machine est au repos sans aucun processus actif (idle). D'après ce que j'ai pu lire, et sans être en grand spécialiste sur le sujet, il s'agirait d'un problème avec le Kernel de Centos qui fonctionnerait à une fréquence supérieure à celle du virtualiseur, comme mes connaissances sont assez limitées je ne m'étendrais pas sur le sujet et je vais me contenter de vous donner la solution que j'utilise pour contourner le problème.

Certains préconise de recompiler le Kernel avec les options adéquates afin de prendre en compte la bonne fréquence, d'autres propose d'utiliser un Kernel maison bidouillé pour fonctionner correctement avec VirtualBox... Personnellement je ne me sent pas très à l'aise avec l'idée de recompiler le Kernel, et pour ce qui est d'une version non-officielle, je préfère éviter dans un cadre professionnel (support oblige...). Je vous propose donc une troisième solution, qui ne requerra qu'un redémarrage de votre VM :

1) Ouvrez le fichier /etc/grub.conf avec votre éditeur favoris.

2) Repérez les lignes concernant le kernel, et ajoutez divider=10 à la fin de celles-ci.

Voici mon fichier grub.conf à titre d'exemple
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-194.26.1.el5)
        root (hd0,0)
        kernel /vmlinuz-2.6.18-194.26.1.el5 ro root=/dev/VolGroup00/LogVol00 divider=10
        initrd /initrd-2.6.18-194.26.1.el5.img

title CentOS (2.6.18-194.el5)
        root (hd0,0)
        kernel /vmlinuz-2.6.18-194.el5 ro root=/dev/VolGroup00/LogVol00 divider=10
        initrd /initrd-2.6.18-194.el5.img
3) Sauvez le fichier

4) Redémarrez votre VM.

5) ...

6) Profit !

Et voilà, maintenant profitez de votre machine virtuelle sous Centos sans compromettre la longévité de votre CPU sur le long terme.

9 déc. 2010

Insertion d'objets Doctrine en boucle et problèmes de mémoire

Depuis que j'utilise Doctrine, j'ai toujours rencontré un problème lors de l'insertion en boucle d'objets dans la base de données, mes tentatives se terminants lamentablement par une erreur "Allowed memory size...". Le problème venant que l'instanciation d'objets en boucle vient toujours a dépasser la valeur maximale de mémoire allouée a PHP. Si comme moi vous avez tout essayé en vain et vous êtes résigné à vous passer de cet ORM pour des requêtes brutes en base, peut-être cet article vous aidera.

Dans mon cas précis, j’écris une task qui me sert a migrer les données d'un ancien site vers sa nouvelle version remise  neuf sous Symfony 1.4 avec Doctrine. Voici la boucle qui concerne la migration de la table utilisateur (note: le nouveau systeme utilise sfDoctrineGuard, mais ça n'a pas vraiment d'importance pour notre problème):

$DBO = Doctrine_Manager::getInstance()->getConnection('doctrine')->getDbh(); 
/**
 * Users migration
 */

$users = $DBO->query("SELECT * FROM old_tblusers")->fetchAll();    
    
foreach($users as $oldUser){
    $user = new sfGuardUser();
    $user->setFirstName($oldUser['FName']);
    $user->setLastName($oldUser['LName']);
    $user->setEmailAddress($oldUser['EMail']);
    $user->setUsername($oldUser['UserName']);
    $user->setPassword($oldUser['Password']);
    $user->save();
    $user->free(true);
    unset($user);

    echo memory_get_usage()."\n";;
}
A lui tout seul cet exemple est peu gourmand en mémoire, mais celle-ci ne sera cependant pas stable et continuera a augmenter à chaque boucle... Voici l'astuce qui permet d'avoir une utilisation de mémoire constante :

Dans le fichier config/databases.yml, ajoutez les informations suivantes à vos paramètres de connexion :
# Disable profiler for import in task
dev:
  doctrine:
    class: sfDoctrineDatabase
    param:
      profiler: false
Je précise évidemment que ma task utilise l'environnement dev pour fonctionner. Grâce a cette modification, mes boucles utilisent entre 152 et 156Mo de mémoire et ce constamment, quel que soit le nombre d'objets à insérer (dans mon cas 48 000). Pour les informations techniques de mon environnement, j'utilise un serveur local sous Centos 5.5, avec PHP 5.3 et MySQL 5.1.

Voila, j’espère que cette astuce fonctionnera pour vous aussi. Je la partage ici car j'ai passé beaucoup de temps à chercher une solution à ce problème sans jamais rien trouver de vraiment probant.