SlideShare une entreprise Scribd logo
Tribulations d'un développeur Java
           dans le Cloud
              Tugdual Grall
                @tgrall




                                     1
Ce que vous allez apprendre


•   Retour d’experience d’un developpeur du “Dimanche”

•   Comprendre “mes” choix

•   Difficultés Surprises du Cloud




                                                         2
About me : “Tug”
•   CTO chez eXo depuis 2008

•   Développeur, Product Manager chez Oracle

•   Co fondateur du NantesJUG

•   @tgrall sur Twitter & RunKeeper

•   Triathlete

•   Développeur de resultri.com

                                               3
Agenda


•   Le Cloud, Pourquoi ? Comment ?

•   Les surprises Bonnes et Mauvaises

•   Et Alors ?




                                        4
Le cloud, pourquoi?
•   Besoins fonctionnels :

    •   Analyser les résultats de courses :Triathlons ou autres

    •   Partager ces résultats simplement : réseaux sociaux

•   Besoins techniques :

    •   Apprendre à développer pour le cloud

    •   Pas de gestion système, ressources disponibles

                                                                  5
6
Choisir une plateforme
•   Aout 2011, avant mon départ en vacances

•   Support du mode déconnecté

•   Java & NoSQL

•   Documentée et “Connue”

•   Gratuite

•   Disponible sur Mac OS X sans “hack”

                                              7
Choisir une plateforme
•   Aout 2011, avant mon départ en vacances

•   Support du mode déconnecté

•   Java & NoSQL

•   Documentée et “Connue”

•   Gratuite

•   Disponible sur Mac OS X sans “hack”

                                              7
Google AppEngine

•   Google PaaS

•   Languages : Java, Python and Go

•   Base de données : BigTable (NoSQL) & CloudSQL (MySQL)

•   Accès simplifié aux API et Services Google

    •   Google Cloud Storage, Analytics, Google Apps, Maps...


                                                                8
Resultri en quelques mots
•   Pour les Geeks                  •   Pour les Sportifs

    •   Pure Servlet/JSP                •   33 courses

    •   REST avec JAX-RS (Jersey)       •   46 754 résultats individuels

    •   Twitter Boostrap                •   3500 visiteurs uniques

                                        •   11 300 pages vues




                                                                           9
Traitement des données



                         10
Accès aux données?


•   Ma premiere réelle experience du monde NoSQL

    •   Mon passé (passif?) : 9 années d’Oracle, ... très SQL (JPA,Toplink, BC4J !)

    •   API : JPA, JDO, Datastore API ou autre (Objectify) ?




                                                                                      11
Accès aux données?

•   Mon choix : JDO & Datastore API

    •   Limité aux API Google (documenté dans le SDK)

    •   JDO : car la doc était la plus complète

    •   Datastore : est venu ensuite pour la simplicité



                                                          12
Resultri: Les Données




Sites “résultats”
 HTML, TXT, CSV, XML




                                               13
Resultri: Les Données




Sites “résultats”           Traitements en local
 HTML, TXT, CSV, XML         Utilisation du GAE Dev Server
                                       Export CSV




                                                             13
Resultri: Les Données




Sites “résultats”           Traitements en local              Import GAE
 HTML, TXT, CSV, XML         Utilisation du GAE Dev Server   Utilisation des outils GAE
                                       Export CSV                    Import CSV




                                                                                          13
Dev to “Cloud” 1/2
 1:   DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();



                                                                                         •
 2:   ...

                                                                                             Une fois les données
 3:   Map<String, Object> propertiesMap = result.getProperties();
 4:   properties = (String[]) propertiesMap.keySet()...;
 5:   ...
 6:
 7:
      Query q = new Query("TriathlonResult");
      q.addFilter("raceURI", Query.FilterOperator.EQUAL,race);
                                                                                             importées en local
 8:   PreparedQuery pq = datastore.prepare(q);



                                                                                         •
 9:   for (Entity result : pq.asIterable()) {
10:
11:
12:
      !    for (int i = 0; i < properties.length; i++) {

      Object o = result.getProperty(properties[i]);
                                                                                             Création d’un Fichier CSV
13:   String printValue = null;



                                                                                         •
14:   if (o != null) {
15:
16:
      !
      !
           if (o instanceof java.util.Date) {
           !    SimpleDateFormat formatter = new SimpleDateFormat("yyy ... H:mm:ss") ;
                                                                                             Utilisation des API Datastore
17:   !    !    Date date = (Date) o;



                                                                                         •
18:   !    !    printValue = formatter.format(date);
19:
20:
      !
      }
           } else if ... { ...!}
                                                                                             Trop “couteux” à faire sur le
                                                                                             Cloud
21:   out.print((printValue == null)?"":printValue);
22:   out.print(",");
23:   !    }
24:   ...




                                                                                                                             14
Dev to “Cloud” 2/2
appcfg.py upload_data
    --config_file=config.yml
    --filename=ironman-south-africa.csv
    --url=http://dev-result-db.appspot.com/remote_api
    --application=dev-result-db
    --kind=TriathlonResult

Uploading data records.
[INFO ] Logging to bulkloader-log-20120416.155812
[INFO ] Throttling transfers:
                                                                     •   Création des entités par CLI
[INFO ] Bandwidth: 250000 bytes/second
[INFO ] HTTP connections: 8/second
[INFO ] Entities inserted/fetched/modified: 20/second
[INFO ] Batch Size: 10
                                                                     •   Très rapide

                                                                     •
[INFO ] Opening database: bulkloader-progress-20120416.155812.sql3
[INFO ] Connecting to dev-result-db.appspot.com/remote_api
Please enter login credentials for dev-result-db.appspot.com
                                                                         Import des données “formatées”
Email: tugdual@gmail.com
Password for tugdual@gmail.com:
[INFO ] Starting import; maximum 10 entities per post
.................................................................
[INFO ] 1552 entities total, 0 previously transferred
[INFO ] 1552 entities (7016782 bytes) transferred in 60.4 seconds
[INFO ] All entities successfully transferre


                                                                                                          15
Recherche Full Text
•   Beta privée en Octobre...

•   Solution “Resultri”

    •   Utilisation de Cloud SQL




                                        16
Recherche Full Text
•   Beta privée en Octobre...

•   Solution “Resultri”

    •   Utilisation de Cloud SQL


            First & Last Names




BigTable                         CloudSQL
                                            16
Accès aux données
•   Attention aux quotas !




                                    17
Accès aux données
•   Utilisation de Memcache

    •   Cache disponible dans GAE

    •   Gestion du cache par le biais de l’API JCache (JSR-107) ou API Google

•   Dans Resultri:

        •   Tout Entity est automatiquement cachée




                                                                                18
Memcache




           19
Les surprises ....



                     20
Création de données
 1:   log.info(" -- Start -- ");
 2:   DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
 3:   Entity employee = new Entity("Employee");
 4:   employee.setProperty("firstName", "Nicolas");
 5:   employee.setProperty("lastName", "Martignole");
 6:   Date hireDate = new Date();
 7:   employee.setProperty("hireDate", hireDate);
 8:   employee.setProperty("attendedHrTraining", true);
 9:   datastore.put(employee);
10:   employee = new Entity("Employee");
11:   employee.setProperty("firstName", "Antonio");
12:   employee.setProperty("lastName", "Goncalvez");
13:   hireDate = new Date();
14:   employee.setProperty("hireDate", hireDate);
15:   employee.setProperty("attendedHrTraining", true);
16:   datastore.put(employee);
17:   Query q = new Query("Employee");
18:   PreparedQuery pq = datastore.prepare(q);
19:   for (Entity emp : pq.asIterable()) {
20:   !   log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" );
21:   }
22:   log.info(" -- End -- ");




                                                                                       21
Création de données
 1: log.info(" -- Start -- ");
 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
 3: Entity employee = new Entity("Employee");
 4: employee.setProperty("firstName", "Nicolas");
 5: employee.setProperty("lastName", "Martignole");
 6: Date hireDate = new Date();
1: Apr 16, 2012 4:22:25 "hireDate", hireDate);
 7: employee.setProperty( PM com....Servlet doGet
2: INFO: -- setProperty("attendedHrTraining", true);
 8: employee. Start --
3: Apr 16, 2012 4:22:25 PM com....Servlet doGet
 9: datastore.put(employee);
4: INFO: Nicolas Martignole
10: employee = new Entity("Employee");
5: Apr 16, 2012 4:22:25 "firstName", "Antonio");
11: employee.setProperty( PM com....Servlet doGet
6: INFO: -- setProperty("lastName", "Goncalvez");
12: employee. End --
7: Apr 16, 2012 4:Date(); com.......$PersistDatastore persist
13: hireDate = new 22:34 PM
8: INFO: Time to persist"hireDate",: hireDate);
14: employee.setProperty( datastore 4 ms
15: employee.setProperty("attendedHrTraining", true);
16: datastore.put(employee);
17: Query q = new Query("Employee");
18: PreparedQuery pq = datastore.prepare(q);

20: !
               Mais où est parti Antonio????
19: for (Entity emp : pq.asIterable()) {
        log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" );
21: }
22: log.info(" -- End -- ");




                                                                                     21
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addSort("lastName");
3:   q.addSort("firstName");
4:   PreparedQuery pq = datastore.prepare(q);
5:   for (Entity emp : pq.asIterable()) {
6:    o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") );
7:   }




                                                                                    22
Datastore : Queries

                                                                                     1:   Doe John
                                                                                     2:   Goncalvez Antonio
1:   Query q = new Query("Employee");
                                                                                     3:   Grall Briac
2:   q.addSort("lastName");                                                          4:   Grall Corentin
3:   q.addSort("firstName");                                                         5:   Grall Malo
4:   PreparedQuery pq = datastore.prepare(q);                                        6:   Grall Nolwenn
5:   for (Entity emp : pq.asIterable()) {                                            7:   Grall Tug
                                                                                     8:   Grall Virginie
6:    o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") );    9:   Martignole Nicolas
7:   }                                                                              10:   Tabarly Eric




                                                                                                               22
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addSort("lastName");
4:   q.addSort("firstName");
5:   PreparedQuery pq = datastore.prepare(q);
6:   for (Entity emp : pq.asIterable()) {
7:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
8:   }




                                                                                     23
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
                                                                                     1:   Grall   Briac
3:   q.addSort("lastName");                                                          2:   Grall   Corentin
4:   q.addSort("firstName");                                                         3:   Grall   Malo
5:   PreparedQuery pq = datastore.prepare(q);                                        4:   Grall   Nolwenn
6:   for (Entity emp : pq.asIterable()) {                                            5:   Grall   Tug
                                                                                     6:   Grall   Virginie
7:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
8:   }




                                                                                                             23
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");
5:   q.addSort("firstName");
6:   PreparedQuery pq = datastore.prepare(q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                     24
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");

                                                                                     ?
5:   q.addSort("firstName");
6:   PreparedQuery pq = datastore.prepare(q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                         24
Datastore : Queries

1:   Query q = new Query("Employee");
2:   q.addFilter("lastName", FilterOperator.EQUAL , "Grall");
3:   q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000);
4:   q.addSort("lastName");
     java.lang.IllegalArgumentException:

                                                                                                   ?
5:   q.addSort("firstName");
        The first sort property must be the same as the property to which the inequality filter is applied.
6:   PreparedQuery pq = datastore.prepareproperty is firstName but the inequality filter is on birthYear
        In your query the first sort (q);
7:   for (Entity emp : pq.asIterable()) {
8:    o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") );
9:   }




                                                                                                              24
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
4:   o.println(results);




                                                                                      25
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
                                                                                      1: Grall Malo
4:   o.println(results);




                                                                                                      25
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
3:   List<Employee> results = (List<Employee>) q.execute(); !
4:   o.println(results);




                                                                                      26
Datastore : Queries


1:   PersistenceManager pm = PMF.get().getPersistenceManager();
2:   Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
3:
4:
     List<Employee> results = (List<Employee>) q.execute(); !
     o.println(results);                                                              ?




                                                                                          26
Datastore : Queries


 1: PersistenceManager pm = PMF.get().getPersistenceManager();
org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException:
 2: Problem = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'");
     Query q with query <SELECT FROM com.grallandco.model.Employee WHERE lastName=='Grall' || firstName=='Malo'>:
 3: Or filters cannot be applied<Employee>) q.execute(); ! (found both lastName and firstName).
     List<Employee> results = (List to multiple properties
 4: o.println(results);                                                                              ?




                                                                                                                    26
Manipulation des données
•   Les données ne sont pas toujours disponibles

    •   High Data Replication (HDR) : surprenant pendant les tests

•   “Limitations” des requêtes (GQL) et JDO

•   Attentions aux index : sans index pas de “requetes”

    •   Verifier la disponibilité des index

•   Utilisation de Memcache (must have! )

                                                                     27
Autres services



                  28
Task Queues
•   Service pour l’execution de taches en arrière plan

    •    Possibilité d’inclure les taches dans un ordonnanceur (cron)

•   Resultri:

    •    Synchronization (BigTable/CloudSQL) , Nettoyage des données
        1: Queue queue = QueueFactory.getQueue("SynchappAppQueue");
        2: TaskOptions taskOptions = TaskOptions.Builder.withUrl("/worker/ProcessSynchronizeDataServlet")
        3:      .param("race-uri", "ironman-florida-2005")
        4:      .param("type", "syncDB")
        5:      .method(TaskOptions.Method.POST);
        6: queue.add(taskOptions);



                                                                                                            29
Test en Local
•   Un version de Jetty avec le SDK Google AppEngine




Simulation “Authentification Google”                    Console Developpeur


                                                                             30
Disponibilité des services




                             31
Authentification Google
       •   Utilisation de Google Account

           •   Se base sur la sécurité JavaEE standard

           •   Gestion des administrateurs par le biais de la console AppEngine

	   <security-constraint>
	   	   <web-resource-collection>
	   	   	   <url-pattern>/admin/*</url-pattern>
	   	   </web-resource-collection>
	   	   <auth-constraint>
	   	   	   <role-name>admin</role-name>
	   	   </auth-constraint>
	   </security-constraint>




                                                                                  32
URL Fetch
•   Accès à des serveurs autres que AppEngine par HTTP/HTTPS

    •    Utilisation de java.net.URL ou
         com.google.appengine.api.urlfetch.URLFetchService

•   Dans Resultri : utilisation du service de GeoCodage de Google Maps



    1:   URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address="+ address );




                                                                                                                  33
Accès aux API Google
•   Google offre de nombreux Services et API

    •   Un seul compte => tous les services




                                               34
Resultri & Google
•   Les services que j’utilise pour Resultri

    •   Google Apps : Gestion du nom de domaines, Mail, ...

    •   CloudSQL : Recherche Full Text (MySQL)

    •   Cloud Storage : Sauvegarde BigTable, Serveur de fichiers

    •   Google Maps

    •   Analytics, Adsense, Webmaster


                                                                  35
En production



                36
Gratuit ?

•   Oui mais....

    •   Avec des quotas

    •   Les règles peuvent changer

        •   Septembre : Google modifie ses tarifs



                                                   37
Gratuit ?

•   Oui mais....

    •   Avec des quotas

    •   Les règles peuvent changer

        •   Septembre : Google modifie ses tarifs



                                                   37
Exemple




          38
Exemple




Import CSV
             38
Gestion du cout
•   En Fevrier : Augmentation du cout

    •   Pas d’activité utilisateur, mais cout en augmentation




                                                                39
Gestion du cout
•   En Fevrier : Augmentation du cout

    •   Pas d’activité utilisateur, mais cout en augmentation




•   Une idée ?

                                                                39
Merci Google!
•   Google Bots, et autres moteurs de recherche

    •   Utilisation de Google Webmaster Tools

    •   Mise en place d’un fichier robots.txt




                                                  40
Merci Google!
•   Google Bots, et autres moteurs de recherche
                                                  User-agent: Twiceler


    •
                                                  Disallow: /
        Utilisation de Google Webmaster Tools     User-agent: psbot
                                                  Disallow: /



    •
                                                  User-agent: *
        Mise en place d’un fichier robots.txt      Disallow: /search
                                                  Disallow: /races/*/compare
                                                  Disallow: /rest
                                                  Disallow: /races/*/*/position
                                                  Disallow: /races/*/*/rank
                                                  Disallow: /races/*/*/otherRaces
                                                  Disallow: /races/*?page=-*




                                                                                    40
Merci Google!
•   Google Bots, et autres moteurs de recherche
                                                  User-agent: Twiceler


    •
                                                  Disallow: /
        Utilisation de Google Webmaster Tools     User-agent: psbot
                                                  Disallow: /



    •
                                                  User-agent: *
        Mise en place d’un fichier robots.txt      Disallow: /search
                                                  Disallow: /races/*/compare
                                                  Disallow: /rest


    •
                                                  Disallow: /races/*/*/position
        243 784 URL bloquées sur Resultri         Disallow: /races/*/*/rank
                                                  Disallow: /races/*/*/otherRaces
                                                  Disallow: /races/*?page=-*




                                                                                    40
Console




          41
Console




          41
Console




          41
Conclusion



             42
Conclusion
•   AppEngine est-il le bon choix pour Resultri?

    •   Pour l’instant trop couteux et “puissant”

    •   Techniquement passionnant

•   Feuille de route

    •   Gestion des utilisateurs : Facebook, Twitter, Google

    •   Notifications

    •   Version Mobile

                                                               43
Conclusion
•   Techniquement : j’apprends beaucoup....   •   Publication d’articles sur mon blog

    •   NoSQL, CloudSQL, Memcache....         •    Twitter Boostrap 2 and Google Maps
                                              •    Google AppEngine Full Text Search with Cloud
    •   Utilisation de Git                         SQL
                                              •    Installing Memcached on Mac OS X and using it in
    •   Twitter Bootstrap, Less                    Java
                                              •    JAX-RS: Jersey and JSON single element arrays
                                              •    Create and Deploy a JAX-RS REST service on
                                                   Google App Engine
                                              •    ....




                                                                                                      44
Questions?




             45
Tribulations d'un développeur Java
           dans le Cloud
              Tugdual Grall
                @tgrall




                                     46

Contenu connexe

Tendances

Paris data geek - Elasticsearch
Paris data geek - ElasticsearchParis data geek - Elasticsearch
Paris data geek - Elasticsearch
David Pilato
 
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
MongoDB
 
Mongo DB
Mongo DBMongo DB
L'expérience client au centre de la donnée @AirFrance
L'expérience client au centre de la donnée @AirFranceL'expérience client au centre de la donnée @AirFrance
L'expérience client au centre de la donnée @AirFrance
MongoDB
 
introduction à MongoDB
introduction à MongoDBintroduction à MongoDB
introduction à MongoDB
Abdoulaye Dieng
 
Elasticsearch - Montpellier JUG
Elasticsearch - Montpellier JUGElasticsearch - Montpellier JUG
Elasticsearch - Montpellier JUG
David Pilato
 
Poitou charentes JUG - Elasticsearch
Poitou charentes JUG - ElasticsearchPoitou charentes JUG - Elasticsearch
Poitou charentes JUG - Elasticsearch
David Pilato
 
Affichage d'un document Office sous Android
Affichage d'un document Office sous AndroidAffichage d'un document Office sous Android
Affichage d'un document Office sous Android
Stéphane Liétard
 
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
Bruno Bonnin
 
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
Sébastien Prunier
 
Modélisation de données pour MongoDB
Modélisation de données pour MongoDBModélisation de données pour MongoDB
Modélisation de données pour MongoDB
MongoDB
 
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
De 20 000 à 4 millions d'utilisateurs : mode d'emploiDe 20 000 à 4 millions d'utilisateurs : mode d'emploi
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
Khanh Maudoux
 
MongoDB : la base NoSQL qui réinvente la gestion de données
MongoDB : la base NoSQL qui réinvente la gestion de donnéesMongoDB : la base NoSQL qui réinvente la gestion de données
MongoDB : la base NoSQL qui réinvente la gestion de données
SOAT
 
Normandy JUG - Elasticsearch
Normandy JUG - ElasticsearchNormandy JUG - Elasticsearch
Normandy JUG - Elasticsearch
David Pilato
 
Infrastructure as code drupal
Infrastructure as code drupalInfrastructure as code drupal
Infrastructure as code drupal
Christophe Villeneuve
 
Hadoop et son écosystème - v2
Hadoop et son écosystème - v2Hadoop et son écosystème - v2
Hadoop et son écosystème - v2
Khanh Maudoux
 
Construisez votre première application MongoDB
Construisez votre première application MongoDBConstruisez votre première application MongoDB
Construisez votre première application MongoDB
MongoDB
 
Présentation de ElasticSearch / Digital apéro du 12/11/2014
Présentation de ElasticSearch / Digital apéro du 12/11/2014Présentation de ElasticSearch / Digital apéro du 12/11/2014
Présentation de ElasticSearch / Digital apéro du 12/11/2014
Silicon Comté
 
Atelier : Développement rapide d&rsquo;une application basée surXWiki
Atelier : Développement rapide d&rsquo;une application basée surXWikiAtelier : Développement rapide d&rsquo;une application basée surXWiki
Atelier : Développement rapide d&rsquo;une application basée surXWiki
Korteby Farouk
 
Finist JUG - Elasticsearch
Finist JUG - ElasticsearchFinist JUG - Elasticsearch
Finist JUG - Elasticsearch
David Pilato
 

Tendances (20)

Paris data geek - Elasticsearch
Paris data geek - ElasticsearchParis data geek - Elasticsearch
Paris data geek - Elasticsearch
 
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
Des mises à jour? Emmenez votre application Stitch encore plus loin grâce aux...
 
Mongo DB
Mongo DBMongo DB
Mongo DB
 
L'expérience client au centre de la donnée @AirFrance
L'expérience client au centre de la donnée @AirFranceL'expérience client au centre de la donnée @AirFrance
L'expérience client au centre de la donnée @AirFrance
 
introduction à MongoDB
introduction à MongoDBintroduction à MongoDB
introduction à MongoDB
 
Elasticsearch - Montpellier JUG
Elasticsearch - Montpellier JUGElasticsearch - Montpellier JUG
Elasticsearch - Montpellier JUG
 
Poitou charentes JUG - Elasticsearch
Poitou charentes JUG - ElasticsearchPoitou charentes JUG - Elasticsearch
Poitou charentes JUG - Elasticsearch
 
Affichage d'un document Office sous Android
Affichage d'un document Office sous AndroidAffichage d'un document Office sous Android
Affichage d'un document Office sous Android
 
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
Breizhcamp 2015 - Comment (ne pas réussir à) modéliser ses data dans elastics...
 
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
[Breizhcamp 2015] MongoDB et Elastic, meilleurs ennemis ?
 
Modélisation de données pour MongoDB
Modélisation de données pour MongoDBModélisation de données pour MongoDB
Modélisation de données pour MongoDB
 
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
De 20 000 à 4 millions d'utilisateurs : mode d'emploiDe 20 000 à 4 millions d'utilisateurs : mode d'emploi
De 20 000 à 4 millions d'utilisateurs : mode d'emploi
 
MongoDB : la base NoSQL qui réinvente la gestion de données
MongoDB : la base NoSQL qui réinvente la gestion de donnéesMongoDB : la base NoSQL qui réinvente la gestion de données
MongoDB : la base NoSQL qui réinvente la gestion de données
 
Normandy JUG - Elasticsearch
Normandy JUG - ElasticsearchNormandy JUG - Elasticsearch
Normandy JUG - Elasticsearch
 
Infrastructure as code drupal
Infrastructure as code drupalInfrastructure as code drupal
Infrastructure as code drupal
 
Hadoop et son écosystème - v2
Hadoop et son écosystème - v2Hadoop et son écosystème - v2
Hadoop et son écosystème - v2
 
Construisez votre première application MongoDB
Construisez votre première application MongoDBConstruisez votre première application MongoDB
Construisez votre première application MongoDB
 
Présentation de ElasticSearch / Digital apéro du 12/11/2014
Présentation de ElasticSearch / Digital apéro du 12/11/2014Présentation de ElasticSearch / Digital apéro du 12/11/2014
Présentation de ElasticSearch / Digital apéro du 12/11/2014
 
Atelier : Développement rapide d&rsquo;une application basée surXWiki
Atelier : Développement rapide d&rsquo;une application basée surXWikiAtelier : Développement rapide d&rsquo;une application basée surXWiki
Atelier : Développement rapide d&rsquo;une application basée surXWiki
 
Finist JUG - Elasticsearch
Finist JUG - ElasticsearchFinist JUG - Elasticsearch
Finist JUG - Elasticsearch
 

Similaire à Devoxx: Tribulation d'un développeur sur le Cloud

Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilitéNouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Julien Dubois
 
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et MobileNouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Ippon
 
Stage de fin d’études – dotcloud
Stage de fin d’études – dotcloudStage de fin d’études – dotcloud
Stage de fin d’études – dotcloud
Joffrey Fu Hrer
 
Stage de fin d’études – dotcloud
Stage de fin d’études – dotcloudStage de fin d’études – dotcloud
Stage de fin d’études – dotcloud
Joffrey Fu Hrer
 
Construire un data lake managé - GDG Paris - Juin 2019
Construire un data lake managé - GDG Paris - Juin 2019Construire un data lake managé - GDG Paris - Juin 2019
Construire un data lake managé - GDG Paris - Juin 2019
Jean-Baptiste Claramonte
 
Azure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmediaAzure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmedia
Microsoft
 
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017) Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
univalence
 
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp012014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
MongoDB
 
Introduction aux RIA (Rich Internet Applications)
Introduction aux RIA (Rich Internet Applications)Introduction aux RIA (Rich Internet Applications)
Introduction aux RIA (Rich Internet Applications)
Tugdual Grall
 
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
sebastienmoreno
 
Cartographie - cas concrets et bonnes pratiques de développement
Cartographie - cas concrets et bonnes pratiques de développementCartographie - cas concrets et bonnes pratiques de développement
Cartographie - cas concrets et bonnes pratiques de développement
Nicolas Boonaert
 
HTML5 en projet
HTML5 en projetHTML5 en projet
HTML5 en projet
Normandy JUG
 
Presentation mug-data mapper
Presentation mug-data mapperPresentation mug-data mapper
Presentation mug-data mapper
FastConnect
 
Découvrez les nouvelles fonctionnalités de Talend 6
Découvrez les nouvelles fonctionnalités de Talend 6Découvrez les nouvelles fonctionnalités de Talend 6
Découvrez les nouvelles fonctionnalités de Talend 6
Jean-Michel Franco
 
Gaib19 ai intudstrialisation - azure machine learning services
Gaib19   ai intudstrialisation - azure machine learning servicesGaib19   ai intudstrialisation - azure machine learning services
Gaib19 ai intudstrialisation - azure machine learning services
Fabien Adato
 
Introduction à CakePHP
Introduction à CakePHPIntroduction à CakePHP
Introduction à CakePHP
Pierre MARTIN
 
Paris RailsCamp 2009
Paris RailsCamp 2009Paris RailsCamp 2009
Paris RailsCamp 2009
Olivier Gutknecht
 
Microsoft experiences azure et asp.net core
Microsoft experiences   azure et asp.net coreMicrosoft experiences   azure et asp.net core
Microsoft experiences azure et asp.net core
Sébastien Ollivier
 
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
Digital GraphTour Paris - Neo4j 4.0, les nouveautésDigital GraphTour Paris - Neo4j 4.0, les nouveautés
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
Neo4j
 
Web sémantique et Web de données, et si on passait à la pratique ?
Web sémantique et Web de données, et si on passait à la pratique ?Web sémantique et Web de données, et si on passait à la pratique ?
Web sémantique et Web de données, et si on passait à la pratique ?
Antidot
 

Similaire à Devoxx: Tribulation d'un développeur sur le Cloud (20)

Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilitéNouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et mobilité
 
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et MobileNouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
Nouveau look pour une nouvelle vie : HTML5, Spring, NoSQL et Mobile
 
Stage de fin d’études – dotcloud
Stage de fin d’études – dotcloudStage de fin d’études – dotcloud
Stage de fin d’études – dotcloud
 
Stage de fin d’études – dotcloud
Stage de fin d’études – dotcloudStage de fin d’études – dotcloud
Stage de fin d’études – dotcloud
 
Construire un data lake managé - GDG Paris - Juin 2019
Construire un data lake managé - GDG Paris - Juin 2019Construire un data lake managé - GDG Paris - Juin 2019
Construire un data lake managé - GDG Paris - Juin 2019
 
Azure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmediaAzure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmedia
 
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017) Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
Spark-adabra, Comment Construire un DATALAKE ! (Devoxx 2017)
 
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp012014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
2014 03-26-appdevseries-session3-interactingwiththedatabase-fr-phpapp01
 
Introduction aux RIA (Rich Internet Applications)
Introduction aux RIA (Rich Internet Applications)Introduction aux RIA (Rich Internet Applications)
Introduction aux RIA (Rich Internet Applications)
 
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
Lost in serverless AWS Lambda, Google Cloud Function, Azure Function quelle s...
 
Cartographie - cas concrets et bonnes pratiques de développement
Cartographie - cas concrets et bonnes pratiques de développementCartographie - cas concrets et bonnes pratiques de développement
Cartographie - cas concrets et bonnes pratiques de développement
 
HTML5 en projet
HTML5 en projetHTML5 en projet
HTML5 en projet
 
Presentation mug-data mapper
Presentation mug-data mapperPresentation mug-data mapper
Presentation mug-data mapper
 
Découvrez les nouvelles fonctionnalités de Talend 6
Découvrez les nouvelles fonctionnalités de Talend 6Découvrez les nouvelles fonctionnalités de Talend 6
Découvrez les nouvelles fonctionnalités de Talend 6
 
Gaib19 ai intudstrialisation - azure machine learning services
Gaib19   ai intudstrialisation - azure machine learning servicesGaib19   ai intudstrialisation - azure machine learning services
Gaib19 ai intudstrialisation - azure machine learning services
 
Introduction à CakePHP
Introduction à CakePHPIntroduction à CakePHP
Introduction à CakePHP
 
Paris RailsCamp 2009
Paris RailsCamp 2009Paris RailsCamp 2009
Paris RailsCamp 2009
 
Microsoft experiences azure et asp.net core
Microsoft experiences   azure et asp.net coreMicrosoft experiences   azure et asp.net core
Microsoft experiences azure et asp.net core
 
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
Digital GraphTour Paris - Neo4j 4.0, les nouveautésDigital GraphTour Paris - Neo4j 4.0, les nouveautés
Digital GraphTour Paris - Neo4j 4.0, les nouveautés
 
Web sémantique et Web de données, et si on passait à la pratique ?
Web sémantique et Web de données, et si on passait à la pratique ?Web sémantique et Web de données, et si on passait à la pratique ?
Web sémantique et Web de données, et si on passait à la pratique ?
 

Plus de Tugdual Grall

Introduction to Streaming with Apache Flink
Introduction to Streaming with Apache FlinkIntroduction to Streaming with Apache Flink
Introduction to Streaming with Apache Flink
Tugdual Grall
 
Introduction to Streaming with Apache Flink
Introduction to Streaming with Apache FlinkIntroduction to Streaming with Apache Flink
Introduction to Streaming with Apache Flink
Tugdual Grall
 
Fast Cars, Big Data - How Streaming Can Help Formula 1
Fast Cars, Big Data - How Streaming Can Help Formula 1Fast Cars, Big Data - How Streaming Can Help Formula 1
Fast Cars, Big Data - How Streaming Can Help Formula 1
Tugdual Grall
 
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Tugdual Grall
 
Big Data Journey
Big Data JourneyBig Data Journey
Big Data Journey
Tugdual Grall
 
Proud to be Polyglot - Riviera Dev 2015
Proud to be Polyglot - Riviera Dev 2015Proud to be Polyglot - Riviera Dev 2015
Proud to be Polyglot - Riviera Dev 2015
Tugdual Grall
 
Introduction to NoSQL with MongoDB - SQLi Workshop
Introduction to NoSQL with MongoDB - SQLi WorkshopIntroduction to NoSQL with MongoDB - SQLi Workshop
Introduction to NoSQL with MongoDB - SQLi Workshop
Tugdual Grall
 
Enabling Telco to Build and Run Modern Applications
Enabling Telco to Build and Run Modern Applications Enabling Telco to Build and Run Modern Applications
Enabling Telco to Build and Run Modern Applications
Tugdual Grall
 
MongoDB and Hadoop
MongoDB and HadoopMongoDB and Hadoop
MongoDB and Hadoop
Tugdual Grall
 
Proud to be polyglot
Proud to be polyglotProud to be polyglot
Proud to be polyglot
Tugdual Grall
 
Drop your table ! MongoDB Schema Design
Drop your table ! MongoDB Schema DesignDrop your table ! MongoDB Schema Design
Drop your table ! MongoDB Schema Design
Tugdual Grall
 
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Tugdual Grall
 
Some cool features of MongoDB
Some cool features of MongoDBSome cool features of MongoDB
Some cool features of MongoDB
Tugdual Grall
 
Building Your First MongoDB Application
Building Your First MongoDB ApplicationBuilding Your First MongoDB Application
Building Your First MongoDB Application
Tugdual Grall
 
Opensourceday 2014-iot
Opensourceday 2014-iotOpensourceday 2014-iot
Opensourceday 2014-iot
Tugdual Grall
 
Neotys conference
Neotys conferenceNeotys conference
Neotys conference
Tugdual Grall
 
Softshake 2013: Introduction to NoSQL with Couchbase
Softshake 2013: Introduction to NoSQL with CouchbaseSoftshake 2013: Introduction to NoSQL with Couchbase
Softshake 2013: Introduction to NoSQL with Couchbase
Tugdual Grall
 
Introduction to NoSQL with Couchbase
Introduction to NoSQL with CouchbaseIntroduction to NoSQL with Couchbase
Introduction to NoSQL with Couchbase
Tugdual Grall
 
Why and How to integrate Hadoop and NoSQL?
Why and How to integrate Hadoop and NoSQL?Why and How to integrate Hadoop and NoSQL?
Why and How to integrate Hadoop and NoSQL?
Tugdual Grall
 
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
Tugdual Grall
 

Plus de Tugdual Grall (20)

Introduction to Streaming with Apache Flink
Introduction to Streaming with Apache FlinkIntroduction to Streaming with Apache Flink
Introduction to Streaming with Apache Flink
 
Introduction to Streaming with Apache Flink
Introduction to Streaming with Apache FlinkIntroduction to Streaming with Apache Flink
Introduction to Streaming with Apache Flink
 
Fast Cars, Big Data - How Streaming Can Help Formula 1
Fast Cars, Big Data - How Streaming Can Help Formula 1Fast Cars, Big Data - How Streaming Can Help Formula 1
Fast Cars, Big Data - How Streaming Can Help Formula 1
 
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
Lambda Architecture: The Best Way to Build Scalable and Reliable Applications!
 
Big Data Journey
Big Data JourneyBig Data Journey
Big Data Journey
 
Proud to be Polyglot - Riviera Dev 2015
Proud to be Polyglot - Riviera Dev 2015Proud to be Polyglot - Riviera Dev 2015
Proud to be Polyglot - Riviera Dev 2015
 
Introduction to NoSQL with MongoDB - SQLi Workshop
Introduction to NoSQL with MongoDB - SQLi WorkshopIntroduction to NoSQL with MongoDB - SQLi Workshop
Introduction to NoSQL with MongoDB - SQLi Workshop
 
Enabling Telco to Build and Run Modern Applications
Enabling Telco to Build and Run Modern Applications Enabling Telco to Build and Run Modern Applications
Enabling Telco to Build and Run Modern Applications
 
MongoDB and Hadoop
MongoDB and HadoopMongoDB and Hadoop
MongoDB and Hadoop
 
Proud to be polyglot
Proud to be polyglotProud to be polyglot
Proud to be polyglot
 
Drop your table ! MongoDB Schema Design
Drop your table ! MongoDB Schema DesignDrop your table ! MongoDB Schema Design
Drop your table ! MongoDB Schema Design
 
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
Devoxx 2014 : Atelier MongoDB - Decouverte de MongoDB 2.6
 
Some cool features of MongoDB
Some cool features of MongoDBSome cool features of MongoDB
Some cool features of MongoDB
 
Building Your First MongoDB Application
Building Your First MongoDB ApplicationBuilding Your First MongoDB Application
Building Your First MongoDB Application
 
Opensourceday 2014-iot
Opensourceday 2014-iotOpensourceday 2014-iot
Opensourceday 2014-iot
 
Neotys conference
Neotys conferenceNeotys conference
Neotys conference
 
Softshake 2013: Introduction to NoSQL with Couchbase
Softshake 2013: Introduction to NoSQL with CouchbaseSoftshake 2013: Introduction to NoSQL with Couchbase
Softshake 2013: Introduction to NoSQL with Couchbase
 
Introduction to NoSQL with Couchbase
Introduction to NoSQL with CouchbaseIntroduction to NoSQL with Couchbase
Introduction to NoSQL with Couchbase
 
Why and How to integrate Hadoop and NoSQL?
Why and How to integrate Hadoop and NoSQL?Why and How to integrate Hadoop and NoSQL?
Why and How to integrate Hadoop and NoSQL?
 
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
NoSQL Matters 2013 - Introduction to Map Reduce with Couchbase 2.0
 

Devoxx: Tribulation d'un développeur sur le Cloud

  • 1. Tribulations d'un développeur Java dans le Cloud Tugdual Grall @tgrall 1
  • 2. Ce que vous allez apprendre • Retour d’experience d’un developpeur du “Dimanche” • Comprendre “mes” choix • Difficultés Surprises du Cloud 2
  • 3. About me : “Tug” • CTO chez eXo depuis 2008 • Développeur, Product Manager chez Oracle • Co fondateur du NantesJUG • @tgrall sur Twitter & RunKeeper • Triathlete • Développeur de resultri.com 3
  • 4. Agenda • Le Cloud, Pourquoi ? Comment ? • Les surprises Bonnes et Mauvaises • Et Alors ? 4
  • 5. Le cloud, pourquoi? • Besoins fonctionnels : • Analyser les résultats de courses :Triathlons ou autres • Partager ces résultats simplement : réseaux sociaux • Besoins techniques : • Apprendre à développer pour le cloud • Pas de gestion système, ressources disponibles 5
  • 6. 6
  • 7. Choisir une plateforme • Aout 2011, avant mon départ en vacances • Support du mode déconnecté • Java & NoSQL • Documentée et “Connue” • Gratuite • Disponible sur Mac OS X sans “hack” 7
  • 8. Choisir une plateforme • Aout 2011, avant mon départ en vacances • Support du mode déconnecté • Java & NoSQL • Documentée et “Connue” • Gratuite • Disponible sur Mac OS X sans “hack” 7
  • 9. Google AppEngine • Google PaaS • Languages : Java, Python and Go • Base de données : BigTable (NoSQL) & CloudSQL (MySQL) • Accès simplifié aux API et Services Google • Google Cloud Storage, Analytics, Google Apps, Maps... 8
  • 10. Resultri en quelques mots • Pour les Geeks • Pour les Sportifs • Pure Servlet/JSP • 33 courses • REST avec JAX-RS (Jersey) • 46 754 résultats individuels • Twitter Boostrap • 3500 visiteurs uniques • 11 300 pages vues 9
  • 12. Accès aux données? • Ma premiere réelle experience du monde NoSQL • Mon passé (passif?) : 9 années d’Oracle, ... très SQL (JPA,Toplink, BC4J !) • API : JPA, JDO, Datastore API ou autre (Objectify) ? 11
  • 13. Accès aux données? • Mon choix : JDO & Datastore API • Limité aux API Google (documenté dans le SDK) • JDO : car la doc était la plus complète • Datastore : est venu ensuite pour la simplicité 12
  • 14. Resultri: Les Données Sites “résultats” HTML, TXT, CSV, XML 13
  • 15. Resultri: Les Données Sites “résultats” Traitements en local HTML, TXT, CSV, XML Utilisation du GAE Dev Server Export CSV 13
  • 16. Resultri: Les Données Sites “résultats” Traitements en local Import GAE HTML, TXT, CSV, XML Utilisation du GAE Dev Server Utilisation des outils GAE Export CSV Import CSV 13
  • 17. Dev to “Cloud” 1/2 1: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); • 2: ... Une fois les données 3: Map<String, Object> propertiesMap = result.getProperties(); 4: properties = (String[]) propertiesMap.keySet()...; 5: ... 6: 7: Query q = new Query("TriathlonResult"); q.addFilter("raceURI", Query.FilterOperator.EQUAL,race); importées en local 8: PreparedQuery pq = datastore.prepare(q); • 9: for (Entity result : pq.asIterable()) { 10: 11: 12: ! for (int i = 0; i < properties.length; i++) { Object o = result.getProperty(properties[i]); Création d’un Fichier CSV 13: String printValue = null; • 14: if (o != null) { 15: 16: ! ! if (o instanceof java.util.Date) { ! SimpleDateFormat formatter = new SimpleDateFormat("yyy ... H:mm:ss") ; Utilisation des API Datastore 17: ! ! Date date = (Date) o; • 18: ! ! printValue = formatter.format(date); 19: 20: ! } } else if ... { ...!} Trop “couteux” à faire sur le Cloud 21: out.print((printValue == null)?"":printValue); 22: out.print(","); 23: ! } 24: ... 14
  • 18. Dev to “Cloud” 2/2 appcfg.py upload_data --config_file=config.yml --filename=ironman-south-africa.csv --url=http://dev-result-db.appspot.com/remote_api --application=dev-result-db --kind=TriathlonResult Uploading data records. [INFO ] Logging to bulkloader-log-20120416.155812 [INFO ] Throttling transfers: • Création des entités par CLI [INFO ] Bandwidth: 250000 bytes/second [INFO ] HTTP connections: 8/second [INFO ] Entities inserted/fetched/modified: 20/second [INFO ] Batch Size: 10 • Très rapide • [INFO ] Opening database: bulkloader-progress-20120416.155812.sql3 [INFO ] Connecting to dev-result-db.appspot.com/remote_api Please enter login credentials for dev-result-db.appspot.com Import des données “formatées” Email: tugdual@gmail.com Password for tugdual@gmail.com: [INFO ] Starting import; maximum 10 entities per post ................................................................. [INFO ] 1552 entities total, 0 previously transferred [INFO ] 1552 entities (7016782 bytes) transferred in 60.4 seconds [INFO ] All entities successfully transferre 15
  • 19. Recherche Full Text • Beta privée en Octobre... • Solution “Resultri” • Utilisation de Cloud SQL 16
  • 20. Recherche Full Text • Beta privée en Octobre... • Solution “Resultri” • Utilisation de Cloud SQL First & Last Names BigTable CloudSQL 16
  • 21. Accès aux données • Attention aux quotas ! 17
  • 22. Accès aux données • Utilisation de Memcache • Cache disponible dans GAE • Gestion du cache par le biais de l’API JCache (JSR-107) ou API Google • Dans Resultri: • Tout Entity est automatiquement cachée 18
  • 23. Memcache 19
  • 25. Création de données 1: log.info(" -- Start -- "); 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 3: Entity employee = new Entity("Employee"); 4: employee.setProperty("firstName", "Nicolas"); 5: employee.setProperty("lastName", "Martignole"); 6: Date hireDate = new Date(); 7: employee.setProperty("hireDate", hireDate); 8: employee.setProperty("attendedHrTraining", true); 9: datastore.put(employee); 10: employee = new Entity("Employee"); 11: employee.setProperty("firstName", "Antonio"); 12: employee.setProperty("lastName", "Goncalvez"); 13: hireDate = new Date(); 14: employee.setProperty("hireDate", hireDate); 15: employee.setProperty("attendedHrTraining", true); 16: datastore.put(employee); 17: Query q = new Query("Employee"); 18: PreparedQuery pq = datastore.prepare(q); 19: for (Entity emp : pq.asIterable()) { 20: ! log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" ); 21: } 22: log.info(" -- End -- "); 21
  • 26. Création de données 1: log.info(" -- Start -- "); 2: DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 3: Entity employee = new Entity("Employee"); 4: employee.setProperty("firstName", "Nicolas"); 5: employee.setProperty("lastName", "Martignole"); 6: Date hireDate = new Date(); 1: Apr 16, 2012 4:22:25 "hireDate", hireDate); 7: employee.setProperty( PM com....Servlet doGet 2: INFO: -- setProperty("attendedHrTraining", true); 8: employee. Start -- 3: Apr 16, 2012 4:22:25 PM com....Servlet doGet 9: datastore.put(employee); 4: INFO: Nicolas Martignole 10: employee = new Entity("Employee"); 5: Apr 16, 2012 4:22:25 "firstName", "Antonio"); 11: employee.setProperty( PM com....Servlet doGet 6: INFO: -- setProperty("lastName", "Goncalvez"); 12: employee. End -- 7: Apr 16, 2012 4:Date(); com.......$PersistDatastore persist 13: hireDate = new 22:34 PM 8: INFO: Time to persist"hireDate",: hireDate); 14: employee.setProperty( datastore 4 ms 15: employee.setProperty("attendedHrTraining", true); 16: datastore.put(employee); 17: Query q = new Query("Employee"); 18: PreparedQuery pq = datastore.prepare(q); 20: ! Mais où est parti Antonio???? 19: for (Entity emp : pq.asIterable()) { log.info(emp.getProperty("firstName") + " "+ emp.getProperty("lastName" ); 21: } 22: log.info(" -- End -- "); 21
  • 27. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addSort("lastName"); 3: q.addSort("firstName"); 4: PreparedQuery pq = datastore.prepare(q); 5: for (Entity emp : pq.asIterable()) { 6: o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") ); 7: } 22
  • 28. Datastore : Queries 1: Doe John 2: Goncalvez Antonio 1: Query q = new Query("Employee"); 3: Grall Briac 2: q.addSort("lastName"); 4: Grall Corentin 3: q.addSort("firstName"); 5: Grall Malo 4: PreparedQuery pq = datastore.prepare(q); 6: Grall Nolwenn 5: for (Entity emp : pq.asIterable()) { 7: Grall Tug 8: Grall Virginie 6: o.println(emp.getProperty("lastName") +" "+ emp.getProperty("firstName") ); 9: Martignole Nicolas 7: } 10: Tabarly Eric 22
  • 29. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addSort("lastName"); 4: q.addSort("firstName"); 5: PreparedQuery pq = datastore.prepare(q); 6: for (Entity emp : pq.asIterable()) { 7: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 8: } 23
  • 30. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 1: Grall Briac 3: q.addSort("lastName"); 2: Grall Corentin 4: q.addSort("firstName"); 3: Grall Malo 5: PreparedQuery pq = datastore.prepare(q); 4: Grall Nolwenn 6: for (Entity emp : pq.asIterable()) { 5: Grall Tug 6: Grall Virginie 7: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 8: } 23
  • 31. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); 5: q.addSort("firstName"); 6: PreparedQuery pq = datastore.prepare(q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 32. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); ? 5: q.addSort("firstName"); 6: PreparedQuery pq = datastore.prepare(q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 33. Datastore : Queries 1: Query q = new Query("Employee"); 2: q.addFilter("lastName", FilterOperator.EQUAL , "Grall"); 3: q.addFilter("birthYear", FilterOperator.LESS_THAN_OR_EQUAL , 2000); 4: q.addSort("lastName"); java.lang.IllegalArgumentException: ? 5: q.addSort("firstName"); The first sort property must be the same as the property to which the inequality filter is applied. 6: PreparedQuery pq = datastore.prepareproperty is firstName but the inequality filter is on birthYear In your query the first sort (q); 7: for (Entity emp : pq.asIterable()) { 8: o.println(emp.getProperty("lastName") + " "+ emp.getProperty("firstName") ); 9: } 24
  • 34. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 4: o.println(results); 25
  • 35. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'&& firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 1: Grall Malo 4: o.println(results); 25
  • 36. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); 3: List<Employee> results = (List<Employee>) q.execute(); ! 4: o.println(results); 26
  • 37. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); 2: Query q = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); 3: 4: List<Employee> results = (List<Employee>) q.execute(); ! o.println(results); ? 26
  • 38. Datastore : Queries 1: PersistenceManager pm = PMF.get().getPersistenceManager(); org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException: 2: Problem = pm.newQuery(Employee.class,"lastName=='Grall'|| firstName=='Malo'"); Query q with query <SELECT FROM com.grallandco.model.Employee WHERE lastName=='Grall' || firstName=='Malo'>: 3: Or filters cannot be applied<Employee>) q.execute(); ! (found both lastName and firstName). List<Employee> results = (List to multiple properties 4: o.println(results); ? 26
  • 39. Manipulation des données • Les données ne sont pas toujours disponibles • High Data Replication (HDR) : surprenant pendant les tests • “Limitations” des requêtes (GQL) et JDO • Attentions aux index : sans index pas de “requetes” • Verifier la disponibilité des index • Utilisation de Memcache (must have! ) 27
  • 41. Task Queues • Service pour l’execution de taches en arrière plan • Possibilité d’inclure les taches dans un ordonnanceur (cron) • Resultri: • Synchronization (BigTable/CloudSQL) , Nettoyage des données 1: Queue queue = QueueFactory.getQueue("SynchappAppQueue"); 2: TaskOptions taskOptions = TaskOptions.Builder.withUrl("/worker/ProcessSynchronizeDataServlet") 3: .param("race-uri", "ironman-florida-2005") 4: .param("type", "syncDB") 5: .method(TaskOptions.Method.POST); 6: queue.add(taskOptions); 29
  • 42. Test en Local • Un version de Jetty avec le SDK Google AppEngine Simulation “Authentification Google” Console Developpeur 30
  • 44. Authentification Google • Utilisation de Google Account • Se base sur la sécurité JavaEE standard • Gestion des administrateurs par le biais de la console AppEngine <security-constraint> <web-resource-collection> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> 32
  • 45. URL Fetch • Accès à des serveurs autres que AppEngine par HTTP/HTTPS • Utilisation de java.net.URL ou com.google.appengine.api.urlfetch.URLFetchService • Dans Resultri : utilisation du service de GeoCodage de Google Maps 1: URL url = new URL("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address="+ address ); 33
  • 46. Accès aux API Google • Google offre de nombreux Services et API • Un seul compte => tous les services 34
  • 47. Resultri & Google • Les services que j’utilise pour Resultri • Google Apps : Gestion du nom de domaines, Mail, ... • CloudSQL : Recherche Full Text (MySQL) • Cloud Storage : Sauvegarde BigTable, Serveur de fichiers • Google Maps • Analytics, Adsense, Webmaster 35
  • 49. Gratuit ? • Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
  • 50. Gratuit ? • Oui mais.... • Avec des quotas • Les règles peuvent changer • Septembre : Google modifie ses tarifs 37
  • 51. Exemple 38
  • 53. Gestion du cout • En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation 39
  • 54. Gestion du cout • En Fevrier : Augmentation du cout • Pas d’activité utilisateur, mais cout en augmentation • Une idée ? 39
  • 55. Merci Google! • Google Bots, et autres moteurs de recherche • Utilisation de Google Webmaster Tools • Mise en place d’un fichier robots.txt 40
  • 56. Merci Google! • Google Bots, et autres moteurs de recherche User-agent: Twiceler • Disallow: / Utilisation de Google Webmaster Tools User-agent: psbot Disallow: / • User-agent: * Mise en place d’un fichier robots.txt Disallow: /search Disallow: /races/*/compare Disallow: /rest Disallow: /races/*/*/position Disallow: /races/*/*/rank Disallow: /races/*/*/otherRaces Disallow: /races/*?page=-* 40
  • 57. Merci Google! • Google Bots, et autres moteurs de recherche User-agent: Twiceler • Disallow: / Utilisation de Google Webmaster Tools User-agent: psbot Disallow: / • User-agent: * Mise en place d’un fichier robots.txt Disallow: /search Disallow: /races/*/compare Disallow: /rest • Disallow: /races/*/*/position 243 784 URL bloquées sur Resultri Disallow: /races/*/*/rank Disallow: /races/*/*/otherRaces Disallow: /races/*?page=-* 40
  • 58. Console 41
  • 59. Console 41
  • 60. Console 41
  • 62. Conclusion • AppEngine est-il le bon choix pour Resultri? • Pour l’instant trop couteux et “puissant” • Techniquement passionnant • Feuille de route • Gestion des utilisateurs : Facebook, Twitter, Google • Notifications • Version Mobile 43
  • 63. Conclusion • Techniquement : j’apprends beaucoup.... • Publication d’articles sur mon blog • NoSQL, CloudSQL, Memcache.... • Twitter Boostrap 2 and Google Maps • Google AppEngine Full Text Search with Cloud • Utilisation de Git SQL • Installing Memcached on Mac OS X and using it in • Twitter Bootstrap, Less Java • JAX-RS: Jersey and JSON single element arrays • Create and Deploy a JAX-RS REST service on Google App Engine • .... 44
  • 65. Tribulations d'un développeur Java dans le Cloud Tugdual Grall @tgrall 46

Notes de l'éditeur

  1. \n
  2. Bas&amp;#xE9; sur MON retour d&amp;#x2019;experience: je ne suis pas un specialiste de GAE !\nJuste un developpement pour le fun\nJe passe tres peu de temps sur cette appli (de moins en moins...)\n\nExplication de mes choix et des &amp;#x201C;problemes&amp;#x201D; rencontr&amp;#xE9;s\n
  3. Chez eXo, Oracle... developpement Java, JavaEE, SOA, ...\nCo Fondateur en 2008 du NantesJUG\nTwitter.... et comme nous allons parler sport: runkeeper\n\nQd je ne te travaille pas, ou je ne m&amp;#x2019;occupe pas de mes enfants...\n - je m;entraine ou coure.... et quelque fois je code\n - notamment dans le train (2h en gros)\n
  4. Voici la structure de la presentation...\n- mes choix et besoins\n- les bonnes surprises, decouvertes (et les moins bonnes) du cloud (GAE)\n- conclusion,,,\n
  5. L&amp;#x2019;id&amp;#xE9;e de ce site est parti de 2 besoins:\n 1- besoin &amp;#x201C;fonctionnel&amp;#x201D; analyser les resultats, suivre les copains et mes evolutions\n 2- besoin &amp;#x201C;technique&amp;#x201D; : une application plus pouss&amp;#xE9;e qu&amp;#x2019;un simple helloworld pour plonger un peu plus dans le cloud (ou sauter)\n
  6. \nPetite demo...\n\nVoici quelques ecrans de mon application.\nJe ne vais pas rentrer dans les details fonctionnels pour le moment....\n\n
  7. Voici mes &amp;#x201C;contraintes techniques&amp;#x201D;.\nLe point le plus important :\n - un projet perso developp&amp;#xE9; en vacances.\n - Commence dans un club de vacances en italie : Pizza &amp; Java\n\nBase sur les crit&amp;#xE8;res precedents j&amp;#x2019;ai choisi GAE.\n- Heroku, RH OpenShift ne supportaient pas encore Java\n- MicroCloud pas encore lanc&amp;#xE9;\n- EC2 trop cher/complique (au sens systeme, je veux une PaaS et non pas IaaS)\n\n\n
  8. GAE repond bien a tous ces criteres &amp;#x201C;techniques&amp;#x201D;\nLes autres services Google m&amp;#x2019;apparaissaient egalement tres interessant a integrer...G+ aussi :)\n
  9. Pourquoi Pure JSP/Servlet ? \n - pourquoi pas ? manque de competence sur les nouveaux fwk de ma part (JSF, Struts, OK,, mais quel int&amp;#xE9;r&amp;#xEA;t ?)\n - je ne voulais pas partir sur une solution Grails, Gaelyk, Play!, car je veux vraiment me concentrer sur GAE lui meme.\n\nLa migraton twitter 1.x - 2... un peu lourde non ?\n
  10. \n
  11. Le choix de la technologie &amp;#x201C;web&amp;#x201D; etait simple pour moi: JSP/JSTL/Servlet/TwitterBS... avec JQuery et autres extensions.\nRestait donc le choix de la persistence.... en utilisant le NoSQL (qui etait en aout la seule base de don&amp;#xE9;e disponible)\n\ndonc....\n
  12. Je me suis orient&amp;#xE9; sur JDO et ... puis datastore.\nIl est clair que comme tout le reste de l&amp;#x2019;application les choix sont discutables... mais pour moi la seule partie vraiment importante de mon application est: la base de donn&amp;#xE9;e. (les donn&amp;#xE9;es &amp;#x201C;traitees&amp;#x201D;)\nOn peut considerer le reste comme jetable, si par la suite il faut que je refactorise certaine parties no pb...\nMon modele object est tres simple, seule le &amp;#x201C;volume&amp;#x201D; entre guillement pourrait etre important dans le future.\n\nDonc JDO: pour la doc, et le cote ORM (voir suite) et Datastore pour la simplicit&amp;#xE9;/flexibilit&amp;#xE9;\n
  13. Les donn&amp;#xE9;es sont captur&amp;#xE9;es sur des sites publics\n\n+ Import&amp;#xE9;es/transform&amp;#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&amp;#xE9; dans l&amp;#x2019;instance GAE avec l&amp;#x2019;outil suivant\n\n
  14. Les donn&amp;#xE9;es sont captur&amp;#xE9;es sur des sites publics\n\n+ Import&amp;#xE9;es/transform&amp;#xE9;s en local sur le serveur de dev (Pure GAE)\n\n+ puis export&amp;#xE9; dans l&amp;#x2019;instance GAE avec l&amp;#x2019;outil suivant\n\n
  15. Utilisation de la Datastore API pour &amp;#x201C;sortir&amp;#x201D; les donn&amp;#xE9;es de la base de dev... et la pousser vers le serveur de prod.\n
  16. Cet outil m&amp;#x2019;est tres utile pour importer les donn&amp;#xE9;es...\nIl est aussi utilisable pour exporter les entites depuis le serveur de prod \nIl est possible de formatter, et transformer les objets par ces scripts (python)\nNotamme export/import: csv, xml, ...\n
  17. Etonnemment, GAE ne support pas encore une recherche full text. \nbesoin simple : Rechercher les athletes par leur nom.\n\nPlusieurs approche possible aujourd&amp;#x2019;hui, elastic search, lucene in GAE : MAIS COMPLEX...\n\nJ ai donc choisi pour m&amp;#x2019;amuser et pour repondre au besoin d&amp;#x2019;utiliser CloudSQL (mySQL dans le cloud)\n
  18. Une fois le systeme en production, avec un gros nombre de data:\n - on voit rapidement le nombre de call aux API monter (meme en read only)...\n - 50 000 requetes en free... getPropety() == 1 call\n
  19. La gestion du cache dans mon application est relativement simple:\n- l&amp;#x2019;eviction est geree automatiquement par memcache (cela dit il est possible de pauser un timer sur le cache)\nAdministration du cache de facon globale dans Resultri:\n - &amp;#x201C;je vide certains caches&amp;#x201D; en fonction de mes operations (par exemple ajout d&amp;#x2019;une course, je vide le cache &amp;#x201C;courses&amp;#x201D; lors de la publication)\n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. je n&amp;#x2019;ai pas encore tester les &amp;#x201C;limites&amp;#x201D;\nMeilleure gestion des ressources\n
  33. Une bonne surprise:\n- support de l&amp;#x2019;authentification Google Account dans le serveur de test (avec support du role admin)\n- Console de dev, pour visualiser/administrer les entit&amp;#xE9;s, queues, ...\n
  34. Une fonctionnalit&amp;#xE9; que je trouve tres interessante:\n - la capacit&amp;#xE9; a tester avec les &amp;#x201C;services cloud&amp;#x201D; no disponible avec plusieurs etats....\n\nJe n&amp;#x2019;ai pas encore vraiment eu besoin d&amp;#x2019;utiliser cette fonctionnalit&amp;#xE9; mais vraiment &amp;#x201C;look nice&amp;#x201D;\n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. Sur cette petite animation vous pouvez voir qu&amp;#x2019;en utilisant l&amp;#x2019;outil APPCFG pour importer mes enregistrement (la moitie d&amp;#x2019;un ironman)\nj&amp;#x2019;explose le quota des Datastore write ...\nPour l&amp;#x2019;anedocte, ce matin j&amp;#x2019;ai fait le menage dans mes entities (suppression des enregistrement &amp;#x201C;session&amp;#x201D;) et cela a exploser mon budget (limit&amp;#xE9; a $1 par jour :( 0.39 front ent / 0.66 en ecriture ... je n&amp;#x2019;ai pas trop verifi&amp;#xE9; la source exacte du probleme )\n
  42. Que se passe-t-il ici?\nsur les images:\n - activite nulle...\n - prix eleve\n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. J&amp;#x2019;ai bcp appris, mais il me reste encore pas mal de chose a tester/faire:\n - optimisation des donn&amp;#xE9;es (volume, indexes, ..)\n - utlisation des services XMPP, mail. &amp;#x201C;pour voir&amp;#x201D;\n - utiliser les &amp;#x201C;versions&amp;#x201D; d&amp;#x2019;applications, et lesoutils de &amp;#x201C;mise a jour des donn&amp;#xE9;es)\n - FullText Search\n - MapReduce\n
  50. \n
  51. \n