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

Bringing chaos to order in your Node.js app

Bringing chaos to order in your Node.js app

Talk given at Node.js Dublin meetup - talks about using the cls node module

danjenkins

May 28, 2015
Tweet

More Decks by danjenkins

Other Decks in Programming

Transcript

  1. WHO AM I? DAN JENKINS NODE.JS DEVELOPER / ARCHITECT GOOGLE

    DEVELOPER EXPERT IN WEBRTC LOVE LEGO & TECHNIC GENERAL GEEK FOUNDER OF NIMBLE APE LTD ❤ OPEN SOURCE 3
  2. NODE.JS & I STARTED WITH NODE.JS WHEN IT WAS A

    BABY - 0.4 BUILT MANY, MANY MICROSERVICES AND REST APIS WITH NODE SPENT THE PAST YEAR WORKING ON A PLATFORM CALLED RESPOKE - WEBRTC 8
  3. FUNNY STORY… 9 H E L L O my name

    is Dan G O O G L E I / O
  4. WARNING… SOME BAD PRACTICES ARE USED IN SOME OF THE

    EXAMPLES. I KNOW YOU SHOULDN’T DO THESE THINGS. BUT, ITS THE EASIEST WAY TO SHOW THE POINT IN THE SMALL CONFINES OF A SLIDE DECK. I PROMISE I KNOW WHAT I’M DOING… 10
  5. WHO’S WRITTEN CODE LIKE THIS ? 12 createAdministrator: function (req,

    res) { Administrators.create({some: data}, function cb(error, admin) { if (error) { console.error('Error creating Admin'); res.send(500) return; } res.admin = admin; doAnotherCall(res, function cb2(err2){ res.send(200, res.admin); }); }); }
  6. OR LIKE THIS 13 var http = require('http'); var uuid

    = require('uuid'); function respond(res) { console.log('Got request with ID', res.id); res.end('Response'); } var server = http.createServer(function handleRequest(req, res){ res.id = uuid(); respond(res) }); server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
  7. OR LIKE THIS 14 var http = require('http'); var uuid

    = require('uuid'); function doSomething(res, id) { console.log('Do something with request with ID', id); respond(res, id); } function respond(res, id) { console.log('Responding to request with ID', id); res.end('Response'); } var server = http.createServer(function handleRequest(req, res){ var id = uuid(); doSomething(res, id); }); server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
  8. 21 ASSIGN A REQUEST ID HERE AND HAVE IT SENT

    ON TO ALL OTHER SERVICES WHICH MEANS ALL YOUR LOGS FROM ALL YOUR SERVICES ALL TIE TOGETHER
  9. “Continuation-local storage works like thread-local storage in threaded programming, but

    is based on chains of Node-style callbacks instead of threads.” 26
  10. IT MEANS YOU CAN STOP DOING THIS… 27 var http

    = require('http'); var uuid = require('uuid'); function respond(res) { console.log('Got request with ID', res.id); res.end('Response'); } var server = http.createServer(function handleRequest(req, res){ res.id = uuid(); respond(res); }); server.listen(8080, function(){ console.log("Server listening on: http://localhost:8080"); });
  11. AND START DOING THIS… 28 var http = require('http'); var

    uuid = require('uuid'); var createNamespace = require('continuation-local-storage').createNamespace; var namespace = createNamespace('request-life'); function respond(res) { console.log('Got request with ID', namespace.get('requestId')); res.end('Response'); } var server = http.createServer(function handleRequest(req, res){ namespace.set('requestId', uuid()); respond(res); }); server.listen(8080, function(){ console.log('Server listening on: http://localhost:8080'); });
  12. 1 31 var createNamespace = require('continuation-local-storage').createNamespace; var session = createNamespace('my

    session'); var db = require('./lib/db.js'); function start(options, next) { db.fetchUserById(options.id, function (error, user) { if (error) return next(error); session.set('user', user); next(); }); }
  13. 2 32 var getNamespace = require('continuation-local-storage').getNamespace; var session = getNamespace('my

    session'); var render = require('./lib/render.js') function finish(response) { var user = session.get('user'); render({user: user}).pipe(response); }
  14. 39 var http = require('http'); var shimmer = require('shimmer'); shimmer.wrap(http,

    'request', function (original) { return function () { console.log("Starting request!"); var returned = original.apply(this, arguments) console.log("Done setting up request -- OH YEAH!"); return returned; }; });
  15. 42

  16. 44 // Copyright (c) 2015. David M. Lee, II 'use

    strict'; var shimmer = require('shimmer'); // require mysql first; otherwise you can get some bizarre // "object is not a function" errors if cls-mysql is loaded first. require('mysql'); var Protocol = require('mysql/lib/protocol/Protocol'); var Pool = require('mysql/lib/Pool'); module.exports = function(ns) { shimmer.wrap(Protocol.prototype, '_enqueue', function(enqueue) { return function(sequence) { sequence._callback = ns.bind(sequence._callback); return enqueue.call(this, sequence); }; }); shimmer.wrap(Pool.prototype, 'getConnection', function(getConnection) { return function(cb) { return getConnection.call(this, ns.bind(cb)); }; }); };
  17. IT ALSO HAS A PERFORMANCE COST THE MORE YOU HAVE,

    THE MORE YOU’LL IMPACT YOUR PERFORMANCE COST IS AGAINST A NAMESPACE, NOT DATA IN THE NAMESPACE KEEP THINGS TO A MINIMUM 50
  18. IF YOU’RE DOING IT RIGHT… YOU SHOULD BE USING SHRINKWRAP

    ANYWAY (OR ANOTHER TECHNIQUE TO LOCK DEPENDENCIES) SO BREAKAGES SHOULD BE LIMITED TO YOUR DEVELOPMENT ENVIRONMENT WHICH MEANS MOAR BONUS FOR LITTLE TO NO RISK VERY LOW PERFORMANCE COST 52