Re: rfc - libpq extensions - Mailing list pgsql-general

From Iker Arizmendi
Subject Re: rfc - libpq extensions
Date
Msg-id 20030110142356.3e648f39.iker@research.att.com
Whole thread Raw
In response to Re: rfc - libpq extensions  (Neil Conway <neilc@samurai.com>)
List pgsql-general
First, consider a motivating example. A single-threaded event-driven
application server serves two concurrent clients, client-1 and client-2,
using only a single "pooled" connection.

client-1 and client-2 connect to the application server.

client-1 issues an app specific request to the app server. The app
server, which is sitting in an event loop, services client-1 by
acquiring the single available connection and issuing an asynchronous
query. The server then registers the database connection's FD for
I/O events and returns to the event loop.

client-2 issues a request. The app server wakes from the event loop and
tries to service client-2. At this point, the server cannot acquire the
database connection because it's in use (and it can't issue the query
because libpq doesn't support queued queries). Simply creating a new
database connection doesn't solve the problem since we may wish to
support a very large number of concurrent clients (which is the reason
we're using an event-driven server and connection pooling).

To get around this I thought the following extension API (implemented
using the libpq API) would help. Here's a highly simplified pseudo-code
example with error handling omitted:

==========

// Create a cache with a maximum of 1 "hard" connections
PGXconnCache* pCache = PQXcreateConnCache(1);

// Create two "soft" (or virtual) connections and associate them
// with the connection cache from which they will draw "hard"
// connections when necessary.
//

PGXconn* pConn1 = PQXconnect(pCache, connString);
PGXconn* pConn2 = PQXconnect(pCache, connString);

// Create 2 queries. Issue one on each connection.
//
// Upon calling PQXconnExecute, pConn1 goes to the "hard"
// connection cache and obtains the single available
// connection and asynchronously issues the query on it.
// The descriptor returned for PQXconnFd and registered
// with our event handling method is the descriptor
// of the underlying "hard" connection's socket.

PGXquery* pQuery1 = PQXcreateQuery(queryString1);
PQXconnExecute(pConn1, pQuery1);
register_event(PQXconnFd(pConn1), EV_READ, event_handler, pConn1);

// At this point, the single connection is in use. When
// pConn2 goes to the cache it will find it empty. So
// pConn2 queues its query and registers itself with
// the cache for a notification as soon as a "hard"
// connection becomes available.
//
// In this case, the descriptor returned by PQXconnFd is
// the descriptor for the read end of a pipe. The write
// end is what pConn2 registered with the cache. As soon
// as pConn1 returns its "hard" conn to the cache, the
// cache will write a single byte to pConn2's notification
// pipe. From an application developer's perspective, however
// this detail is transparent.

PGXquery* pQuery2 = PQXcreateQuery(queryString2);
PQXconnExecute(pConn2, pQuery2);
register_event(PQXconnFd(pConn2), EV_READ, event_handler, pConn2);

while(1)
{
    event_dispatch();
    if (PQXgetQueryState(pQuery1) == PGX_QUERY_COMPLETE))
    {
        // process results
    }
    if (PQXgetQueryState(pQuery2) == PGX_QUERY_COMPLETE))
    {
        // process results
    }

}

==========

void
event_handler(fd, void* arg)
{
    PGXconn* pConn = (PGXconn*)(arg);
    // For pConn1, the call to connEvent will result in
    // processing of events coming over the database socket.
    // Once pConn1 is done processing its current query, it
    // will return its "hard" connection to the cache. The
    // cache will then notify the waiting pConn2 pipe (by
    // writing to pConn2's pipe).
    // When the thread of control returns to the event loop
    // it will pick that event up and we'll end up back here
    // but with an event for pConn2.
    // pConn2 can then go to the cache, get the newly
    // available "hard" connection and asynchronously issue
    // its query.
    PQXconnEvent(pConn);
}

==========

// Here's a preliminary list of methods that I've started
// to implement. The descriptions/semantics are preliminary, too.

/**
 * Create hard connection cache with a maximum of maxCn
 * hard connections for any given connection string. Returns
 * a valid pointer on success, 0 otherwise.
 *
 */
extern PGXconnCache* PQXcreateConnCache(unsigned int maxCn);

/**
 * Make hard connection allocations synchronous. Subsequent
 * operations on this cache will be thread safe (TBD ?)
 *
 */
extern void PQXsetConnCacheSync(PGXconnCache* pCC);

/**
 * Release hard connection cache pCC. Any soft connections
 * are closed and their respective pending queries are cancelled.
 *
 */
extern void PQXfreeConnCache(PGXconnCache* pCC);

/**
 * Create a soft connection associated with the hard
 * connection cache pCC. The format of the connection
 * string is as described in PQconnectdb().
 *
 */
extern PGXconn* PQXconnect(PGXconnCache* pCC, const char* conninfo);

/**
 * Returns the event descriptor for the given soft connection
 * suitable for use with poll() or select().
 *
 */
extern int PQXconnFd(const PGXconn* pCn);

/**
 * Use soft connection pCn to execute the query pQ. If the
 * soft connection is associated with an actual connection
 * then query goes out immediately, otherwise it gets
 * queued for later execution. Returns 1 if query was issued,
 * 0 if it was queued, and -1 on error.
 *
 */
extern int PQXconnExecute(PGXconn* pCn, PGXquery* pQ);

/**
 * Call this method whenever there's activity on
 * the descriptor for this soft connection.
 *
 */
extern int PQXconnEvent(PGXconn* pCn);

/**
 * Closes the soft connection pCn. Any held hard connections
 * are returned to the cache from which the soft connection
 * sprang.
 *
 */
extern void PQXconnClose(PGXconn* pCn);

/**
 * Releases all resources associated with pCn. If the connection
 * is open, it is closed before being freed.
 *
 */
extern void PQXconnFree(PGXconn* pCn);

/**
 * Returns the soft connection state. If you're asynchronously
 * connecting you should register pCn's event descriptor for
 * writing when this method returns PGX_CONN_CONNECTING. Note
 * that you should always check this method after calling
 * PQXconnect since connections can sometime happen synchronously
 * when the TCP peer is local (this will save you from having
 * to register the FD for write events).
 */
extern PGXconnStateEnum PQXconnState(PGXconn* pCn);

/**
 * Returns the error message associated with a soft connection,
 * if any.
 *
 */
extern const char* PQXconnError(PGXconn* pCn);

/**
 * Create a SQL query from the given text. The
 * text is NOT checked for syntax locally but on the
 * backend server.
 *
 */
extern PGXquery* PQXcreateQuery(const char* pText);

/**
 * Releases the given query. If the query is pending
 * execution it is cancelled. If it is currently executing
 * a cancel request is issued (and the response is
 * ignored).
 *
 */
extern void PQXfreeQuery(PGXquery* pQ);

/**
 * Cancels the query - if the query is pending execution
 * then its cancelled immediately. If it's been shipped
 * off then the query state becomes CANCEL_PENDING.
 *
 * Returns 1 on success, 0 on failure.
 */
extern int PQXcancelQuery(PGXquery* pQ);

/**
 * Get a query's result object (if any)
 *
 */
extern PGresult* PQXgetQueryResult(PGXquery* pQ);

/**
 * Returns the state of the given query.
 *
 */
extern PGXqueryStateEnum PQXgetQueryState(PGXquery* pQ);

/**
 * Sets the text for query pQ provided pQ is not currently
 * executing. The query buffer for pQ is reused or expanded
 * to accomodate pText as necessary. Returns 1 on success,
 * 0 otherwise.
 *
 */
extern int PQXsetQueryText(PGXquery* pQ, const char* pText);


On Fri, 10 Jan 2003 17:15:39 +0000 (UTC)
neilc@samurai.com (Neil Conway) wrote:

> On Fri, 2003-01-10 at 12:10, Iker Arizmendi wrote:
> > As things stand now you have to wait for each query to finish before
> > issuing a new one.
>
> How exactly are you planning to fix this?
>
> Cheers,
>
> Neil
> --
> Neil Conway <neilc@samurai.com> || PGP Key ID: DB3C29FC
>
>
>
>
> ---------------------------(end of
> broadcast)--------------------------- TIP 5: Have you checked our
> extensive FAQ?
>
> http://www.postgresql.org/users-lounge/docs/faq.html

pgsql-general by date:

Previous
From: "Campano, Troy"
Date:
Subject: shell script when a user logs in
Next
From: "scott.marlowe"
Date:
Subject: Re: persistant transactions