Faites simple

On m'a récemment posé la question sur les 3 créations ou innovations qui avaient le plus marqué le monde de l'information ces dernières années. Sans devoir trop y reflechir la liste est aussi diverse qu'évidente pour moi : Google, Apple iPod/iTunes/iTunes store, et les Box de nos fournisseurs d'accès à Internet. Google se caractérise par une pauvreté fonctionnelle de prime abord dans l'interface exposée aux utilisateur, mais la puissance, la fiabilité, et la robustesse en font maintenant un outil globalement incontournable dans le Web d'aujourd'hui. Les internautes n'imaginent pas d'utiliser autre chose. On peut mesurer cette adhésion forte jour après jour quand on voit les autres moteurs de recherche se casser les dents, et ne pas parvenir à atteindre une masse critique. Le système Google prend toute son ampleur avec ses autres produits sur le même principe, par exemple Reader.  Il va à l'essentiel pour quelqu'un qui débute dans l'utilisation des flux RSS, et apporte des informations, et des fonctions de haut niveau pour le lecteur  chevronné. Lorsque Apple a lancé les iPods le produit est apparu comme magique tant sa facilité d'utilisation était déconcertante. Vous mettiez cet appareil dans les main de n'importe qui, le coté intuitif rendait son apprentissage quasi instantané. D'entrée l'application iTunes a été positionnée comme outil incontournable avec l'intégration directe vers la boutique iTunes store en ligne. Cette intégration forte vers les autres produits et services de la marque à la pomme est maintenant incontournable. En chaînant ainsi les produits complémentaires, elle pérennise ses revenus. Avec l'arrivée de produits plus complexes comme l'iPod Touch, Apple est parvenu à toucher une seconde génération d'utilisateurs qui veulent plus de technologie directement accessible. Malgré ce changement les produits conservent une facilité qui garanti aussi bien une vitesse d'adoption et une addiction forte. Les fournisseurs d'accès avec la création des box ont très largement démocratisé l'accès à internet. Si on revient un petit peu en arrière avant ces offres très intégrées, on avait des matériels qui ne remplissaient qu'une seule fonction, vous connecter à Internet. Pour le téléphone et la télévision il fallait continuer à utiliser d'autres produits. Au delà de la faible couverture fonctionnelles de ces offres, souvent les réglages pouvaient êtres aussi compliqués qu'aléatoires. Quand les offres tout en un sont arrivées, les utilisateurs ont vu débarquer des matériels qui changeait radicalement leur accès aux technologies de communications.  Une inquiétude est apparue : est ce que tout ce matériel ne va pas être trop complexe à utiliser ?  Une fois de plus l'adhésion et le succès sont passé par quelques fonctions essentielles faciles à utiliser et à mettre en oeuvre, et d'autres un peu plus complexes pour accéder aux autres services. On pourrait citer des tonnes d'autres exemples comme par exemple Twitter avec son service qui ne fait pas grand-chose, mais qui peut être enrichit par une API ouverte. Pour résumer tout ça, il est essentiel lorsque qu'on innove et qu'on met à la disposition du public de nouveaux produits de bien limiter le scope d'utilisation auquel est soumis l'utilisateur. Tout un chacun peut assimiler un périmètre plus ou moins restreint, ne noyons pas l'utilisateur sous trop d'innovations. La vraie innovation c'est finalement la simplicité et le fait de ne pas transformer l'utilisateur en spécialiste. C'est d'ailleurs ce dernier point qui semble être un facteur tant d'efficacité, que de succès commercial.

Google AppEngine et le traitement des données

Une des grande capacité des architectures CloudComputing c'est la capacité de scalabilité de celles-ci. Google AppEngine ne déroge pas à la règle, avec une capacité à stocker de gros volumes de données, mais aussi à absorber de grands volumes de connections. Lorsqu'il est nécessaire de traiter des volumes de données importants, pour par exemple mettre à jour une base, ou déclencher des emailing, les temps de process peuvent s'accroître et sortir du cadre d'utilisation classique de la plate-forme. Il y a cependant des solutions pour bien gérer ces problématiques. Elles consistent essentiellement à structurer les accès aux données, et utiliser l'ensemble des outils disponibles. Prenons le cas assez simple d'une table qu'on va parcourir pour mettre à jour des enregistrements. A chaque objet renvoyé par la boucle "for", on enregistre la mise à jour dans la base.

for entity in MyModel.all().filter("color =",
   old_favorite).fetch(100):
   entity.color = new_favorite
   entity.put()

En faisant de cette manière, on fait 1 transaction pour récupérer les 100 enregistrements, puis 1 nouvelle transaction à chaque mise à jour, ce qui nous fait un total de 101 transactions. Dans le système de base de données "Bigtable" de Google Appengine c'est globalement l'appel à l'API qui coute cher. En comparaison avec ce qu'on a pu voir au dessus, il y a une autre façon de réaliser ce traitement :

updated = []
for entity in MyModel.all().filter("color =",
   old_favorite).fetch(100):
   entity.color = new_favorite
   updated.append(entity)
db.put(updated)

Le nombre de transaction chute de manière drastique puisqu'on passe de 101 à 2. L'opération d'enregistrement est beaucuop plus lourde que pouvaient l'être les 101 équivalentes, mais le nombre de call à l'API est réduit à maximum, donc plus efficace. Lorsque de gros volumes de données sont à traiter il peut arriver que le temps de traitement dépasse ce qui est autorisé pour l'exécution d'une requête par Google AppEngine. Depuis la dernière version de Google AppEngine, il est possible de gérer une file d'attente des traitements sur le serveur. Prenons ici l'exemple fourni dans la documentation, il s'agit d'un mailing de masse sur l'ensemble des utilisateurs d'une base. Il y a fort à parier que ce type traitement dépasse très largement dans sa globalité les quelques secondes autorisées pour l'exécution d'une requête. En découpant le traitement en des taches suffisamment petites, et en les mettant dans une file d'attente, ce traitement devient immédiatement scalable.

# for each user, add a task to send a custom email message
for u in users:
   taskqueue.add(url='/work/sendmail',
      params=dict(to=u.email,
         subject='Hello ' + u.name, body='this is a message!'))
return # finished now, emails will be sent offline when tasks execute

# task handler at /work/sendmail, automatically called for each task created above
class MailWorker(webapp.RequestHandler):
   def post(self):
      mail.send_mail('from_me@example.com',
                          self.request.get('to'),
                          self.request.get('subject'),
                          self.request.get('body'))

Modèles de données de Google AppEngine

Face à la problématique du portage en environnement Google AppEngine d'une application qui doit gérer des volumes de données importantes, il y a quelques bonnes pratiques qu'il faut utiliser absolument. La récente conférence Google IO a été l'occasion de voir une conférence sur le thème de "building scalable application". Les éléments exposés ici ne sont qu'une partie de cette conférence. L'utilisation StringListProperty permet de réduire de manière importante le nombre de Model de données à gérer. Le format de stockage des informations est beaucoup plus dense, et la recherche d'infos est plus simple. La mise en œuvre est très simple, par exemple pour gérer les couleurs favorites d'un utilisateur :

class Favorites(db.Model):
  colors = db.StringListProperty()
  username = db.StringProperty()

fav.colors = ["red", "blue", "green"]

Pour retrouver tous les utilisateurs qui aiment le jaune :

results = db.GqlQuery( "SELECT * FROM FavoriteColors WHERE color = 'yellow'")
users = [r.username for r in results]

Les petites contraintes sont par contre une consommation supérieure de CPU pour la serialisation, et les tris sont un peu plus difficiles à réaliser. Si on compare la façon de laquelle une application de microblogging serait faite avec un outil de base de données classique :

SELECT * FROM Messages INNER JOIN UserMessages 
USING (message_id) WHERE UserMessages.user_id = 'X';

Et maintenant avec App Engine :

class Message(db.Model):
  sender = db.StringProperty()
  receivers = db.StringListProperty()
  body = db.TextProperty()

results = db.GqlQuery("SELECT * FROM Message WHERE receivers = :1", me)

C'est tout de suite plus simple ! Par contre si les propertylist sont de très grande tailles (plus de 100 items) les performances peuvent être fortement affectées. La solution dans ce cas est de couper l'enregistrement en deux en mettant la liste d'un coté, et les données de l'autre. En SQL ceci nécessiterait de réaliser une complexe jointure entre les 2 tables mais ici c'est immédiatement plus simple.

class Message(db.Model):
  sender = db.StringProperty()
  body = db.TextProperty()

class MessageIndex(db.Model):
  receivers = db.StringListProperty()

indexes = db.GqlQuery("SELECT __key__ FROM MessageIndex WHERE receivers = :1", me)
keys = [k.parent() for k in indexes]
messages = db.get(keys)

Voila pour cette rapide introduction aux structures de données de l'AppEngine. Si il y a vraiment un point qu'il ne faut pas oublier lorsqu'on utilise des outils de type CloudComputing c'est bien la scalabilité. Avec ces bonnes pratiques, c'est bien de toute l'expérience de Google sur le sujet  que vous aller pouvoir profiter .

Google AppEngine : Le rendu de pages HTML

Après avoir regardé la structure générale du framework et l'organisation des fichiers, nous avons regardé la structure applicative. Cette fois ci nous allons nous atttaquer à un gros morceau puisque nous allons évoquer ensemble le moteur de rendu des pages HTML. Ceux qui sont habitués aux appli web développées en Python ne vont pas trop être surpris puisque Google s'est appuyé sur le reconnu Django. Nous allons passer en revue le fonctionnement de ce moteur, mais je vous recommande d'aller voir la documentation de Django pour approfondir le sujet. Après avoir fait donc ce rendu de base on va maintenant regarder comment créer des blocs de rendu, avec une possiblité d'organiser le code, et de réutiliser facilement celui-ci.

Google App Engine : HelloWorld !

Dans le précédent post je vous avait présenté Google App Engine dans sa globalité, afin de positionner ce framework de développement. Maintenant nous allons entrer plus en détail avec la mis en oeuvre d'un petite application. Au titre vous aurez compris que tout ça va rester trivial pour le moment ! Pour aller un peu plus loin il peut être utile de structurer un peu le code et de pouvoir le décomposer en modules. Ici aussi rien d'extraordinaire, seulement des choses simples, que nous allons découvrir pas à pas. 

 

Google App Engine

Ce post est le premier d'une série sur google App Engine le moteur d'exécution d'application de google en mode cloud computing. Ce framework s'appuie sur Python, langage largement utilisé sur les frontaux web de Google, et permet de déployer des applications sur les serveurs de Google. Tout ceci est d'une pris en main très rapide, même si vous n'avez pas de réelle expérience de Python. Je vous laisse découvrir ça. Principaux liens :