
INTRODUCTION
-------------

This is pgnotify, a PostgreSQL client-side asynchronous notification
handler for Python.

Typically, asynchronous notification is used to communicate the message "I
changed this table, take a look at it to see what's new" from one PostgreSQL 
client to other interested PostgreSQL clients. 

A useful programming technique is to generate asynchronous notification in
a rule that is triggered by table updates; this way, notification happens
automatically when the table is changed and the application programmer
can't accidentally forget to do it.

Consider the following scenario:

            +---------------------+
            | postgresql client 1 |            
            +---------------------+
                      |
                      | INSERT, UPDATE, DELETE, etc.
                      V
       +---------------------------------------------+
       | postgresql database server                  |
       | (client 1's action triggers, um, a TRIGGER) |
       +---------------------------------------------+
                      |
                      | triggered procedure generates 
                      | a NOTIFY asynchronously
                      V
            +---------------------+
            | postgresql client 2 |            
            +---------------------+

Here, "postgresql client 1" may be a trading application running on a
trader's desktop in a dealing room. The trader enters a trade into the
application, which then updates a number of tables - deals, positions, 
etc. - in the database. The updates trigger a server procedure to perform 
risk analysis, say.

Suppose the risk analysis procedure determines that the trade exceeds
certain risk limits; it then generates an asynchronous notification to
"postgresql client 2", which is, say, the credit risk management system,
for follow-up.


ARCHITECTURE
-------------

From the PostgreSQL documentation, "PostgreSQL supports asynchronous
notification via the LISTEN and NOTIFY commands. A backend registers its
interest in a particular notification condition with the LISTEN command
(and can stop listening with the UNLISTEN command). All backends listening
on a particular condition will be notified asynchronously when a NOTIFY of
that condition name is executed by any backend. No additional information
is passed from the notifier to the listener. Thus, typically, any actual
data that needs to be communicated is transferred through a database
relation." 

Here, _backend_ refers to the "postgres" process executing on the
PostgreSQL database server. In contrast, _frontend_ is the application
invoking database operations over the network, e.g., Zope, or your
PyGreSQL-using Tkinter GUI application.


Here's a command line demonstration of asynchronous notification:

    pgnotify_test=> listen event;
    LISTEN
    pgnotify_test=> notify event;
    NOTIFY
    Asynchronous NOTIFY 'event' from backend with pid '456' received.
    pgnotify_test=> unlisten event;
    UNLISTEN
    pgnotify_test=>


Programmatically, a client submits LISTEN and UNLISTEN commands as ordinary
SQL queries. Subsequently, arrival of NOTIFY messages can be detected by
calling the libpq function PQnotifies().

In PyGreSQL, PQnotifies() is implemented as the method getnotify() of a
Pythonic _connection_object_. The application may invoke getnotify() after
each database operation to check for NOTIFY messages, but to ensure timely
receipt of the messages the application may need to submit queries
constantly, even empty ones. This wastes processing power and is
discouraged. 

A better way is to use select(2) to check the connection object's
underlying PostgreSQL socket for readability, then invoke getnotify(). The
socket descriptor is accessible via the libpq function PQsocket(); by
Python convention, PQsocket() becomes the Pythonic method fileno().

This better way is what pgnotify provides.

pgnotify is best illustrated by example:

- In terminal window 1:

    $ python
    Python 2.1 (#3, May 21 2001, 21:31:43) 
    [GCC 2.95.2 19991024 (release)] on freebsd4
    Type "copyright", "credits" or "license" for more information.
    >>> import pg
    >>> import pgnotify
    >>> def cb(args):
    ...     print args
    ... 
    >>> db = pg.connect('pgnotify_test')
    >>> pgn = pgnotify.pgnotify(db, 'event', cb)
    >>> pgn()
    [ pgn blocks at select() ]


- In terminal window 2:

    $ psql pgnotify_test
    Welcome to psql, the PostgreSQL interactive terminal.
    
    Type:  \copyright for distribution terms
           \h for help with SQL commands
           \? for help on internal slash commands
           \g or terminate with semicolon to execute query
           \q to quit
    
    pgnotify_test=> notify event;
    NOTIFY
    pgnotify_test=> 


- Back in terminal window 1:

    [ select() returns ]
    {'event': 'event', 'pid': 582}
    [ pgn blocks at select() again ]


- In terminal window 2 again:

    pgnotify_test=> notify stop_event;
    NOTIFY
    pgnotify_test=> 


- Over in terminal window 1:

    [ select() returns again ]
    {'event': 'stop_event', 'pid': 582}
    >>>
    >>> pgn
    <pgnotify.pgnotify instance at 0x8103c4c>
    >>>


Because pgnotify potentially blocks on select() and tests the PostgreSQL
connection's underlying socket for _readability_ only, it is best to
dedicate the connection to receiving notifications, i.e., do not use that
connection to invoke other database operations. Typically, one would run
a pgnotify instance in its own thread, as demonstrated in test_notify.py.


PLATFORM
---------

pgnotify is developed on this platform:
- FreeBSD 4.0
- Python 2.1
- PostgreSQL 7.1.1
- PyGreSQL 3.2

At present, pgnotify works with PyGreSQL only. It should work with PoPy and
psycopg when those modules provide the necessary Pythonic interfaces to
PQnotifies() and PQsocket(), as described above in ARCHITECTURE.


---------------------------------------------------------------------------
$Id: README,v 1.2 2001/06/08 17:44:18 ngps Exp ngps $

