Real Time Notifications

With Socket.io and Postgres

@GoodCodeHQ - by Deni Bertovic / @denibertovic

Stack

  1. Python/Django (Gunicorn)
  2. Nginx (proxy)
  3. Postgres

Requirements

  1. A user saves a Model on the "Frontend"
  2. Notify all the clients(browsers) connected to the Admin interface

Options

  1. Temp Table/Flag and Polling
  2. Server Sent Events
  3. Websockets (Gevent???)
  4. Socket.io

Socket.io

  1. Node.js
  2. Redis??!!
  3. Python code modifications

Already using Postgres so...

  1. NOTIFY
  2. LISTEN
  3. UNLISTEN

Postgres NOTIFY

NOTIFY channel [ , payload ]

Or just

pg_notify(text, text)

Node/Socket.io

LISTEN channel;
UNLISTEN channel;

Socket.io

						
function initPostgresPubSub(socket) {
    socket.on('subscribe', function(channel) {
      socket.join(channel);
      console.log("New socket joined...");
      if (!listening.hasOwnProperty(channel)) {
        // only create new connection and new listeners for new
        // channels
        var client = new pg.Client(pgConnectionString);
        client.connect();
        client.query('LISTEN "'+channel+'"');
        listening[channel] = client;
        client.on('notification', function(data) {
          try {
            var obj = JSON.parse(data.payload);
            io.sockets.in(channel).emit('notification', obj);
          } catch(e) {
            console.log(e);
            console.log(data.payload);
          }
        });
      }
    });
  }
						
					

Browser

						
'startSocket': function() {
    var self = this;
    this.socket = io.connect(this.options.socketServer, {
        transports: ['websocket', 'xhr-multipart', 'xhr-polling', 'jsonp-polling']
    });

    this.socket.on('connect', function() {
        // here we subscribe to the unique channel for notifications only for this particular Admin
        self.socket.emit('subscribe', 'watchers_' + self.options.SOME_UNIQUE_ID );
    });

    this.socket.on('notification', function(notification) {
        var update = new APP.Model.Update(notification);
        self.options.updates.splice(0, 0, update);
        console.log('New update: ', arguments);
    });
 },
						
					

Postgres (BEHOLD)

						
CREATE OR REPLACE FUNCTION notify_my_table_insert() RETURNS trigger AS $$
DECLARE
BEGIN
  PERFORM pg_notify('watchers_' || NEW.id, row_to_json(NEW)::TEXT);
  RETURN new;
END;
$$ LANGUAGE plpgsql;


DROP TRIGGER IF EXISTS my_insert_trigger ON my_table;

CREATE TRIGGER my_insert_trigger AFTER INSERT ON my_table
FOR EACH ROW EXECUTE PROCEDURE notify_my_table_insert();
						
					

Features

  1. Transaction safe
  2. No duplicates
  3. Delivery Guarantee (to listening clients)
  4. Ordering Guarantee
  5. No need to change core App logic

Notice

That we didn't have to do any Python.

Drawbacks

  • Notifications are not persistent *

Thank you!