Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Libérez la puissance de Couchbase depuis votre application Play 2

Libérez la puissance de Couchbase depuis votre application Play 2

Scala IO 2013

mathieuancelin

October 24, 2013
Tweet

More Decks by mathieuancelin

Other Decks in Programming

Transcript

  1. Mathieu ANCELIN • Ingénieur d’étude @SERLI • Java, Scala &

    OSS • JOnAS, GlassFish, Weld, etc ... • Poitou-Charentes JUG • Membre de l’expert group CDI 1.1 (JSR-346) • Membre de l’expert group OSGi Enterprise • @TrevorReznik
  2. • Société de conseil et d’ingénierie du SI • 65

    personnes • 80% de business Java • Contribution a des projets OSS • 10% de la force de travail sur l’OSS • Membre de l’EG JSR-346 • Membre de l’OSGi Alliance • www.serli.com @SerliFr
  3. • Société de conseil et d’ingénierie du SI • 65

    personnes • 80% de business Java • Contribution a des projets OSS • 10% de la force de travail sur l’OSS • Membre de l’EG JSR-346 • Membre de l’OSGi Alliance • www.serli.com @SerliFr
  4. Couchbase Facilement   scalable Hautes  performances   consistantes   24h/24

    Modifica(on  du  cluster  sans   changements  dans  l’applica(on,  sans   down(me  et  le  tout  en  un  simple  clic Temps  de  réponse  en  lecture  et  écrite  en   dessous  de  la  milliseconde  avec  des  débits   consistant Pas  de  down(me  pour  les  mises  à   jours  logicielles  ou  matérielles,  etc  ... Modèle  de  données   flexible Modèle  de  document  JSON  sans   schéma  fixe JSON JSON JSON JSON JSON PERFORMANCE
  5. Couchbase • Base de donnée NoSQL orientée documents • version

    mobile • Console d’administration sexy • Indexation et requêtage par vues • représentation des données pour le requêtage construite à l’insertion • notion de map/reduce • vues écrites en Javascript
  6. Couchbase • Projet open-source • https://github.com/couchbase/ • https://github.com/couchbaselabs/ • Disponible

    gratuitement • Support par Couchbase • Drivers/clients disponibles pour beaucoup de langages • Java, .NET, PHP, Ruby, Python, Node, C, etc ... http://www.couchbase.com/download
  7. Mais pourquoi ? • Soirée Couchbase au Poitou-Charentes JUG •

    Fan de Play 2, version Scala ;-) • Driver Java pour Couchbase disponible • basé sur Netty, Spymemcached, etc ... • API très complète • très Java-oriented • Mais il y avait un autre problème ... • réglé depuis :-)
  8.  CouchbaseClient  client  =              

                   new  CouchbaseClient(hosts,  bucket,  password);    client.set("my-­‐first-­‐document",  "Hello  Couchbase!").get();    client.get("my-­‐first-­‐document").get(); Java Driver : Future !!!
  9.  CouchbaseClient  client  =              

                   new  CouchbaseClient(hosts,  bucket,  password);    client.set("my-­‐first-­‐document",  "Hello  Couchbase!").get();    client.get("my-­‐first-­‐document").get(); Java Driver : Future !!!
  10. play2-couchbase • Plugin Play 2 • Configuration centralisée • Plusieurs

    buckets configurables • Utilisation massive des mécanismes d’asynchronisme de Play 2 • Futures, Promises, Enumerators • transformation de futures Java en future Scala • contribution au driver Java • Plusieurs plugins pour faciliter la vie du développeur • API Java & Scala adaptée à Play 2 • Accès au driver natif si besoin
  11. Configuration libraryDependencies  ++=  Seq(    cache,    "org.ancelin.play2.couchbase"  %%  "play2-­‐couchbase"

     %  "0.5" ) resolvers  +=  "ancelin"  at  "https://raw.github.com/mathieuancelin/play2-­‐ couchbase/master/repository/releases" resolvers  +=  "Spy  Repository"  at  "http://files.couchbase.com/maven2"
  12. Pour les intrépides ... libraryDependencies  ++=  Seq(    cache,  

     "org.ancelin.play2.couchbase"  %%  "play2-­‐couchbase"  %  "0.6-­‐SNAPSHOT" ) resolvers  +=  "ancelin"  at  "https://raw.github.com/mathieuancelin/play2-­‐ couchbase/master/repository/snapshots" resolvers  +=  "Spy  Repository"  at  "http://files.couchbase.com/maven2"
  13. Les buckets couchbase  {    buckets  =  [{    

       host="127.0.0.1"        port="8091"        base="pools"        bucket="mybucket"        user=""        pass=""        timeout="0"    }] }
  14. Utilisation object  UserController  extends  Controller  with  CouchbaseController  {    implicit

     val  ec  =  Couchbase.couchbaseExecutor    def  getUser(key:  String)  =  CouchbaseAction("mybucket")  {  bucket  =>        bucket.get[JsObject](key).map  {  user  =>            Ok(user.getOrElse(Json.obj("error",  s"User  $key  not  found!"))        }    } }
  15. Utilisation case  class  User(id:  String,  name:  String,  email:  String)  

    object  UserController  extends  Controller  with  CouchbaseController  {      implicit  val  userReader  =  Json.reads[User]    implicit  val  ec  =  Couchbase.couchbaseExecutor      def  getUser(key:  String)  =  CouchbaseAction("mybucket")  {  bucket  =>        bucket.get[User](key).map  {  user  =>            user.map(Ok(views.html.user(_)))                .getOrElse(BadRequest(s"Unable  to  find  user  with  key:  $key"))        }    } }
  16. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    def  bucket  =  Couchbase.bucket("mybucket")    def  findById(id:  String):  Future[Option[User]]    =                                                                                    bucket.get[User](id)    def  save(user:  User):  Future[OperationStatus]      =                                                                                    bucket.set[User](user.id,  user)    def  delete(user:  User):  Future[OperationStatus]  =                                                                                    bucket.delete(user.id) }
  17. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    val  bucket  =  Couchbase.bucket("mybucket")    def  findAll():  Future[List[User]]  =  ???       }
  18. Les vues function  (doc,  meta)  {      if  (doc.name)

     {          emit(doc.name,  null);      }   }   document : users vue : by_name map :
  19. Les vues function  (doc,  meta)  {      if  (doc.email)

     {          emit(doc.email,  null);      }   }   document : users vue : by_email map :
  20. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    val  bucket  =  Couchbase.bucket("mybucket")    def  findAll():  Future[List[User]]  =  ???       }
  21. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    val  bucket  =  Couchbase.bucket("mybucket")    def  findAll():  Future[List[User]]  =          bucket.find[User]("users",  "by_name")            (new  Query().setIncludeDocs(true).setStale(Stale.FALSE))                .toList }
  22. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    val  bucket  =  Couchbase.bucket("mybucket")    def  findAll():  Future[Enumerator[User]]  =          bucket.find[User]("users",  "by_name")            (new  Query().setIncludeDocs(true).setStale(Stale.FALSE))                .enumerate }
  23. Depuis un modèle case  class  User(id:  String,  name:  String,  email:

     String) object  User  {    implicit  val  userFmt  =  Json.format[User]    val  bucket  =  Couchbase.bucket("mybucket")    def  findByName(name:  String):  Future[Option[User]]  =  {        val  query  =  new  Query().setIncludeDocs(true).setLimit(1)                    .setRangeStart(ComplexKey.of(name))                    .setRangeEnd(ComplexKey.of(s"$name\uefff")                    .setStale(Stale.FALSE))        bucket.find[User]("users",  "by_name")(query).headOption    } }
  24. CRUD case  class  User(_id:  String,  name:  String,  email:  String) object

     User  {    implicit  val  fmt  =  Json.format[User] } object  UserController  extends  CouchbaseCrudSourceController[User]  {    def  bucket  =  Couchbase.bucket("mybucket") }
  25. CRUD -­‐>            /users    

         controllers.UserController GET          /users/               POST        /users/                                                                                     GET          /users/{id}                                                                             PUT          /users/{id}                                                                             PUT          /users/{id}/partial                                                             DELETE    /users/{id}                                                                             POST        /users/find/       GET          /users/stream/ POST        /users/batch                                                                           PUT          /users/batch                                                                           DELETE    /users/batch                                                                          
  26. CRUD case  class  User(_id:  String,  name:  String,  email:  String) object

     User  {    implicit  val  fmt  =  Json.format[User] } object  UserController  extends  CouchbaseCrudSourceController[User]  {    def  bucket  =  Couchbase.bucket("mybucket")    override  def  defaultViewName  =  "by_email"    override  def  defaultDesignDocname  =  "users"    override  def  idKey  =  "_id" }
  27. + +

  28. Couchbase as Cache 500:org.ancelin.play2.couchbase.plugins.CouchbaseCachePlugin couchbase  {    cache  {  

         bucket="mybucket"        enabled=true        namespace="play-­‐cache."    } } Cache.set("item.key",  connectedUser) val  maybeUser:  Option[User]  =  Cache.getAs[User]("item.key")
  29. Evolutions 600:org.ancelin.play2.couchbase.plugins.CouchbaseEvolutionsPlugin couchbase  {    evolutions  {      

     default  {            apply  =  true        }    } } {    "name":"users",    "views":{        "byName":  {            "map":  "function  (doc,  meta)  {                              if(doc.name)                    emit(doc.name);                                  }"        }    } }
  30. Fixtures 700:org.ancelin.play2.couchbase.plugins.CouchbaseFixturesPlugin couchbase  {    fixtures  {      

     mybucket  {              insert  =  true            key  =  "_id"                    }    } } [        {                "_id":  "1",                "name":  "John  Doe",                "email":  "[email protected]",                "password":  "password"        },        {                "_id":  "2",                "name":  "Jane  Doe",                "email":  "[email protected]",                "password":  "password"        } ]
  31. N1QL 1000:org.ancelin.play2.couchbase.plugins.CouchbaseN1QLPlugin couchbase  {      n1ql  {    

         host="127.0.0.1"          port=8093      } } • Langage de requêtage orienté document • Très très très inspiré de SQL ;-) • Serveur à part disponible de pré-béta • Sera probablement intégré dans la distribution
  32. N1QL case  class  User(name:  String,  age:  Int) object  N1QLController  extends

     Controller  {    implicit  val  userFormat  =  Json.format[User]    def  find(age:  Int)  =  Action.async  {        N1QL(  s"""  SELECT  name,  age  FROM  mybucket  WHERE  age  >  ${age}  """  )                                                                                  .toList[User].map  {  user  =>            Ok(views.html.index(s"Users  older  than  ${age}",  users))        }    } }
  33. N1QL case  class  User(name:  String,  age:  Int) object  N1QLController  extends

     Controller  {    implicit  val  userFormat  =  Json.format[User]    def  find(age:  Int)  =  Action.async  {        N1QL(  s"""  SELECT  name,  age  FROM  mybucket  WHERE  age  >  ${age}  """  )                                                                            .enumerate[User].map  {  users  =>            Ok.chunked(    (users  &>      Enumeratee.collect[User]{case  p@User(_,  age)  if  age  <  50  =>  p}  ><>      Enumeratee.map[User](userFormat.writes))  >>>  Enumerator.eof  )            }    } }