Stubber / mocker request.remote_ip

Pour faker une adresse IP d’appel, j’utilisais ça :
Seulement, Rubocop n’était pas content. Avec ça,
Rubocop est content, et c’est la manière la plus simple de le faire 😉

Ruby 2.1.5 avec Rbenv et Ubuntu 16.04

L’autre jour j’ai eu besoin d’intervenir sur un ancien projet Rails, qui tourne sur un serveur en Ruby 2.1.5 (sortie en novembre 2014, et sans support depuis mars 2017).

Avant d’envisager une montée de version, il fallait déjà que j’arrive à tester la version actuelle. Je pensais l’affaire rapide avec Rbenv : rbenv install 2.1.5  et le tour était joué.

Vous vous en doutez, la commande a échouée lamentablement. En regardant les logs j’ai compris que le problème était lié au paquet libssl-dev

/usr/include/openssl/asn1_mac.h:10:2: error: #error « This file is obsolete; please update your software. »

Grâce à Google, j’ai compris d’où venait mon problème : les anciennes versions de Ruby (inférieures à la version 2.3) ne sont compatibles qu’avec la version 1.0 du paquet libssl-dev.

Hors Ubuntu 16.04 est fourni avec le paquet en version 1.1.0. Il a donc fallu que je downgrade en version 1.0 le paquet fautif avec la commande suivante

J’ai pu ensuite installer Ruby 2.1.5 avec Rbenv tout simplement.

 

Ruby On Rails – Des tâches récurrentes simples avec Whenever

Il arrive que dans la vie d’un projet on ait besoin d’exécuter certaines tâches de manières récurrentes (export/import de données, suppression de vieux comptes, envoi de mail, …).

Sous Linux, la méthode la plus simple est d’utiliser l’utilitaire cron : il nous permet de lancer commandes, et scripts à une date et une heure précise et/ou de manière récurrente.

Exemples.

Donc si on veut mettre en place les tâches récurrentes de notre projet Rails, il nous suffit de :

  1. nous connecter sur le serveur
  2. modifier la crontab pour y inclure nos nouvelles tâches

cron est un utilitaire réputé, il existe donc de très nombreuses ressources pour apprendre à l’utiliser. Mais il y a plusieurs choses qui me gênent :

  • la syntaxe cron pour définir les heures et la récurrence est parfois dure à relire (ex. 0 0 25-31 1,3,5,7,8,10,12 0 my-script.sh )
  • la modification de la crontab peut avoir un impact global pas forcément prévu
  • on ne peut pas connaître l’ensemble des tâches récurrentes d’un projet
  • le fait de devoir aller sur le serveur pour mettre à jour la crontab, oblige à le faire sur chaque machine sur laquelle l’application est déployée, il peut donc y avoir des oublis

C’est pourquoi j’ai pris l’habitude d’utiliser la gem Whenever pour gérer les tâches récurrentes de mes projets Rails

Continuer la lecture de « Ruby On Rails – Des tâches récurrentes simples avec Whenever »

#Barcamp : quoi de neuf sur Rails 5 avant sa sortie finale ?

Notre équipe développe sur Ruby on Rails depuis maintenant de nombreuses années, nous surveillons donc en permanence l’évolution du Framework. La version 5 approche à grands pas, ou en sommes-nous ?

  • Actuellement (mars 2016) en Beta 3 (mais déjà mis en production sur le projet Basecamp 3)
  • Ruby 2.2+ exclusivement
  • Nouveauté (« the big one ») : Action Cable qui permet de faire du temps réel en natif sur un projet. Ils ont profité de cette nouveauté pour ajouter un système permettant de réutiliser une vue en dehors de son controller (pour les websockets). Vincent a déjà présenté cette nouveauté hier dans un article.
  • API Mode : ils ont encore améliorer la possibilité de faire d’un projet rails une API en créant le mode –api lors de la création d’un projet. Ex : rails new mon_api –api va nous générer une structure de projet simplifiée nous permettant de ne travailler qu’avec du JSON, et non du HTML. D’autres améliorations seront apportées mais il semble qu’ils se sont beaucoup penchés sur la partie JSON pour le moment.
  • Commandes rails vs. rake : certaines commandes étaient placées dans bin/rails et d’autres dans bin/rake.  Dans Rails 5, tout a été rassemblé dans bin/rails du coup il faudra s’habituer à faire un rails db:migrate au lieu de rake db:migrate (certaines commandes seront encore disponibles sous rake mais autant prendre l’habitude dès le début).
  • Test runner : lorsque l’on exécute des tests les failures sont désormais affichées en live, pas besoin d’attendre la fin des tests pour repérer d’où vient le problème.
  • Turbolinks 3 : Turbolinks permet de rafraichir des pages plus rapidement en rechargeant le contenu du body d’une page depuis le serveur au lieu de rafraichir la page et de recompiler à chaque fois le js et le CSS. Au lieu de faire comme l’ajax il remplace tout le body par défaut mais il est possible de spécifier quel élément rafraichir. Cela semble utile pour les sites one-pages. Les avis sont vraiment partagés, certains trouvent cela révolutionnaire d’autres s’en passeront comme sur Rails 4.
  • « Et ça sort quand ? » : aucune date n’a été donnée (trouvée ?) donc on ne sait pas. Mais la communauté est très active et les betas qui sortent montrent qu’il y a énormément de contributeurs qui sont actifs sur le sujet.

Pour clôturer cette petite présentation, j’ai installé un Rails 5 (beta) et développé une petite démo en utilisant ActionCable, le tout en moins de 2 heures (ce qui s’annonce très prometteur, nous y reviendrons probablement sur d’autres billets). Vivement la suite !

Rails et le temps réél vs Nodejs

Ah, le temps réel, saint Graal du développeur, oui mais pas si simple !

Du coup on a tenté le temps réel en Rails. Apparemment une gem, Faye, existe en Ruby donc tout semblait bien partie quand après de multiples tentatives nous nous sommes aperçus qu’il fallait pour cela 2 serveurs web, le serveur habituel de Rails (et encore que, il fallait utiliser thin en local et passenger en prod) et un second serveur web pour le temps réel. Déjà là s’est posé à nous la question de la maintenabilité de ce joyeux bazar.

Bon, passons, mais même avec ces 2 serveurs la mise en place n’est pas aisée, la documentation pour faire du Rails en temps réel n’est pas très fournie. On a cherché mais au bout de 2 heures peu fructueuses nous nous sommes dit que quitte à avoir une maintenance un peu plus lourde pourquoi ne pas partir sur du Node js si besoin, peut-être plus adapté au temps réel avec la fameuse librairie socket.io.

Ces 2 compères sont très compatibles et son souvent utilisés ensemble dans les tutoriels sur nodejs, avec une documentation assez récente et fournie.

Nous verrons bien lorsque le besoin de temps réel se fera ressentir si un proof of concept nodejs – socket.io fera l’affaire.

Affaire à suivre et à creuser.

Les jointures SQL en Rails 4

Imaginons que votre but est de récupérer tous les résultats dont l’id est la clé étrangère des lignes d’une autre table si ces dernières contiennent une valeur donnée.

La première solution consiste à utiliser le select de Ruby (retourne un tableau d’éléments Active Record) :

La seconde est de le faire en SQL (retourne un tableau d’éléments Active Record) :

La troisième solution est d’utiliser la méthode joins (retourne un objet Active Relation) :

Cependant, les résultats retournés par la méthode joins sont chargés en lecture seule et sans leurs relations, ce qui peut poser problème.

Cette jointure SQL est de type Inner JOIN : il s’agit d’une intersection entre les tables comparées, seules les lignes strictement en commun seront retournées. La raison pour laquelle les résultats sont marqués readonly vient de Rails : les « piggy-back attributes » sont, d’après ce topic StackOverFlow, les attributs de la seconde table qui sont stockés dans les objets retournés. Ces objets ne représentent pas le modèle et ne peuvent donc pas être sauvegardés.

Le comportement de la méthode joins peut être modifié en passant des paramètres, pour obtenir n’importe quel type de jointure (doc). Il est fréquent que cette méthode retourne des doublons dans une relation belongs_to_has_many, veillez à chainer la méthode uniq si nécessaire.

Il existe d’autre part la méthode includes qui s’utilise exactement comme joins (retourne un objet Active Relation) :

À la différence de joins, cette méthode charge toutes les associations des objets demandés : cette technique s’appelle l’eager loading et peut s’optimiser en utilisant includes correctement (doc).

La « stratégie » des requêtes qui est implémentée dans cette méthode varie : elle peut soit effectuer plusieurs requêtes soit un LEFT OUTER JOIN.

La documentation alerte sur l’effet potentiellement inattendu de cette méthode, du fait de ce type de jointure SQL. Elle recommande d’écrire sa propre requête à l’aide de la méthode joins : voyons comment faire.

Dans l’exemple plus haut, nous avions une requête retournant des résultats corrects, sans eager loading, mais contenant beaucoup trop de champs pour représenter notre modèle. Précisons-les :

Les résultats de cette requête seront désormais enregistrables, mais l’utilisation de liaisons déclenchera de nouvelles requêtes. Il est bien sûr possible de placer cette requête dans un scope.

L’eager loading n’est pas toujours une bonne solution, car le coût de récupérer la totalité d’une table peut s’avérer moins efficace que de récupérer la poignée de résultats dont vous avez besoin. Cela ne semble toutefois pas possible avec la méthode joins.

4 strates à changer pour accélérer une app Ruby on Rails

Le framework Ruby on Rails est livré avec son propre serveur Web, appelé Webrick, mais il est souvent nécessaire d’avoir des VirtualHosts afin d’héberger plusieurs sites sur un même port. Nous allons voir quels sont les choix techniques qui s’offrent à nous.

1- Remplacer le serveur Web applicatif

Comme énoncé en introduction, Webrick est le serveur Web livré par défaut avec Ruby on Rails pour exécuter nos applications. Il existe de nombreuses alternatives telles qu’Unicorn ou Puma, cependant Phusion Passenger est celle qui est recommandée par les auteurs de RoR pour un passage en production.

Phusion Passenger est notablement plus rapide que ses concurrents :

Phusion PassengerCe projet Libre met en avant sa capacité à multi-threader le traitement des requêtes et à générer son propre cache, offrant ainsi de meilleures performances. Il est disponible en module pour Apache et Nginx.

2- Choisir un serveur Web frontal

Il est fréquent de souhaiter héberger plusieurs applications derrière la même adresse IP et sur un même port (80), parfois dans des langages différents. Le recourt à un serveur aussi spécialisé que Phusion Passenger rendrait cette cohabitation impossible. La solution consiste à intercaler un serveur Web entre les internautes et le serveur Web applicatif.

Apache est un serveur Web multiplateforme, capable de servir des fichiers statiques et de déléguer l’exécution de code source à un module. S’il est très répandu et occupe aux alentours de 40% de parts de marché, il n’est cependant pas réputé pour sa légèreté ni pour sa rapidité.

Heureusement la diversité ne manque pas, Nginx (prononcer « engine ex ») est un concurrent sérieux, qui avoisine 15% de parts de marché. Simple, robuste, léger et très rapide, son fonctionnement diffère de celui d’Apache tout en se configurant de manière presque identique.

Performances

Lors de chaque requête HTTP qu’Apache reçoit, un nouveau thread est créé. Ce comportement occasionne certains désagréments, comme un ralentissement dans le traitement des requêtes au fil du temps.

De son côté, Nginx reçoit d’abord toutes les requêtes dans le même thread, puis créé des threads par paliers au fur et à mesure de la montée en charge. Cela a pour effet une consommation en Ram presque linéaire et une rapidité de traitement plus élevée qu’Apache.

Ces constats ne sont toutefois vérifiables qu’en charge significative, par exemple dans ce benchmark daté de 2008 observant le cas précis de la distribution de fichiers statiques pour 3500 connexions simultanées :
– Apache traite 2500 requêtes / seconde pour 35Mo de Ram
– Nginx traite 9500 requêtes / seconde pour 2,5Mo de Ram

Nginx a par ailleurs été choisi par WordPress.com car c’était le seul serveur capable de traiter 10M de requêtes / jour (soit plus de 8000 par seconde) ainsi que pour sa stabilité. Apache a été corrigé et optimisé dans sa version 2.4 mais conserve toujours la même architecture.

Fonctionnalités

D’un point de vue configuration, Apache et Nginx proposent une syntaxe semblable, bien que plus simple dans le cas d’Nginx. La rédaction de VirtualHost est très proche, ces derniers étant rangés dans les deux cas dans des répertoires appelés « sites-availables » et « sites-enabled ». Les deux serveurs disposent de modules parfois communs, comme par exemple Passenger qui permet d’exécuter du code Ruby.

3- Utiliser une solution de cache

Après avoir optimisé notre code source, il devient envisageable de faire intervenir un système de cache au niveau applicatif.

En fonction du dynamisme de l’application, la fréquence de rafraichissement du cache peut varier de quelques minutes à plusieurs mois. Par exemple, la page produit d’un site d’e-commerce ne sera regénérée que lors d’un achat, afin de changer le décompte du stock. La partie publique aurait tout intérêt à être rendue statique au format HTML.

La génération d’un cache de ce type peut être réalisé avec la gem développée par l’équipe de RoR, appelée actionpack-page_caching.

4- Ajouter un reverse proxy

Il est possible d’étudier une solution spécifique pour servir nos fichiers statiques plus rapidement qu’avec notre serveur Web frontal. Cette solution ne semble pas particulièrement intéressante au premier abord, car le plus grand du temps de génération d’une page provient des algorithmes mis en œuvre dans notre application Ruby on Rails.

Mais avec l’utilisation d’une gem telle que décrite dans le chapitre précédent, notre site n’a plus besoin d’être dynamique la plupart du temps.

C’est à ce moment que le serveur Varnish entre en jeu :

Varnish-Nginx-PassengerVarnish est un reverse-proxy optimisé pour servir des fichiers statiques. Dans cette configuration, il interceptera les requêtes sur le port 80 et les transfèrera à Nginx sur un autre port. La gestion des VirtualHost reste sur Nginx bien que nécessitant une rapide adaptation.