Skip to content

Instantly share code, notes, and snippets.

@drocco007
Last active May 27, 2018 09:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drocco007/6e44ac1a581546c16e67 to your computer and use it in GitHub Desktop.
Save drocco007/6e44ac1a581546c16e67 to your computer and use it in GitHub Desktop.
Postgres/WebSocket demo
import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
import eventlet
from eventlet import wsgi
from eventlet import websocket
from eventlet.hubs import trampoline
dsn = "" # customise this
def dblisten(q):
"""
Open a db connection and add notifications to *q*.
"""
cnn = psycopg2.connect(dsn)
cnn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cur = cnn.cursor()
cur.execute("LISTEN data;")
while 1:
trampoline(cnn, read=True)
cnn.poll()
while cnn.notifies:
n = cnn.notifies.pop()
q.put(n)
@websocket.WebSocketWSGI
def handle(ws):
"""
Receive a connection and send it database notifications.
"""
q = eventlet.Queue()
eventlet.spawn(dblisten, q)
while 1:
n = q.get()
ws.send(unicode(n.payload))
def dispatch(environ, start_response):
if environ['PATH_INFO'] == '/data':
return handle(environ, start_response)
else:
start_response('200 OK',
[('content-type', 'text/html')])
return [page]
page = """
<html>
<head><title>pushdemo</title>
<!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> -->
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<style type="text/css">
.bar {width: 20px; height: 20px;}
</style>
<script>
window.onload = function() {
ws = new WebSocket("ws://localhost:7000/data");
ws.binaryType = "arraybuffer";
ws.onmessage = function(msg) {
bar = $('#' + msg.data);
bar.width(bar.width() + 10);
}
}
</script>
</head>
<body>
<div style="width: 400px;">
<div id="red" class="bar"
style="background-color: red;">&nbsp;</div>
<div id="green" class="bar"
style="background-color: green;">&nbsp;</div>
<div id="blue" class="bar"
style="background-color: blue;">&nbsp;</div>
</div>
</body>
</html>
"""
if __name__ == "__main__":
listener = eventlet.listen(('127.0.0.1', 7000))
wsgi.server(listener, dispatch)

Postgres/WebSocket Demo

This is the runnable version of the Postgres/WebSocket idea that I presented in my PyOhio talk Pushy Postgres and Python. It is a slightly updated version of Daniele Varrazzo's example code from his blog post PostgreSQL notifications with Psycopg2 and Eventlet. If you want to try it out yourself,

  1. Make sure you have a Postgres database you can connect to
  2. Edit pushdemo.py and update the variable dsn to point to your database

    dsn = 'dbname=testdb user=postgres password=secret'

    You do not need to create a separate database for this demo.

  3. pip install psycopg2 eventlet
  4. python pushdemo.py
  5. Open a browser to http://localhost:7000. You should see a stack of three small color bars.
  6. Open a shell connection to the database:

    psql testdb postgres
  7. Notify!

    NOTIFY data, 'blue';
    NOTIFY data, 'green';

Notes, for the Curious

The only significant change I made to the original demo is on line 35:

ws.send(unicode(n.payload))

I added the Unicode conversion due to a defect in Eventlet's API: without this conversion, Eventlet treats the payload as binary, which the client side of the WebSocket is not prepared to handle.


The demo uses Eventlet's trampoline__ instead of the standard library select__ call that I presented in the case study, so the blocking call:

select.select([conn], [], [], 10) != ([], [], [])

is spelled with the Eventlet-specific (line 20):

trampoline(cnn, read=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment