Wednesday, February 22, 2017

5 steps to making a Node.js frontend app 10x faster

Step 1: Parallelize Everything

In order to render the HTML page for any dashboard, the node.js application needs to retrieve a lot of data for the dashboard in question.
At minimum this means it needs to retrieve the data from the user’s current browsing session to check they’re logged in and it needs to pull in data about the user (e.g. the user’s name, which sites they have access to, their API key and the parameters of their GoSquared subscription), and about the site in question for the dashboard (site name, unique token etc).
In order to retrieve this data, the application needed to make several calls to internal API functions, many of which could take up to 2 seconds to complete. Each request was made by a separate Express middleware, which meant they were running in series. Each request would wait for the previous one to complete before starting.
Since node.js is perfectly suited to running multiple asynchronous functions in parallel, and since a lot of these internal API requests didn’t depend on each other, it made sense to parallelize them — fire off all the requests at once and then continue once they’ve all completed. We achieved this with the aid of the (incredibly useful) async module:
So instead of:
app.use(getUser);
app.use(getSiteList);
app.use(getCurrentSite);
app.use(getSubscription);
… we could do something like this:
function parallel(middlewares) {
  return function (req, res, next) {
    async.each(middlewares, function (mw, cb) {
      mw(req, res, cb);
    }, next);
  };
}

app.use(parallel([
  getUser,
  getSiteList,
  getCurrentSite,
  getSubscription
]));
Straight away this cut our average response time down from 10 seconds to roughly 1.5 seconds. But we knew we could still do better.

Step 2: Cache, Cache, Cache

Even once we’d parallelized all of our internal data-fetching, loading a dashboard was still pretty slow. The reason for this was because not only was the application fetching all this data for the initial page load, it was also fetching it for a lot of subsequent JavaScript requests (at this point we were still limiting widgets in the dashboard based on GoSquared plan, so we needed to restrict who had access to which resources). And every one of these subsequent requests also had an average response time of about 1.5 seconds.
The solution to this was to cache any fetched data that wasn’t likely to change. A user isn’t going to upgrade or downgrade their GoSquared subscription in the 2 seconds it takes for the dashboard to load its JS, so there’s no point fetching subscription data again if we’ve already fetched it once.
So, we went ahead and cached all the data we could, cutting response times down from 1.5 seconds to about 500ms on any requests which already had the data cached.

Step 3: Intelligent JS and CSS loading on the front-end

The front-end of the dashboard application has a lot of interconnected components. The JavaScript for the application falls into three main parts: libraries (such as jQuery, D3 etc.), the main application core, and widgets (each widget in the application is modularised and has its own code). Code in each of these parts is edited in very different ways: libraries are barely touched and are updated at most once a month; the core is changed several times a day; widgets can vary from receiving several changes in a day to not being touched in weeks.
Originally we bundled all our libraries into the core application bundle (which was included via a script tag on the page), and all of the widgets into a secondary bundle which was loaded dynamically. This meant that even with good cache control, any tiny change we made to the core code would mean browsers would have to download all of the (unchanged) library code, or any change to one widget would require downloading all of the widgets again.
One way around this problem would be to break each individual component into its own file and include them all individually — that way any files that don’t get changed frequently can sit in the browser’s HTTP cache and not be requested. The problem with this, though, is that there would be a lot of files, some of them incredibly small. And (especially on mobile browsers), the overhead of loading that many individual resources vastly outweights the extra overhead we had before of re-downloading unchanged content.
We eventually came up with a compromise solution based on Addy Osmani’s basket.js, using a combination of server-side script concatenation and localStorage for caching. In a nutshell, the page includes a lightweight loader script, which figures out which JS and CSS it has already cached and which needs to be fetched. The loader then requests all the resources it needs from the server in one request, and saves all the resources into localStorage under individual keys. This gives us a great compromise between cutting down the number of HTTP requests while still being able to maintain cacheability, and not re-downloading code unnecessarily when it hasn’t changed. Addtionally, after running a few benchmarks, we found that localStorage is (sometimes) actually faster than the native HTTP cache, especially on mobile browsers.
Along with this, we also switched all of our static (JS and CSS) asset loading to be served through CloudFront, Amazon Web Service’s content delivery network. This means content is served from the nearest possible geographic location to the user, cutting down request latency from as high as 2500ms (in places such as Singapore) to tens of milliseconds.
We also introduced some optimizations to prevent loading or storing duplicate code. For example, the Languages widget uses exactly the same code in Now, Trends and Ecommerce. By de-duplicating the caching and requests based on a digest of each resource’s contents, we were able to cut out unnecessary requests and storage.
With these intelligent changes to resource loading we were able to cut down the total number of HTTP requests necessary to render the dashboard to one (just the page itself), which meant that for users quickly switching between dashboards for different sites, each dashboard would load within a few seconds.
But we could do even better.

Step 4: Cut out the middle-man for fetching data

All the user, site and subscription data described in the first two steps was being fetched via a secure internal HTTP API to our internal account system, which at the time was written in some old, clunky, slow PHP. As part of our extensive rewrite of that whole system from PHP to Node, we were also able to cut out the internal HTTP component completely, instead including a node module directly in the dashboard application and requesting our databases directly. This allowed us much finer-grained control over exactly what data we were fetching, as well as eliminating a huge amount of overhead.
With this significant change, we were able to reduce our average response time (even without the caching described in Step 2), to 25ms.

Step 5: Do More on the Client

Thanks to all the changes we’d made up to this point, all that was different between different dashboards for different sites was a config object passed to the loader on initialization. It didn’t make sense, therefore, to be reloading the entire page when simply switching between sites or between Now and Trends, if all of the important resources had already been loaded. With a little bit of rearranging of the config object, we were able to include all of the data necessary to load any of the dashboards accessible to the user. Throw in some HTML5 History with pushState and popState, and we’re now able to switch between sites or dashboards without making a single HTTP request or even fetching scripts out of the localStorage cache. This means that switching between dashboards now takes a couple of hundred milliseconds, rather than several seconds.

What else?

So far all this has been about reducing load times and getting to a usable dashboard in the shortest time possible. But we’ve also done a lot to optimise the application itself to make sure it’s as fast as possible. In summary:
  • Don’t use big complex libraries if you don’t have to — for example, jQuery UI is great for flexibility and working around all manner of browser quirks, but we don’t support a lot of the older browsers so the code bloat is unnecessary. We were able to replace our entire usage of jQuery UI with some clever thinking and 100-or-so lines of concise JS (we also take advantage of things like HTML5’s native drag-and-drop).
  • Even respectable libraries have their weak spots — for example we use moment with moment-timezone for a lot of our date and time handling. However moment-timezone is woefully inefficient (especially on mobile) if you’re using it a lot. With a little bit of hacking we added a few optimizations of our own and made it much better for our use-case.
  • Slow animations make everything feel slow — a lot of studies have been posted about this in the past, and it really makes a difference. Simply reducing some CSS transition times from 500ms to 250ms, and cutting others out entirely, made the whole dashboard feel snappier and more responsive.
  • Instant visual feedback — one of the big things we found when using Trends was that switching between time frames just felt slow. It took under a second, but because there was a noticeable delay between clicking on the timeframe selector and anything actually happening, things felt broken. Fetching new data from our API is always going to take some time — it’s not going to be instant. So instead we introduced the loading spinner on each widget. Nothing is actually any faster, but the whole experience feels more responsive. There is immediate visual feedback when you click the button, so you know it’s working properly.
  • Flat design is actually really handy for performance — it may well just be a design trend, but cutting out superficial CSS gradients and box shadows does wonders for render performance. If the browser doesn’t have to use CPU power to render all these fancy CSS effects, you get an instant boost to render performance.
Now dashboard in action

What next?

Even after all these optimizations and tweaks, we’re well aware that there’s still plenty of room for improvement. Especially on mobile, where CPU power, memory, rendering performance, latency and bandwidth are all significantly more limited than they are on the desktop. We’ll continue improving everything we can, so stay tuned for further updates!

Tuesday, February 21, 2017

node.js jade view engine tutorials

/*views/shared/root.jade*/
doctype html
html(lang="en")
    head
        block head
            block title
                title Default Title
            meta(name="viewport", content="width=device-width, initial-scale=1.0")
            link(href='/#{loader.theme_favicon("default")}', rel='icon', type='image/png')
            link(href='/#{loader.theme_static("css/styles.css")}', rel='stylesheet', type='text/css')
            script(src='/#{loader.theme_static("js/scripts.js")}')
    body
        #body-wrapper
            block body

/*views/shared/site.jade*/
 
extends root
block body
    block variables
    div okey=#{body_class}
    #content-wrapper
        block content
    #footer-wrapper
        block footer
            - var imageName = new Date().getFullYear();
            span Copyright #{imageName} <br>


/*views/home/index.jade*/

extends ../shared/site
block variables
    -var body_class = 'i-am-the-home-page'
block append head
    script(src='/#{loader.theme_static("js/scripts123.js")}')
block title
    title #{model.title}
block content
    h1 Post's Header
    p Here's a list of all blog posts
mixin list-item
block footer
    //block comment
    h1 hello world
    //-invisible block comment
    h2 how are you?
    mixin header(title)
        div.container&attributes(attributes)
            h3.sectionHeader #{title}
            div.content(style="display: block")
                block
    +header('footer')(style="text-align:center; display: block; width: 500px;")
        p this is a footers paragraph
    -var jade_obj = require('jade');   
    -var list_view = loader.view_filename('login/partial/list.jade', router.langdir);   
    -if (loader.file_exists(list_view)) {
        -var html_str = jade_obj.renderFile(list_view, {list: model.list});
        div !{html_str}
    -} else {
        include ./partial/list
    -}


/*views/home/partial/list.jade*/

-var items = (list !== undefined ? list : model.list);
ul
    mixin htmlli(liitem)
        li(id="#{liitem}")= '!' + liitem
    -each item, i in items
        -if (items.length > 0)
            +htmlli(i + '. ' + item)
        -else
            include ./item

/*views/home/partial/item.jade*/

li= item

Friday, February 17, 2017

Node.js - use of module.exports as a constructor, Essential Node.js patterns and snippets

/***************************/ constructor version first

1.

function Login(base, req, res) {
    this._base = base;
    this._req = req;
    this._res = res;
    this.title = 'Login_title';
    this.description = 'Login_description';
}
Login.prototype = {
    constructor: Login,
    description: 'default_description',
    auth: function (args) {
        this._res.send(this.title + " => " + args + " , " + this.description);
    }
};
module.exports = Login;

/**************************/ constructor version second

2.

 class Login {
    constructor(base, req, res) {
        this.base = base;
        this.req = req;
        this.res = res;
        this.foo = 10;
    }
    auth(args) {
        this.res.json(this.foo + ';;;' + args);
    }
}
module.exports = Login;

****************************************************


var _constructor = require('./login');
var _object = new _constructor(base, req, res);
this.function = "auth";

if (typeof _object[this.function] === 'function') {
         _object[this.function]("passed args");
}


***********************************************************

1.2 Accessing global values from objects

// constructor
var Class = function(global, value2) {
  this.global = global;
}
// access using this.global in class methods

1.3 Factory pattern

// Constructor
var Class = function(value1, value2) { ... }
// Factory
Class.factory(value1) { return new Class(value1, "aaa"); }
// properties and methods
Class.prototype = { ... };

1.4 Sharing state between modules

var Common = {
  util: require('util'),
  fs:   require('fs'),
  path: require('path')
};

module.exports = Common;

// in other modules
var Common = require('./common.js');

1.5 Singleton class (added Feb 2011)

var Singleton = (function() {
   var private_variable = 'value';
   function private_function() {
      ...
   }
   function public_function() {
      ...
   }
  return {
      public_function: public_function
  };
})();




Saturday, February 11, 2017

node.js clustered express app with socket.io chat with redis-server boost

var express = require('express'),
        cluster = require('cluster'),
        net = require('net'),
        socketio = require('socket.io'),
        socket_redis = require('socket.io-redis');

var port = 80, num_processes = require('os').cpus().length;

if (cluster.isMaster) {
    // This stores our workers. We need to keep them to be able to reference
    // them based on source IP address. It's also useful for auto-restart,
    // for example.
    var workers = [];

    // Helper function for spawning worker at index 'i'.
    var spawn = function (i) {
        workers[i] = cluster.fork();

        // Optional: Restart worker on exit
        workers[i].on('exit', function (code, signal) {
            console.log('respawning worker', i);
            spawn(i);
        });
    };

    // Spawn workers.
    for (var i = 0; i < num_processes; i++) {
        spawn(i);
    }

    // Helper function for getting a worker index based on IP address.
    // This is a hot path so it should be really fast. The way it works
    // is by converting the IP address to a number by removing non numeric
    // characters, then compressing it to the number of slots we have.
    //
    // Compared against "real" hashing (from the sticky-session code) and
    // "real" IP number conversion, this function is on par in terms of
    // worker index distribution only much faster.
    var worker_index = function (ip, len) {
        var s = '';
        for (var i = 0, _len = ip.length; i < _len; i++) {
            if (!isNaN(ip[i])) {
                s += ip[i];
            }
        }
        return Number(s) % len;
    };

    // Create the outside facing server listening on our port.
    var server = net.createServer({pauseOnConnect: true}, function (connection) {
        // We received a connection and need to pass it to the appropriate
        // worker. Get the worker for this connection's source IP and pass
        // it the connection.
        var worker = workers[worker_index(connection.remoteAddress, num_processes)];
        worker.send('sticky-session:connection', connection);
    }).listen(port);

} else {
    /* don't use yet
     * var app = require('express')();
     var server = require('http').Server(app);
     var io = require('socket.io')(server);
     var redis = require('socket.io-redis');
     var http = app.listen(80, function () {
     console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
     });*/
    // Note we don't use a port here because the master listens on it for us.
    var app = new express();

    // Here you might use middleware, attach routes, etc.
    app.use('/assets', express.static(__dirname + '/public'));
    app.get('/', function (req, res) {
        res.sendFile(__dirname + '/index.html');
    });

    // Don't expose our internal server to the outside.
    var server = app.listen(),
            io = socketio(server);

    // Tell Socket.IO to use the redis adapter. By default, the redis
    // server is assumed to be on localhost:6379. You don't have to
    // specify them explicitly unless you want to change them.
    io.adapter(socket_redis({host: 'localhost', port: 6379}));

    var online_members = 0;
    var chat_socket = io.of('/chat')
            .on('connection', function (client_socket) {
                var log = process.pid + ', ' + client_socket.id + ' : Connected';
                console.log(log);

                client_socket.on('login', function (data) {
                    online_members = online_members + 1;

                    client_socket.emit('ilogged', {id: client_socket.id, pid: process.pid, online_members: online_members});
                    client_socket.broadcast.emit('ulogged', {id: client_socket.id, pid: process.pid, online_members: online_members});

                    log = process.pid + ', ' + client_socket.id + ' : Logged';
                    console.log(log);
                });

                client_socket.on('message', function (data) {
                    var msg = process.pid + " : " + data;
                    client_socket.broadcast.emit('message', msg);

                    log = process.pid + ' : ' + data;
                    console.log(log);
                });

                client_socket.on('disconnect', function () {
                    online_members = online_members - 1;
                    client_socket.broadcast.emit('uclosed', {id: client_socket.id, pid: process.pid, online_members: online_members});

                    log = process.pid + ', ' + client_socket.id + ' : Disconnected';
                    console.log(log);
                });
            });

    console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');

    // Listen to messages sent from the master. Ignore everything else.
    process.on('message', function (message, connection) {
        if (message !== 'sticky-session:connection') {
            return;
        }

        // Emulate a connection event on the server by emitting the
        // event with the connection the master sent us.
        server.emit('connection', connection);

        connection.resume();
    });
}



scripts



var chat_client_start = function () { var logged = null; var chat_client = io.connect('http://BOR-PC/chat'); chat_client.on('connect', function () { $('#messages').append($('
  • ').text('Logging...')); if (logged === null) { chat_client.emit("login", "I am logging..."); } }); chat_client.on('ilogged', function (data) { logged = data; var txt = "I am Logged: " + data.pid + ', ' + data.id + ', c:' + data.online_members; $('#messages').append($('


  • ').text(txt)); }); chat_client.on('ulogged', function (data) { var txt = "User Logged: " + data.pid + ', ' + data.id + ', c:' + data.online_members; $('#messages').append($('
  • ').text(txt)); }); chat_client.on('uclosed', function (data) { var txt = "User Closed: " + data.pid + ', ' + data.id + ', c:' + data.online_members; $('#messages').append($('
  • ').text(txt)); }); chat_client.on('message', function (msg) { $('#messages').append($('
  • ').text(msg)); }); chat_client.on('disconnect', function () { logged = null; // chat_client.disconnect(); // chat_client.removeAllListeners('connect'); $('#messages').append($('
  • ').text('Server disconnected.')); $('#messages').append($('
  • ').text('Reconnecting...')); setTimeout(function () { chat_client_start(); }, 5000); }); $('form').submit(function () { var msg_txt = $('#msg_txt').val(); $('#messages').append($('
  • ').text(msg_txt)); chat_client.emit('message', msg_txt); $('#msg_txt').val(''); return false; }); }; chat_client_start();
  •  

    Wednesday, February 8, 2017

    how to create a user in oracle 11g and grant permissions, account unlock

    CREATE USER username IDENTIFIED BY password;
    GRANT CONNECT TO username;
    GRANT SELECT,INSERT,UPDATE,DELETE on schema.table TO username;
     esvel
    GRANT EXECUTE on schema.procedure TO username;
    esvel
    GRANT ALL privileges TO username;


    CREATE USER <<username>> IDENTIFIED BY <<password>>; -- create user with password
    GRANT CONNECT,RESOURCE,DBA TO <<username>>; -- grant DBA,Connect and 
    Resource permission to this user(not sure this is necessary 
    if you give admin option)
    GRANT CREATE SESSION TO <<username>> WITH ADMIN OPTION; --Give admin option to user
    GRANT UNLIMITED TABLESPACE TO <<username>>; -- give unlimited tablespace grant

    http://stackoverflow.com/questions/9447492/how-to-create-a-user-in-oracle-11g-and-grant-permissions



    Resolving ORACLE ERROR:ORA-28000: the account is locked

    After installation of Oracle10g, there was a problem ..couldnt login using SQL+. None of the accounts(scott/tiger) worked . At last a quick web search gave the solution . Here is what it is:

    From your command prompt, type
    sqlplus "/ as sysdba"
    Once logged in as SYSDBA, you need to unlock the SCOTT [or maybe SYSTEM] account
    SQL> alter user scott account unlock;
    SQL> grant connect, resource to scott;


    example in my PC: 

    Microsoft Windows [Version 6.1.7601]
    Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

    C:\Users\BOR>sqlplus "/ as sysdba"

    SQL*Plus: Release 11.2.0.1.0 Production on Tue Feb 14 08:56:49 2017

    Copyright (c) 1982, 2010, Oracle.  All rights reserved.


    Connected to:
    Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
    With the Partitioning, OLAP, Data Mining and Real Application Testing options

    SQL> alter user SYSTEM unlock;
    alter user SYSTEM unlock
                      *
    ERROR at line 1:
    ORA-00922: missing or invalid option


    SQL> ALTER USER SYSTEM ACCOUNT UNLOCK;

    User altered.

    SQL> ALTER USER SYSTEM IDENTIFIED BY password;

    User altered.

    SQL>