Slide 1

Slide 1 text

ReactiveMongo “Experiences using ReactiveMongo to build a JSON API with Play” - Alexander Jarvis London, 9th April 2013 PANAXIOM

Slide 2

Slide 2 text

Overview • A brief history & context • What is ReactiveMongo? • Asynchronous & Non-blocking I/O • Rewrite, reduce. • Vanity benchmarks • Typical usage & patterns

Slide 3

Slide 3 text

๏ Real-time location sharing ๏ Currently in alpha ๏ API available http://meep.io ๏ Uses ReactiveMongo

Slide 4

Slide 4 text

Common project goals • Provide a fast JSON API over HTTP • High throughput, low latency • Scalability curl https://meep.io/users/UR6pdkqrFEgAG_H2 -H 'Authorization: Bearer xd97JMba+a*yM51ZsBEUUT)_fgYt10BO' -H 'Accept: application/json' { "id":"UR6pdkqrFEgAG_H2", "email":"[email protected]", "firstName":"Charles", "lastName":"Babbage", "image":"UR6qFEqrFBsA3OL9", "updated":"2013-02-15T22:32:38.410+01:00" }

Slide 5

Slide 5 text

• Web framework for Java and Scala • 1.0 public 2009 (mostly Java, Scala module) • 2.0 rewritten in Scala at end of 2011 • Gaining momentum, support from Typesafe • Open Source (Apache 2) playframework.com

Slide 6

Slide 6 text

• Stateless (low memory, horizontal scaling) • Non-blocking I/O • Built using the Akka Actor Model & Netty

Slide 7

Slide 7 text

Enough Playing around, more mongo • Transition from relational databases and ORMs to MongoDB and documents makes sense for a JSON API • With de-normalised data a JSON API is even closer to representations in the database Web Client Web Service MongoDB BSON JSON

Slide 8

Slide 8 text

Finding your MongoDB Nirvana Official Java driver (with mongo-jackson-mapper) public boolean updateAccessToken() { ! return db().update(DBQuery.is("_id", new org.bson.types.ObjectId(id)), ! ! ! new BasicDBObject("$set", new BasicDBObject("accessToken", accessToken)), ! ! ! false, ! ! ! false, ! ! ! WriteConcern.SAFE).getLastError().ok(); }

Slide 9

Slide 9 text

Finding your MongoDB Nirvana Official Java driver (with mongo-jackson-mapper) public boolean updateAccessToken() { ! return db().update(DBQuery.is("_id", new org.bson.types.ObjectId(id)), ! ! ! new BasicDBObject("$set", new BasicDBObject("accessToken", accessToken)), ! ! ! false, ! ! ! false, ! ! ! WriteConcern.SAFE).getLastError().ok(); } public boolean updateAccessToken() { ! return users().update("{_id: #}", this._id).concern(WriteConcern.SAFE) ! ! ! .with("{$set: {accessToken: #}}", this.accessToken).getLastError().ok(); } Jongo after 1 month

Slide 10

Slide 10 text

ReactiveMongo • Asynchronous & Non-Blocking Scala Driver for MongoDB • Written by Stephane Godbillion at Zenexity (original company behind Play) • Released 30th August 2012 • Open Source (Apache 2) reactivemongo.org

Slide 11

Slide 11 text

Motivation • Classic synchronous DB drivers block threads until response is received • Threads can only scale up to a limit • Context switches are expensive • Made specifically for Play • The future is streaming data • Related to the “The C10K problem”

Slide 12

Slide 12 text

Non-blocking I/O The fast food restaurant analogy: non-blocking: “When you place an order at the cashier, it’s sent off for someone else to fulfil and you step aside for the next customer to be served.” blocking: “When you place an order at the cashier, you wait for your order and no other customers can be served...”

Slide 13

Slide 13 text

Scala Futures • A composable container for the result of a computation that may not exist yet. • ReactiveMongo’s basic Cursor functions return Futures: def headOption(): Future[Option[T]] def toList(upTo: Int): Future[List[T]] def toList(): Future[List[T]] (simplified)

Slide 14

Slide 14 text

Rewrite, reduce • Strategic, long-term decision • Play’s Java API didn’t feel as fluid as 1.0

Slide 15

Slide 15 text

Rewrite, reduce • Strategic, long-term decision • Play’s Java API didn’t feel as fluid as 1.0 24th Sep -> 7th Nov 2012 (1 month and 14 days) 0 525 1,050 1,575 2,100 Lines of code 1,199 2,028 Java Scala

Slide 16

Slide 16 text

Vanity benchmarks Downloading a GridFS file 0 375 750 1125 1500 244 Kb file 1464.11 966.68 Requests / sec (mean) Official Reactive Apache Bench ab -n 5000 -c 10

Slide 17

Slide 17 text

Vanity benchmarks Downloading a GridFS file 0 375 750 1125 1500 244 Kb file 1464.11 966.68 Requests / sec (mean) Official Reactive 0 23 45 68 90 6.2 Mb file 86.12 57.12 Requests / sec (mean) Official Reactive Apache Bench ab -n 5000 -c 10

Slide 18

Slide 18 text

Vanity benchmarks 33% increase in requests / second Official Java Driver ReactiveMongo • Noticed fans spinning when using the Java Driver • ReactiveMongo handles resources better

Slide 19

Slide 19 text

Vanity benchmarks 33% increase in requests / second Official Java Driver ReactiveMongo • Noticed fans spinning when using the Java Driver • ReactiveMongo handles resources better

Slide 20

Slide 20 text

A Simple Example Play Routes File https://github.com/alexanderjarvis/reactivemongo-json-demo

Slide 21

Slide 21 text

Mapping the Future Inside a Play Controller

Slide 22

Slide 22 text

Mapping the Future Inside a Play Controller Future[List[Thing]]

Slide 23

Slide 23 text

Mapping the Future Inside a Play Controller { async Future[List[Thing]]

Slide 24

Slide 24 text

GET a /thing curl localhost:9000/things/5143294a8a147dbf12ad65bb { "id":"5143294a8a147dbf12ad65bb", "title":"the same thing", "description":"updated thing", "created":1363355978000 }

Slide 25

Slide 25 text

GET a /thing curl localhost:9000/things/5143294a8a147dbf12ad65bb { "id":"5143294a8a147dbf12ad65bb", "title":"the same thing", "description":"updated thing", "created":1363355978000 } Future[Option[Thing]]

Slide 26

Slide 26 text

Future is a Monad • Allows you to chain results together • map, flatMap, filter • flatMap will flatten the result e.g. two nested maps create a Future of a Future. Future[Future[Result]] Future[Result] flatMap

Slide 27

Slide 27 text

Model • Manually define the reader/writer • Scala macros will reduce this to one line • Similar to Json reads writes combinators

Slide 28

Slide 28 text

Json implicit conversions (from a url shortener)

Slide 29

Slide 29 text

Json implicit conversions (from a url shortener) Json path

Slide 30

Slide 30 text

3 Ways to structure your web app

Slide 31

Slide 31 text

1. Monolithic Controller • What we have seen thus far • Mongo collection as val in Controller • Choice of JSON to BSON implicit conversion • Will get messy when mixing more models and controllers

Slide 32

Slide 32 text

DAO Model Controller Data Source 2. Data Access Objects Abstraction of common operations, separate from model

Slide 33

Slide 33 text

DAO Model Controller Data Source 2. Data Access Objects Abstraction of common operations, separate from model

Slide 34

Slide 34 text

DAO and flatMap POST /meetings/:id/accept

Slide 35

Slide 35 text

DAO and flatMap 1 POST /meetings/:id/accept

Slide 36

Slide 36 text

DAO and flatMap 1 2 POST /meetings/:id/accept

Slide 37

Slide 37 text

3. JSON Coast-to-Coast • Validate and transform JSON without static case class models • Data flow • JSON to BSON implicit conversions • Business logic needs to speak JSON github.com/mandubian/play2-json-demo example at

Slide 38

Slide 38 text

Enumerators / Iteratees • Streaming data • Immutable Producer / Consumer Potentially huge list in memory Streamed from Mongo

Slide 39

Slide 39 text

Try it out!

Slide 40

Slide 40 text

References 1. Play framework http://playframework.com/ 2. Mongo Jackson Mapper http://mongojack.org/ 3. Jongo http://jongo.org/ 4. ReactiveMongo http://reactivemongo.org/ 5. The C10K problem http://www.kegel.com/c10k.html 6. Scala Futures http://docs.scala-lang.org/overviews/core/futures.html 7. Scala Futures (akka) http://doc.akka.io/docs/akka/2.1.1/scala/futures.html 8. Reactive-json-demo https://github.com/alexanderjarvis/reactivemongo-json-demo 9. Linky https://github.com/alexanderjarvis/linky 10. JSON Coast-to-Coast http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/