J'essaye de coder.

Changer l’interclassement de la connexion sur phpMyAdmin > 4.1

Quelle connerie…
Impossible de changer l’interclassement à partir de la version 4.1 de phpMyAdmin, quand on travaille en UTF-8. Il sélectionne toujours par défaut utf8mb4_*, qui du coup pète à peu près toutes les modifications en brute sur l’interface quand tout est déjà en utf8_unicode_ci et consorts.

Pour pouvoir changer l’interclassement, il faut simplement commenter une partie du code ci-dessous dans le fichier libraries/DatabaseInterface.class.php :

if ($default_charset == 'utf8mb4'
    && strncmp('utf8_', $GLOBALS['collation_connection'], 5) == 0
) {
    $GLOBALS['collation_connection'] = 'utf8mb4_' . substr(
        $GLOBALS['collation_connection'],
        5
    );
}

Ce code fixe l’interclassement en utf8mb4_* quand il détecte de l’utf8 standard.

Pourquoi ils ont fait ça ? Aucune idée. Personnellement, j’ai pas spécialement envie de passer en utf8mb4 pour l’instant (la gestion des emojis ne m’intéresse pas particulièrement sur mes derniers projets, entre autres…)

Changer à la volée la connexion à une BDD en cours sur Laravel

Pour un projet qui devait gérer des sous-domaines dynamiques (et bien sur des connexions en DB dynamique), j’avais besoin de changer à la volée une connexion en cours à la base de données, en dégageant celle de défaut par celle qui doit être utilisée, en fonction du sous-domaine.

Sans forcément passer par l’IoC de Laravel, et se farcir le paramètre {domain} du Route::group(array('domain' => '{subdomain}.example.com')), il y a un moyen très simple avec un filtre de pré-routage :

Route::filter('subdomain', function($route, $request)
{
	$host = $request->getHost();
	$subdomain = explode('.', $host)[0];

	try {
		$database = CMS::getValue($subdomain . '_database');

		$user = Config::get('database.connections.'.$database.'.username');
		$passwd = Config::get('database.connections.'.$database.'.password');

		$pdo = new PDO('mysql:dbname=' . Config::get('database.connections.'.$database.'.database') . ';host=' . Config::get('database.connections.'.$database.'.host'), $user, $passwd);
		DB::disconnect();
		DB::setPdo($pdo);
	} catch(Illuminate\Database\Eloquent\ModelNotFoundException $e) {
		return View::make('errors.internal_error');
	}
});

Ma table liée au modèle CMS ne contient juste que 2 colonnes clé => valeur, qui lie un sous-domaine à un nom de connexion dans la configuration base de données de Laravel.

Au final, ma connexion de défaut ne sert juste qu’à aller chercher la bonne connexion à utiliser pour la suite, étant écrasée par une nouvelle.

Ce n’est peut-être pas très performant à partir d’un certain nombres de requêtes (on se connecte 2 fois par session), mais pour un petit site c’est amplement suffisant.

Espaces insécables et PHP

Je viens de trouver ce super plug-in pour Sublime Text (2/3) : https://github.com/possan/sublime_unicode_nbsp (Unicode Character Highlighter sous Package Control), qui permet tout simplement de mettre en relief les caractères merdiques qui cassent la compilation et le parsing de fichier PHP (et ce n’est pas limité à PHP, mais tout langage de script, etc).

Bon techniquement avec un SublimeLinter ça ne sert pas à grand chose (puisque on voit en live que le parsing ne passe pas), mais c’est bien utile dans les fichiers de template où le linter ne passe généralement pas (et où les espaces insécables pètent quand même le code).

Pourquoi je peux pas me connecter sur mon MySQL/SSH/Whatever… ?

Si vous aussi ça fait quelques minutes/heures/jours que vous ragez sur ce problème que je vais décrire…
Vous venez d’installer un serveur MySQL tout neuf, ou un serveur SSH, ou n’importe quel type de je ne sais quoi, mais vous n’arrivez désespérément à vous connecter dessus.

On va prendre l’exemple du serveur MySQL :
– Le serveur est bien configuré (bind-address commenté ou configuré sur 0.0.0.0 pour les connexions extérieures)
– Les utilisateurs ont bien les droits qu’ils faut (style % ou X.X.X.X sur un utilisateur pour les connexions extérieures, toujours)
– iptables est bien configuré, voir même pas configuré du tout et qu’il accepte tout et n’importe quoi

MAIS, ça ne marche pas. Et un coup de tail /var/log/auth.log balance du connect refused… c’est que vous ne regardez pas du bon côté.

Et oui, allez faire un tour du côté de /etc/hosts.deny voir si il n’y a pas un petit ALL:ALL qui traîne entre autres…!

C’était le conseil post-rage du jour !

Daisy chaining en MySQL, ou comment créer un nouveau slave qui réplique d’un slave existant

Ajouter des slaves dans une grappe MySQL, c’est bien, ça permet de faire de la redondance de données et si ça plante, on est toujours assuré que ça bosse bien.

Dans mon nouveau job, l’installation actuelle est toute simple (pour l’instant) : un serveur MySQL maître, et un esclave qui permet de réduire la charge sur le maître pour la lecture de données moins importantes.
On ne fait pas de load balancing cependant. C’est juste de la sauvegarde. Simplement, vu qu’on ne travaille pas en clustering MySQL, rajouter un serveur c’est un peu la galère si c’était pas prévu de base.
De base, pour rajouter un esclave, on le fait sur le maître, histoire d’avoir une topologie master -> slave (x2) et non pas master -> slave -> slave.

C’est toute la problématique d’aujourd’hui. Nous on veut faire du master -> slave -> slave (du daisy chaining), pour réduire encore plus la charge du master pour certaines API (mais toujours pas de load balancing automatique).
Pourquoi on ne doit pas arrêter le master ? Parce que ça coupe la prod… et non, on ne peut pas se le permettre, malheureusement (même à 3H du matin !).

Tous les articles relatant de ça expliquent qu’il ne faut pas oublier le paramètre log-slave-updates = 1 dans la configuration de MySQL du premier esclave. C’est vrai. Mais ça ne suffit pas, et loin de là. Parce qu’une fois le paramètre collé et le premier esclave redémarré, le second esclave qui vient s’ajouter ne se mettra pas à jour par rapport aux nouveaux enregistrements du premier esclave.

Pourquoi ? Tout simplement parce que vu qu’on fait ça pour la première fois de sa vie, on ne sait pas forcément qu’il fallait aussi reprendre tout le relay-log du premier esclave… et ça, c’est expliqué nul part.
Donc si toi aussi tu es dans cette problématique et que tu t’es pris la tête une journée entière sans comprendre ce qui n’allait pas, suis ce modeste guide relatant comment faire de la réplication slave -> slave sans se prendre (trop) la tête !

Je rappelle la problématique : on est dans un environnement où on ne peut pas se permettre d’arrêter le maître pour rajouter un esclave.

Pour commencer, on va déjà créer un nouvel utilisateur sur le premier esclave pour la réplication comme on a pu faire sur le maître :

GRANT REPLICATION SLAVE ON *.* TO 'slave-user'@'192.168.0.%' 
      IDENTIFIED BY 'password';
-- Adresse et password à changer, bien sur !
FLUSH PRIVILEGES;

Une fois l’utilisateur crée, on va arrêter la réplication sur le premier esclave et aller voir à quel index on en est :

STOP SLAVE;
SHOW MASTER STATUS;
*************************** 1. row ***************************
            File: mysql-bin.000317
        Position: 106
    Binlog_Do_DB: telco
Binlog_Ignore_DB: mysql
1 row in set (0.00 sec)

En l’occurrence, mon maître sur l’esclave en est à la position 106 (en gros, nous n’avons jamais écrit les mises à jour de l’esclave, ce qui est normal). Ne pas oublier aussi de noter le fichier de bin-log sur lequel on est, c’est important pour la suite.
Une fois l’esclave arrêté, on peut faire une sauvegarde (Ici, tout dépend de comment vous voulez la jouer, la sauvegarde dépend de l’environnement, si on la fait à la mano avec un mysqldump ou avec Percona, qui permet d’exporter directement les fichiers et non pas de faire un dump …). Bref, pour la petite ligne qui va bien :

mysqldump -uroot -p mabase > mabase.sql

Maintenant, la phase importante, la configuration et l’export de tous les bons fichiers qui faut.
On file dans /etc/mysql/my.cnf (je ne met que le nécessaire ici) :

[mysqld]
server-id         = 99 # l'ID de votre premier esclave
skip-slave-start  = 1  # Nous est utile lors du redémarrage de l'esclave
log_bin           = /var/log/mysql/mysql-bin.log
log-slave-updates = 1  # Le truc à faire rager si on l'oublie (:
binlog_do_db      = mabase
relay_log         = /var/log/mysql/mysql-relay-bin.log

Une fois configurée, on peut exporter le relay-log. C’est ici que tout ce joue. Avant de comprendre qu’il fallait exporter le relay-log, je me contentais d’activer le 2ème esclave sur le bon index et bon bin-log et de le démarrer. Mais les mises à jour ne fonctionnaient désespérément pas.
Généralement, le relay-log se trouve dans /var/log/mysql/ mais si il n’a pas été configuré, par défaut il se trouvera dans /var/lib/mysql/.
On doit récupérer tout ce qui est du style mysql-relay-bin.0* et l’index mysql-relay-bin.index.

A ce niveau, on peut redémarrer le premier esclave. Cependant, la réplication ne fonctionnera pas, puisque le paramètre skip-slave-start n’a pas fait démarrer l’esclave (c’est normal). On ne redémarre pas la réplication pour l’instant !

Une fois ces fichiers (dump SQL et relay-log) copiés sur le nouveau slave, on s’attelle maintenant à le configurer !
(on fait tout ça en ayant le daemon à l’arrêt, évidemment)

Pas de miracle particulier au niveau de la configuration du second slave :

[mysqld]
server-id         = 999 # l'ID du nouvel esclave ! Doit être différent !
skip-slave-start  = 1  # Nous est utile lors du redémarrage de l'esclave
log_bin           = /var/log/mysql/mysql-bin.log
#log-slave-updates = 1  # Si un jour on doit en rajouter un ... le laisser
binlog_do_db      = mabase
relay_log         = /var/log/mysql/mysql-relay-bin.log
master-user       = slave-user # pas obligatoire de le mettre ici
master-password   = password   # pareil

Attention cependant, dans la configuration le nom du relay_log doit être identique aux fichiers qu’on a copié du premier esclave (mysql ou mysqld…)

Une fois la configuration montée, on copie nos fichier dans /var/log/mysql.
(Vérifiez bien les droits des fichiers ! Si les fichiers n’ont pas les bons droits, ou n’appartiennent pas à l’utilisateur mysql, le serveur va vous balancer une erreur quand on essayera de configurer l’esclave !)

Il ne reste plus que quelques étapes. On redémarre le daemon, et on charge notre dump dans le nouvel esclave tout neuf (en ayant préalablement crée la base de données si elle n’existait pas…!) :

mysql -p -uroot mabase < mabase.sql

Une fois le dump chargé, on se connecte au daemon et on va configurer ce nouveau slave :

CHANGE MASTER TO MASTER_HOST='192.168.0.X', MASTER_USER='slave-user', 
       MASTER_PASSWORD='password';
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000317', 
       MASTER_LOG_POS=106;
START SLAVE;

Si tout se passe bien, on peut faire vérifier l’état du seconde slave pour voir si il s’est bien connecté au premier, d’une part, et si la réplication s’est bien mise en route :

SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.0.2
                  Master_User: slave-user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000318
          Read_Master_Log_Pos: 16390414
               Relay_Log_File: mysqld-relay-bin.000001
                Relay_Log_Pos: 16390559
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 16390414
              Relay_Log_Space: 16390689
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)

Si on a bien Slave_IO_Running: Yes et Slave_SQL_Running: Yes, c’est que tout va bien, le seconde slave s’est bien connecté au premier et la réplication fonctionne. Cependant, il n’y aura rien à répliquer puisque le premier slave ne réplique pas encore les nouvelles données du master.

Le moment fâcheux arrive. On relance la réplication sur le premier esclave.
Là, le premier esclave va se remettre à jour par rapport au maître. OK, ça c’était le fonctionnement normal.
Par contre, vu qu’on logue maintenant les mises à jour de l’esclave, le seconde esclave doit lui aussi se mettre à jour par rapport au premier !
Si votre base est à l’arrêt, faites un test en créant un nouvel enregistrement sur le maître. Sinon, vous verrez tout de suite si votre second esclave rattrape lui aussi son retard…

Si tout fonctionne, félicitations ! Sinon, c’est que vous avez dû louper une étape par là !

J’espère que cet article pourra en dépanner certains dans mon cas. N’hésitez pas à commenter pour raconter votre expérience, c’est toujours bien de se les partager :)

tl;dr : dans un environnement où planter une base de données fait perdre son salaire chaque seconde que la production est par terre, je crois que lire un article de fond en comble ne fait pas trop de mal :D
Mais quand même, je colle ce qui m’a servi à monter mon second esclave, pour info !

-- Sur slave-1
mysql> GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'192.168.0.%' 
       IDENTIFIED BY '!password?';
mysql> STOP SLAVE;
mysql> SHOW MASTER STATUS;
-- Noter fichier et position
mysql> EXIT;

-- Mettre à jour configuration de db-b
bash> nano /etc/mysql/my.cnf

[mysqld]
+ log_bin = /var/log/mysql/mysql-bin.log -- Si n'existe pas
+ skip-slave-start = 1
+ log-slave-updates = 1
+ relay_log = /var/log/mysql/mysql-relay-bin.log -- Si n'existe pas
+ binlog_do_db = mabase
-- Enregistrer

bash> service mysql restart
-- Avec skip-slave-start, le slave ne démarre pas, donc on est bon
bash> mysqldump -ppassword -uroot mabase > mabase.sql

bash> cd /var/lib/mysql -- Si relay_log non setté
bash> tar cvf relay.tar mysqld-relay*
// OU //
bash> cd /var/log/mysql -- Si relay_log déjà setté
bash> tar cvf relay.tar mysql-relay*

-- Sur slave-new
bash> service mysql stop
bash> scp root@192.168.0.X:/var/[log/lib]/mysql/relay.tar .
bash> scp root@192.168.0.X:[fichier de dump SQL] . 
bash> mv relay.tar /var/log/mysql
bash> nano /etc/mysql/my.cnf

[mysqld]
+ log_bin = /var/log/mysql/mysql-bin.log
+ skip-slave-start = 1
+ relay_log = /var/log/mysql/mysql-relay-bin.log
+ binlog_do_db = mabase
-- Enregistrer

bash> service mysql start
bash> mysql -p -uroot
mysql> CREATE DATABASE mabase;
mysql> EXIT;
bash> mysql -p -uroot mabase < mabase.sql
bash> service mysql stop
bash> cd /var/log/mysql
bash> tar xvf relay.tar
bash> service mysql start
bash> mysql -p -uroot
mysql> CHANGE MASTER TO MASTER_HOST='192.168.0.X',MASTER_USER='slave_user', 
       MASTER_PASSWORD='!password?';
mysql> CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000318', 
       MASTER_LOG_POS=106;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G;
-- Ici le slave s'est normalement connecté sur l'ancien slave

-- Sur db-b
bash> mysql -p -uroot
mysql> START SLAVE; 
-- Si tout va bien, on peut relancer le slave
mysql> SHOW SLAVE STATUS\G; 
-- On vérifie que le premier slave rattrape le retard accumulé

Laravel, migrations, et clés étrangères

Il y a un comportement bizarre avec Laravel 4, par rapport aux migrations et au schema builder : des fois, le fait de créer un integer et lui appliquer un foreign pour pouvoir faire une relation étrangère vers une autre table ne fonctionne pas, et renvoi une erreur SQL.

J’ai compris après un petit moment qu’un integer qui est auto-incrémenté, en clé primaire et crée par le schema builder de Laravel n’est pas un int(11) qu’on pourrait avoir l’habitude de retrouver (si on crée ses tables à l’arrache comme d’habitude), mais bien un int(10) unsigned qui bien sur, empêche de créer des ID négatifs puisque non signé (ce qui en soi peut être logique, sauf peut-être pour SQL Server 97 et j’en passe…)

J’étais d’ailleurs tombé sur cette question sur Stack Overflow où quelqu’un se posait la même question puisque ses migrations foiraient.

Au final, la solution n’est que trop simple : créer un entier non signé pour ensuite lui appliquer un foreign, comme ceci :


$table->unsignedInteger('example_id');

Et tout de suite, ça va déjà mieux.

Bootstrap 3 RC1

Enfin, ça y est, Bootstrap 3 commence à sortir de la pénombre. L’équipe derrière ce framework a teasé toute la matinée (chez nous), pour enfin sortir la première RC : on peut la trouver ici

Et ils ont aussi décidé de quitter le GitHub de Twitter pour un nouveau compte.

Rapidement, niveau nouveautés, ils ont l’air d’être passés au flat design pour tous les éléments graphiques, d’avoir revu pas mal de contrôles JS, et bien sûr de passer au « mobile first ». Le responsive s’en trouvera sûrement que mieux fonctionnel…

Mais c’est le week-end donc on testera tout ça un autre jour …!

Transitions CSS fluides, simplement

Ce repository GitHub contient une petite merveille d’effets CSS, compatible avec tous les bons browsers. Il s’appelle Effeckt.css, et c’est une compilation d’effets CSS de transitions, transformations simples, mais compatible mobile, et surtout qui utilise les librairies 3D.

J’ai eu ce souci avec SMSwrittr pour quelques effets, dont le menu de gauche qui sur mobile n’était pas très performant à l’affichage (vu que cette appli Web est sensée, être mobile first …)

Bref, je l’ai testé rapidement et ça m’a l’air d’être du tout bon. En plus, il peut s’intégrer facilement et ne pèse pas lourd.

Laravel

Laravel 4

J’ai découvert Laravel il y maintenant plus d’un an, via Richard (@_PandaJS), dans sa quête à la recherche de la moindre nouvelle techno ou dernier framework à la mode :

CommitStrip.com : toujours les bons mots

Il faut dire que je n’étais pas très sur le principe pour d’utiliser une version bêta (qui était donc la bêta de la 4) pour commencer des projets, et je suis resté borner à utiliser CodeIgniter pour finir et recommencer mes projets (sérieux) de l’époque.

Aujourd’hui, étant sorti en version finale il y a quelque temps maintenant, c’est donc sur un élan tout à fait normal que je me suis décidé à l’utiliser pour mes projets en production, après avoir bien bossé sur la bêta pour des trucs insignifiants.

Le premier projet que j’ai décidé de passer en prod très bientôt (d’ici le 1er août) est SMSwrittr, qui est une sorte d’appli Web en responsive pour envoyer des SMS, pensée pour les gens qui font pas mal de contact SMS de masse (artisans, entreprises), et doté d’une magnifique API RESTful (s’il vous plaît). Je ne vais pas vanter le projet dans ce billet, ce n’est pas son but.

À force d’utiliser Laravel pour ce nouveau projet (et bien sûr dans la nouvelle API d’Infomaniak), tout en admirant la fluidité impressionnante à laquelle on peut coder rapidement et correctement, j’ai décidé de réveiller pas mal d’anciens projets sous CodeIgniter pour les refactoriser sous Laravel. J’ai eu un vieux doute au début, en me demandant si cela était bien nécessaire, mais cette nouvelle m’a fixé : la maison mère de CodeIgniter, EllisLab, décide de laisser tomber le support de CodeIgniter avant de sortir la version 3 qui est actuellement présente sur GitHub en développement.
Force est de constater que si la maison mère commence à laisser tomber son « bébé », et malgré le fait que ce projet est donné gracieusement à la communauté open source, le support ne sera jamais le même (il ne faut pas se leurrer …)

Aujourd’hui, vu le suivi de Laravel par Cartalyst (entre autres), j’ai décidé de me prendre un abonnement pour Arsenal(45$/3 mois ou 150$ pour l’année).
Cela peut paraître cher pour du code hébergé sur GitHub et maintenu par une communauté. Cependant, après avoir importé des projets comme Sentry 2 (qui lui est non payant sur leur repository), voir des packages comme Themes 2 ou API m’a fait rêver.
Pouvoir implémenter ces mêmes packages avec Composer pour faire un système de theming très rapide, de la compression de JS et CSS à la volée sans passer par node.js et sa tartine de plug-ins de compression (pour l’instant je suis un peu anti node.js même si je m’y mets doucement), et surtout pouvoir gérer façon namespace PHP les thèmes, m’ont redonné l’envie de développer un CMS comme je l’avais imaginé il y a quelques années.
Un CMS complètement modulaire, pour une utilité simple : faire du site ultra souple, et très rapidement, et surtout pas cher, pour des gens qui n’ont pas forcément l’envie de payer une web agency qui leur ferait du design qui déchire, certes, mais qui déchire tout autant leur compte en banque. C’est la crise, ma bonne dame.

Bref, que prêcher ? Laravel c’est bien, c’est bien mieux que CodeIgniter, CakePHP, FuelPHP, et tout ce qui suit (on ne peut pas dire autant de Symphony, puisqu’il y a de grands bouts de ce framework dans Laravel).
Cependant, beaucoup voient l’arrivée de node.js et le full JavaScript frontend/backend comme la fin du Web classique tel qu’on le connaît : avec un couple plus que vieillot tel qu’Apache/PHP (nginx/PHP-FPM si vous préférez).
Je ne suis pas partisan de ça.
Je n’ai pas d’exemple sous la main d’une API faite à 100% sous node.js qui répondrait aussi rapidement que n’importe quelle API telle que Gravatar ou autre chose faite à base de Ruby ou PHP, et consorts. Et qui surtout, tiendrait la charge ?
(Twitter amène beaucoup pour le frontend, et pour la communauté JavaScript. Cependant, si leur backend était fait en node.js, je doute fortement de l’accessibilité de leur service. Si je ne me trompe pas, leur backend est en Scala)
Bien sûr, ces avis n’impliquent que moi. Mais étant actuellement dans le développement avec @_PandaJS d’une API d’achievement pour le web, cet avis se conforte d’autant plus pour moi.
Et puis, étant très fortement borné, ça sera dur de me faire changer d’avis.

tl;dr : Laravel 4 c’est bien, même si les développeurs changeaient complètement tout dans la bêta à chaque release, maintenant que c’est terminé, ça marche. Et que Laravel 4 + API d’Arsenal, il n’y’a rien de mieux pour développer du full RESTful et gérer une interface/appli Web avec des technos comme AngularJS pour le frontend.

Ouverture

J’ai décidé sous l’impulsion de mon cher ami @_PandaJS qui a fait de même récemment (son blog à lui), d’ouvrir un blog qui parlera sans aucun doute de développement Web, et peut-être même d’autres conneries qui n’auront sûrement rien à voir avec le développement Web, le PHP, ou l’informatique en général (ça sera donc des coups de rage ou des articles relatant à la politique, toujours couverte de rage ambiante).

Bref, si l’envie vous vient de me lire, et bien … stickez ce site en favori, en RSS, ou dans un protocole qui n’existe pas encore (peu m’importe, tant que vous me lisez !), et au plaisir de vous revoir sur le blog.