58
By @FinistSeb and @LostInBrittany

BreizhCamp 2013 - Pimp my backend

Embed Size (px)

DESCRIPTION

Our presentation at BreizhCamp 2013

Citation preview

Page 1: BreizhCamp 2013 - Pimp my backend

By @FinistSeb and @LostInBrittany

Page 2: BreizhCamp 2013 - Pimp my backend

Le développeur de l'Est le plus à l'Ouest !Java & C++ Developer, coding addict,

continuous intégration ayatollah

● Senior Software Engineer currently, Crédit Mutuel Arkea

● FinistJug actif member

BreizhBeans community and commiter

[email protected] +sebastien.lambour

@FinistSeb

Sebastien Lambour

Page 3: BreizhCamp 2013 - Pimp my backend

Horacio GonzalezSpaniard lost in Brittany, Java developer,

dreamer and all-around geek

● Senior Software Engineer at Crédit Mutuel Arkea○ Currently...

● Leader of the FinistJUG and the BreizhBeans community

http://lostinbrittany.org/ +Horacio.Gonzalez @LostInBrittany

Page 4: BreizhCamp 2013 - Pimp my backend

BreizhBeans

Developer community at the far end of French Brittany

http://breizhbeans.org/ @BreizhBeans

Page 5: BreizhCamp 2013 - Pimp my backend

I help to create a startup on social translation, would

you code with me ?

Great, I do the backend! How many members?

Some 30.000 - 50.000 will be fine!

Ouch, is it not too small? I like big toys!!!!

Me too!Let's think big then

OK, it will rock !

Nexus 4

Why ?

Page 6: BreizhCamp 2013 - Pimp my backend

Backends They often look like that !

Page 7: BreizhCamp 2013 - Pimp my backend

Transforming it is HARD !

Next challenge :Granite aircraft !!

Page 8: BreizhCamp 2013 - Pimp my backend

A sexy backend?Let's groom it!

OK, go around...

Light weight Test driven

ScalableCloud Ready

Java

Data Driven

Page 9: BreizhCamp 2013 - Pimp my backend

Following the beaten trackBrainless route

The usual suspects...

JSF 2.0Hibernate

JPACXF

SQL

Spring CRUD(Create, Read, Update,Delete)

Page 10: BreizhCamp 2013 - Pimp my backend

Booooooring...And ineffective

Let's use it!

Page 11: BreizhCamp 2013 - Pimp my backend

Design choicesBecause we need some solid roots

Page 12: BreizhCamp 2013 - Pimp my backend

Core principlesWhat do we want our backend to be?

● Scalability ○ Because traffic growth should be a blessing, not a problem!

● Adaptability ○ Because your business evolves, and your backend need to evolve

with it!

● Testability○ Because you want to be sure that your code works!

● NoSQL (meaning Not Only SQL)○ Because you need not only tables and foreign keys, but non-SQL

stores, search indexes, caches...

● Productivity○ Because the other 4 doesn't matter if you need to spend 4

working days to modify a service...

Page 13: BreizhCamp 2013 - Pimp my backend

A first choice : Apache Thrift

« Software framework for scalable and performant cross-language services development »

● Interface description language● Multi-language code generation● Serialisation/deserialisation● RPC Framework● High performance

○ On transport layer○ On data manipulation

Page 14: BreizhCamp 2013 - Pimp my backend

Thrift, it's cool Thrift boots productivity!

Page 15: BreizhCamp 2013 - Pimp my backend

A second choice : MongoDB

● We needed a NO-SQL store for documents

● We wanted to use Cassandra○ Cassandra & Thrift, a love story

● We wanted a PaaS-ready backend○ Neither Cloudbees nor Cloudfoundry supported Cassandra○ But they supported MongoDB

● We loved MongoDB○ Document DB are great for document oriented applications

Page 16: BreizhCamp 2013 - Pimp my backend

ThriftMongoBridgeBecause we wanted both

Thrift and MongoDB

Page 17: BreizhCamp 2013 - Pimp my backend

Thrift rocks and works

But...

How to put Thrift objects into MongoDB ?

Page 18: BreizhCamp 2013 - Pimp my backend

First bad idea!

Use Spring Data or Morphia

Thrift object mapping is bad !

Page 19: BreizhCamp 2013 - Pimp my backend

Damned!

First bad idea!

Page 20: BreizhCamp 2013 - Pimp my backend

Second bad idea !

Use the Mongo Driver JSON parsing

Deserialisation will be a bit hard...

Page 21: BreizhCamp 2013 - Pimp my backend

Damned !

Second bad idea!

Page 22: BreizhCamp 2013 - Pimp my backend

Thrift isn't cool anymore ?

We need a new idea !

Page 23: BreizhCamp 2013 - Pimp my backend

Thrift, it's cool !But we had work to do...

Write a TBSONProtocol !

The ThriftMongoBridge is born !

Page 24: BreizhCamp 2013 - Pimp my backend

What does it do ?

struct AnotherThrift {1:string anotherString,2:i32

anotherInteger,}

Thrift IDL

POJO generated by the Thrift compiler

public class AnotherThrift implements org.apache.thrift.TBase... { ... public String anotherString; public int anotherInteger;

... public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; .... public void read(org.apache.thrift.protocol.TProtocol iprot)... ... public void write(org.apache.thrift.protocol.TProtocol oprot) ......;}

Compile to

{anotherString:"value", anotherInteger:69}

Write To BSON Read from BSON

TBSonSerializerTBSonProtocol TBSonDeSerializer

Page 25: BreizhCamp 2013 - Pimp my backend

Really simple to use Too simple ?

AnotherThrift anotherThrift = new AnotherThrift();

anotherThrift.setAnotherString("value");

anotherThrift.setAnotherInteger(69);

// serialize into DBObject

DBObject dbObject = ThriftMongoHelper.thrift2DBObject(anotherThrift);

{anotherString:"value", anotherInteger:69}

Page 26: BreizhCamp 2013 - Pimp my backend

How it works

DBObject (BSON)

TBSONProtocol

TBaseTBSONSerializer

write(protocol)

getResult

Context Stack

sentense directives (begin / end)

Data (read / write)

Struct contextDBObject

push pop

Thrift object generated

MongoDB object

Page 27: BreizhCamp 2013 - Pimp my backend

The BreizhCamp demoBecause you want to see some code,

don't you?

Page 28: BreizhCamp 2013 - Pimp my backend

The BreizhCamp demoGiven this Thrift model & service

struct BreizhCampEvent { 1:i64 date, 2:string startTime, 3:string endTime, 4:com.breizhbeans.backend.thrift.enums.model.EventTypes type, 5:string room, 6:string resume, 7:string details, 8:list<string> speakers 9:list<com.breizhbeans.backend.thrift.enums.model.Track> tracks,}

service ProgramService {

com.....GetProgramResponse getProgram(1:com.....GetProgramRequest request)

throws (1:com....TechnicalException texp, 2:com...FunctionalException fexp)

}

struct GetProgramRequest {1:com.breizhbeans.backend.thrift.enums.model.Track track,2:com.breizhbeans.backend.thrift.enums.model.EventTypes type,3:string room,4:list<string> speakers,

}struct GetProgramResponse {

1:list<com.breizhbeans.backend.thrift.model.BreizhCampEvent> events,}

Page 29: BreizhCamp 2013 - Pimp my backend

The BreizhCamp demoFirst : Write a test

● One loader by collection● One goal : Have Human readable test ;-)● Manage automatically data insertion before test and

clean up after test @Test

public void testGetEvents() throws Exception {

List<BreizhCampEvent> actualEvents = programDao.getEvent();

List<BreizhCampEvent> expectedEvents = new ArrayList<BreizhCampEvent>();

expectedEvents.add(getEventPimpMyBackend());

Assert.assertEquals(expectedEvents, actualEvents);

}

Application code to test

Standard JUnit assertion

Expected data building(used by the test and the data loader)

Page 30: BreizhCamp 2013 - Pimp my backend

The BreizhCamp demoWrite a DAO

public List<BreizhCampEvent> getEvent() throws TechnicalException {

DBCursor cursor = null;

try {

List<BreizhCampEvent> events = new ArrayList<BreizhCampEvent>();

DBCollection collection = db.getCollection(collectionName);

DBObject query = new BasicDBObject();

// execute the query

cursor = collection.find(query);

while(cursor.hasNext()) {

events.add( (BreizhCampEvent) ThriftMongoHelper.DBObject2Thrift(cursor.next()));

}

return events;

} catch (Exception e) {

throw new H4TTechnicalException("Unexpected error", e, null);

} finally {

cursor.close();

}

}

retrieve DB collection

Build and execute the Query

Deserialize Thrift objects

Error handling

Never forget it ;-)

Page 31: BreizhCamp 2013 - Pimp my backend

@Override

public BreizhCampEvent updateRoom(final BreizhCampEvent event) throws TechnicalException {

try {

BasicDBObject updateQuery = new BasicDBObject();

updateQuery.append("$set", new BasicDBObject()

.append(getFields(BreizhCampEvent._Fields.ROOM), event.getRoom()));

db.getCollection(collectionName)

.update(getPrimaryKeyRequest(event), updateQuery)

.getLastError()

.throwOnError();

return event;

} catch (Exception e) {

throw new H4TTechnicalException("Unexpected error", e, ObjectUtils.toString(event));

}

}

The BreizhCamp demoCan I update thrift Object by code?

Mongo Update Query

Thrift structure mapping

Query execution

Error handling

Page 32: BreizhCamp 2013 - Pimp my backend

The BreizhCamp demoCan I update thrift Object by script?

Mongo DB Bson writed : > db.program.find();

{ "_id" : "id=10", "date" : NumberLong("-56433978000000"), "startTime" : "14:45", "endTime" : "15:40", "type" : 4, "room" : "Bréhat", "resume" : "Pimp my backend!", "details" : "Le frontend, le frontend...... ...", "speakers" : [ "Sébastien Lambour", "Horacio Gonzalez" ], "tracks" : [ 7 ], "id" : 10, "classname" : "com.breizhbeans.backend.thrift.model.BreizhCampEvent" }

Update the field room :> db.program.update();

{ "id": 10}, { $set: { "room": "Ouessant" } }

Page 33: BreizhCamp 2013 - Pimp my backend

And the perfs?Because speed matters!

Page 34: BreizhCamp 2013 - Pimp my backend

Talking about perfsThe candidates are ...

Morphia

Spring Data

And... ThriftMongoBridge, of course

Page 35: BreizhCamp 2013 - Pimp my backend

Spring Data vsThriftMongoBridge

● Write and Read 500 objects into MongoDB.

● Each document is composed with a List<> and a Set<> of 500 objects.

● The document have a JSON size of 91613 bytes

Who will win ?

Page 36: BreizhCamp 2013 - Pimp my backend

CodeThriftMongoBridge

First// Get a collection

DBCollection collection = db.getCollection("dummyColl");

Write// Serialize the Thrift object

DBObject dbObject = ThriftMongoHelper.thrift2DBObject(thriftObject);

// Put the document

collection.insert(dbObject);

ReadDBCursor cursorDoc = collection.find();

while (cursorDoc.hasNext()) {

DBObject dbObject = cursorDoc.next();

ThriftObject thirftObject = (ThriftObject)ThriftMongoHelper.DBObject2Thrift(dbObject);

}

Page 37: BreizhCamp 2013 - Pimp my backend

CodeSpring Data

First// Retrieve the MongoOperations bean

MongoOperations mongoOperation =

(MongoOperations) ctx.getBean("mongoTemplate");

Write// Serialize the Thrift object

mongoOperation.save(springObject, "springObject");

ReadList<SpringObject> listUser =

mongoOperation.findAll(SpringObject.class, "springObject");

OK. Spring Data is (a little bit) more compact...

Page 38: BreizhCamp 2013 - Pimp my backend

Time to run !

Page 39: BreizhCamp 2013 - Pimp my backend

Thrift over MongoDB rocks!

We need a logo for our OpenSource library...

Available now :

2.0

https://github.com/BreizhBeans/ThriftTools

Page 40: BreizhCamp 2013 - Pimp my backend

A storage library a backend is not,young padawan

Because we need more things

Page 41: BreizhCamp 2013 - Pimp my backend

At the beginning...Pretty layers, neatly isolated, like a Care Bears' birthday cake

Page 42: BreizhCamp 2013 - Pimp my backend

Later in the projectLike a cake after an earthquake

Page 43: BreizhCamp 2013 - Pimp my backend

And in the end...As pretty as a British pudding... and almost as tasty

Page 44: BreizhCamp 2013 - Pimp my backend

Be Layer lessNow build the Backend !

● Each package have a goal

● NO package assembly rules

● Improve the complexity detection by dependencies analysis

Goal : Fight against the "God classes" anti-pattern

unreachable dream

Page 45: BreizhCamp 2013 - Pimp my backend

Prevent protocol rupture, have a stable services signature.

com.h4t.CheckAccessResponse checkAccess (1:com.h4t.CheckAccessRequest request) throws (1:com.h4t.TechnicalException texp, 2:com.h4t.FunctionalException fexp)

stable method signature

Thrift services

Page 46: BreizhCamp 2013 - Pimp my backend

Integrate this into the backend

Thrift IFace implementation

Request / Response decapsulation + log & monitoring

Core Business code

MongoOpérations

Thrift object

ThriftMongoHelper

Mongo Driver

DBObject

write read

Query

DBObject

Page 47: BreizhCamp 2013 - Pimp my backend

Inversion of control

Keep simple too !

Prefer static dependencies injection.

Time to do the choice

or

Page 48: BreizhCamp 2013 - Pimp my backend

Inversion of control

Why ?

Because and

○ binding of the MongoDB service by the cloud○ no production configuration required○ only one war

Page 49: BreizhCamp 2013 - Pimp my backend

TestabilityBecause without tests

you need faith

Page 50: BreizhCamp 2013 - Pimp my backend

Test strategy

No compromise!The rules are simple... no exception!

Page 51: BreizhCamp 2013 - Pimp my backend

TDD is my religion1. Execute test without setup2. Execute test with your build tool3. Execute test into your IDE4. No injection into test framework5. Reuse only application injection6. Keep it simple (repeat it 7 times)7. Have a loose coupling (test the smallest parts)8. Test must be deterministic and reproducible9. Do pair review with the tests

10. If the complexity is growing up refactor now...

11. If a rule goes wrong, rewrite it !

Page 52: BreizhCamp 2013 - Pimp my backend

From concept to reality

ThriftMongoTestToolsThrift DataSet

Collection Loader

Load thrift object with preset datasinto MongoDB

Get expected Thift object with preset datas

Get a Thrift object from MongoDB related to a unique key

Page 53: BreizhCamp 2013 - Pimp my backend

DAO : Validate queries and set up the test data loaders

IFACE : Functional tests

SERVICE : Test a business centric service

TU : Unit test not related to the business (test first approach)

Tests have goals

Page 54: BreizhCamp 2013 - Pimp my backend

@Before

public void setUp() throws Exception {

eventLoader.load(getEventPimpMyBackend());

}

private BreizhCampEvent getEventPimpMyBackend() throws Exception {

String details = "Le frontend, le frontend... .....";

BreizhCampEvent event = eventLoader.getExpected(10, "14/06/2013", "14:45", "15:40", EventTypes.CONF, "Bréhat", "Pimp my backend!", details);

event = eventLoader.addSpeakers(event, "Sébastien Lambour", "Horacio Gonzalez");

event = eventLoader.addTracks(event, Track.CLOUD);

return event;

}

Tests are Data Driven

Build test Data with java code, files, BDDor the MongoThrift Test tools (not opensourced yet ;-) )

Inject Data into MongoDB

Page 55: BreizhCamp 2013 - Pimp my backend

What's next?Because our work is not completed... yet!

Page 56: BreizhCamp 2013 - Pimp my backend

Why integrate our lib ?

#Pragmatism● Productivity oriented● Fully testable ● Container less (no servlet dependencies)● Open Source : Apache 2.0

The ThriftMongoBackend Core model will born soon !

With a name: TINAF (ThriftMongoBackend Is Not A Framework)

Page 57: BreizhCamp 2013 - Pimp my backend

What's next ?

● monitoring Thrift services in the cloud

● load tests with vmware & 10gen

● contribute TINAF

● Develop a thrift service valve to ensure brandwitdth for critical services

... do a talk at Devoxx BE

Page 58: BreizhCamp 2013 - Pimp my backend

Thank you !