Mi viaje al BACK SIDE

60
Mi viaje al BACK SIDE

Transcript of Mi viaje al BACK SIDE

Page 1: Mi viaje al BACK SIDE

Mi viaje al

BACK SIDE

Page 2: Mi viaje al BACK SIDE

@LemaNahuel

Pico teclas en javascript

Page 3: Mi viaje al BACK SIDE

Que es FWTV?

FWTV es el primer canal de WebTV de Hispanoamérica, con contenido pensado exclusivamente para Internet, donde los espectadores pueden interactuar con los programas que se ofrecen tanto en vivo como bajo demanda, en cualquier momento y desde cualquier lugar.

https://www.fwtv.tv/

Page 4: Mi viaje al BACK SIDE

@Obaca2015 Yo

Page 5: Mi viaje al BACK SIDE

Hace mucho tiempo atrás ... (2013-2014)

Page 6: Mi viaje al BACK SIDE

fwtv.tv

fwtv.tv/api/*

fwtv.tv/admin/*

Heroku con 4 dynos

Page 7: Mi viaje al BACK SIDE

● Proyecto Monolítico● Realizado a las chapas en unas pocas semanas● +150 endpoints y en continuo crecimiento● callback hell● Dependencias freezadas (Node .08, Mongoose,

Redis, Express, etc)

● Sin Documentación o Tests● Filesystem ya no escalable● Archivos kilométricos (promedio 1000 - 2500

líneas)● ...

Page 8: Mi viaje al BACK SIDE

... ART* 500 ms - 1000 ms o más … ¬¬

*Average Response Time

Page 9: Mi viaje al BACK SIDE
Page 10: Mi viaje al BACK SIDE

Preparando el terreno para la v2

Page 11: Mi viaje al BACK SIDE

Empezamos con los pasos obvios

Page 12: Mi viaje al BACK SIDE

y como mineros separamos los proyectos

Page 13: Mi viaje al BACK SIDE

api.

.tv

admin.

Page 14: Mi viaje al BACK SIDE

● Implementación de Tests● Agregada documentación● Extremo linteo con EsLint y jsHint● Actualizadas dependencias y Node 0.12● Se reestructuró todo el filesystem

○ routes/(public || private)/*.js

○ controllers/(public || private)/*.js

● Desarmar archivos kilométricos haciendo pequeños módulos

● ...

Page 15: Mi viaje al BACK SIDE

… ART meeh … ¬¬

Page 16: Mi viaje al BACK SIDE

… pero ahora escala mejor!

Page 17: Mi viaje al BACK SIDE

v2.0 > v2.1 - Go functional!

Page 18: Mi viaje al BACK SIDE

Estandarizado el uso de lodash● aprox 100 implementaciones de _.each()● más métodos implementados como _.pluck(),

_.indexOf(), _.find(), _.without(), _.

omit(), etc.

Page 19: Mi viaje al BACK SIDE

_.each(req.body.genres, (genre) => {

if (_.isObject(genre)) {

newGenres.push(genre._id);

} else {

newGenres.push(genre);

}

});

var genres = req.body.genres,

genre = null,

i = 0,

l = genres && genres.length ? genres.length : 0;

for (i; i < l; i++) {

genre = genres[i];

if (typeof genre === 'object') {

newGenres.push(genre._id);

} else {

newGenres.push(genre);

}

}

Por ejemplo:_.each()

Page 20: Mi viaje al BACK SIDE

… ART sin mejoría …

Page 21: Mi viaje al BACK SIDE

… pero el código es más legible!

Page 22: Mi viaje al BACK SIDE

v2.1 > v2.2 - only what you need

Page 23: Mi viaje al BACK SIDE

Implementación de “Extender”● Ahora se puede especificar cuales campos se

necesitan y cuáles no.● Utilizado especialmente para traducciones y datos

que solo usa la api o el admin.

Page 24: Mi viaje al BACK SIDE

Por ejemplo:

function getTranslations() { if (LOCALE.indexOf('es') === -1) { return ',translations(' +LOCALE + '(description))'; }

return '';}

var fields = '?fields='; fields += 'landing'; fields += ',popularShows(_id,logo,slug,title,description,slogan' + getTranslations() + ')';

return $http.get(PATHS.HOME + fields);

Page 25: Mi viaje al BACK SIDE

Agregados query strings para Paginar y Limitar● Skip● Limit● LastId

Page 26: Mi viaje al BACK SIDE

Por ejemplo:

return $http.get(PATHS.SHOWS, {

params: {

limit: params.limit || 12,

lastId: params.lastId,

skip: params.skip

}

});

Page 27: Mi viaje al BACK SIDE

… ART apenas notable …

Page 28: Mi viaje al BACK SIDE

… pero se bajó el peso de 100 kb - 250 kb a 1b-30 kb!

Page 29: Mi viaje al BACK SIDE

v2.2 > v2.3 - the real meaning of parallel

Page 30: Mi viaje al BACK SIDE

ASYNC● _.each() >

○ async.each()● callback hell >

○ async.waterfall() ○ async.parallel()

Page 31: Mi viaje al BACK SIDE

Por ejemplo:async.waterfall()

getHome(function(home){ setPopularAndLiveShows(home, function(popularShows, liveNow, newNow)(){ home.popularShows = popularShows; home.liveNow = liveNow; home.newNow = newNow;

setFeaturesVideos(home, function(featuredVideos){ home.featuredVideos = featuredVideos; helpers.handleResponse(res, null, home); }); });});

async.waterfall([function(cb) { getHome(function(home) { cb(null, home); });}, function(cb){ setPopularAndLiveShows(home, function(popularShows, liveNow, newNow){ home.popularShows = popularShows; home.liveNow = liveNow; home.newNow = newNow;

cb(null, home); });}, function(home, cb) { setFeaturesVideos(home, function(featuredVideos) { home.featuredVideos = featuredVideos; cb(null, home); });}], function(err, home) { helpers.handleResponse(res, null, home);});

Page 32: Mi viaje al BACK SIDE

Por ejemplo:async.parallel()

async.parallel([function(cb) { setPopularVideos(home, likedProgramIds, function(popularVideos) { home.popularVideos = popularVideos; cb(null); }); }, function(cb) { setGenres(home, function(genres) { if(genres) { home.genres = genres; } cb(null, home); });}], function(err) { cb(null, home);});

Page 33: Mi viaje al BACK SIDE

Por ejemplo:async.each()

async.each(shows, function(show, cb) { ScheduledPromo.count({

program: show._id }).exec(function(err, scheduleds) { Program.findByIdAndUpdate(show._id, { $set: { 'hasElements.scheduled': !!scheduleds } }).exec(function(err) { cb(err); }); }); }, function(err) { helpers.handleResponse(res, null, { success: !err });});

Page 34: Mi viaje al BACK SIDE

… ART zarpadamente notable…

Page 35: Mi viaje al BACK SIDE

… bajo de 500 ms - 1000 ms a 150 ms - 300 ms!

Page 36: Mi viaje al BACK SIDE

v2.2 > v3 - Sh*t just got real

Page 37: Mi viaje al BACK SIDE

Actualización a Node 4.* y dependencias npm

Page 38: Mi viaje al BACK SIDE

… ART igual …

Page 39: Mi viaje al BACK SIDE

… pero se redujo el consumo de RAM en Heroku significativamente!

Page 40: Mi viaje al BACK SIDE

v3 > v3.1 - sugar Marty ... syntaxis sugar everywhere

Page 41: Mi viaje al BACK SIDE

Actualización a Node 5.* y dependencias npm● Refactor mínimo a ES6

○ EsLint + ES6

○ const y let

○ arrow functions

○ classes

Page 42: Mi viaje al BACK SIDE

… ART sin mejoras y continúa bajando el consumo de RAM ...

Page 43: Mi viaje al BACK SIDE

… pero el código es más limpio

Page 44: Mi viaje al BACK SIDE

El viaje hasta ahora● Todas las dependencias actualizadas (Node && NPM)● Programación funcional mediante lodash● Peticiones muy livianas● Asincronismo, paralelismo y callback hell resuelto con async● ES6 mínimamente implementado● ART entre 100 ms - 300 ms

Page 45: Mi viaje al BACK SIDE

aun así algo faltaba …

Page 46: Mi viaje al BACK SIDE

… revisar los accesos a MongoDB

Page 47: Mi viaje al BACK SIDE

v3.1 > v3.2 - the final fight!

Page 48: Mi viaje al BACK SIDE

● Mediante con Mongoose● ~400 queries a la base en toda la API.● la mayoría realizadas por el front y las apps

Page 49: Mi viaje al BACK SIDE

I’m Mr. Meeseeks!! Look at me!

Page 50: Mi viaje al BACK SIDE

Paso 1:● +400 queries 1 x 1:

○ .find() >

■ .findById() || .findOne()

○ .update() >

■ .findByIdAndUpdate() || .findOneAndUpdate()

○ .remove() >

■ .findByIdAndRemove() || .findOneAndRemove()

Page 51: Mi viaje al BACK SIDE

Por ejemplo:.find() > .findById()

Model.find({ _id: req.params.id }).populate('video grouping program genres').exec((err, doc) => { helpers.handleResponse(res, err, doc);});

Model.update({ _id: req.params.id }, {}).exec((err, doc) => { helpers.handleResponse(res, err, doc);});

Model.remove({ _id: req.params.id }).exec((err, doc) => { helpers.handleResponse(res, err, doc);});

Model.findById(req.params.id) .populate('video grouping program genres') .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndUpdate(req.params.id, {}) .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndRemove(req.params.id) .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Page 52: Mi viaje al BACK SIDE

Paso 2:● Implementar el método .lean() en todos

los .find*() antes del .exec()

Page 53: Mi viaje al BACK SIDE

Ejemplo:.lean()

Model.findById(req.params.id) .populate('video grouping program genres') .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndUpdate(req.params.id, {}) .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndRemove(req.params.id) .exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findById(req.params.id) .populate('video grouping program genres') .lean().exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndUpdate(req.params.id, {}) .lean().exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Model.findByIdAndRemove(req.params.id) .lean().exec((err, doc) => { helpers.handleResponse(res, err, doc); });

Page 54: Mi viaje al BACK SIDE

ahora si viejo … a ver

Page 55: Mi viaje al BACK SIDE

… el ART bajó a 5 ms - 35 ms!! MOTHERF*CKAAA!!!!

Page 56: Mi viaje al BACK SIDE

Conclusiones

Page 57: Mi viaje al BACK SIDE

● Código más limpio, ordenado y funcional● Respuestas muy livianas● Bajo consumo de RAM● Queries eficientes● Misma cantidad de dynos, mayor capacidad● ncu semanales

Page 58: Mi viaje al BACK SIDE

Preguntas?

Page 59: Mi viaje al BACK SIDE

Cuantos Mr. Meeseeks encontraste?- 9- 13- 15

Page 60: Mi viaje al BACK SIDE

Gracias!

@LemaNahuel