Jan-Henrik,
So I converted dbmail to use libzdb, and use prepared statements.
But I can't seem to get prepared statements to work for postgresql.
Sqlite and mysql work as expected by postgres keeps throwing exceptions.
My test code reads:
//URL_T url = \ URL_new("postgresql://localhost:5432/dbmail?user=dbmail&password=dbmail"); URL_T url = URL_new("postgresql://dbmail:dbmail@localhost/dbmail"); ConnectionPool_T pool = ConnectionPool_new(url); Connection_T con = ConnectionPool_getConnection(pool); PreparedStatement_T p = Connection_prepareStatement(con, "SELECT * FROM dbmail_users WHERE userid=?");
THis keeps throwing exception at:
Uncaught exception SQLException raised in Connection_prepareStatement at src/db/Connection.c:294
On the postgres side all I see is:
2008-02-23 09:46:48 CET DEBUG: parse 1804289383: SELECT * FROM dbmail_users WHERE userid=$1 2008-02-23 09:46:48 CET DEBUG: StartTransactionCommand 2008-02-23 09:46:48 CET DEBUG: StartTransaction 2008-02-23 09:46:48 CET DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 2270118/1/0, nestlvl: 1, children: <> 2008-02-23 09:46:48 CET DEBUG: CommitTransactionCommand 2008-02-23 09:46:48 CET DEBUG: CommitTransaction 2008-02-23 09:46:48 CET DEBUG: name: unnamed; blockState: STARTED; state: INPROGR, xid/subid/cid: 2270118/1/1, nestlvl: 1, children: <> 2008-02-23 09:46:48 CET LOG: unexpected EOF on client connection
Any ideas?
To add to the mystery, the pool.c unit test runs fine against the very same url.
Paul J Stevens wrote:
Jan-Henrik,
So I converted dbmail to use libzdb, and use prepared statements.
But I can't seem to get prepared statements to work for postgresql.
Sqlite and mysql work as expected by postgres keeps throwing exceptions.
My test code reads:
//URL_T url = \ URL_new("postgresql://localhost:5432/dbmail?user=dbmail&password=dbmail"); URL_T url = URL_new("postgresql://dbmail:dbmail@localhost/dbmail"); ConnectionPool_T pool = ConnectionPool_new(url); Connection_T con = ConnectionPool_getConnection(pool); PreparedStatement_T p = Connection_prepareStatement(con, "SELECT * FROM dbmail_users WHERE userid=?");
THis keeps throwing exception at:
Uncaught exception SQLException raised in Connection_prepareStatement at src/db/Connection.c:294
On the postgres side all I see is:
2008-02-23 09:46:48 CET DEBUG: parse 1804289383: SELECT * FROM dbmail_users WHERE userid=$1 2008-02-23 09:46:48 CET DEBUG: StartTransactionCommand 2008-02-23 09:46:48 CET DEBUG: StartTransaction 2008-02-23 09:46:48 CET DEBUG: name: unnamed; blockState: DEFAULT; state: INPROGR, xid/subid/cid: 2270118/1/0, nestlvl: 1, children: <> 2008-02-23 09:46:48 CET DEBUG: CommitTransactionCommand 2008-02-23 09:46:48 CET DEBUG: CommitTransaction 2008-02-23 09:46:48 CET DEBUG: name: unnamed; blockState: STARTED; state: INPROGR, xid/subid/cid: 2270118/1/1, nestlvl: 1, children: <> 2008-02-23 09:46:48 CET LOG: unexpected EOF on client connection
Any ideas?
On 23. feb.. 2008, at 09.49, Paul J Stevens wrote:
My test code reads:
URL_T url = URL_new("postgresql://dbmail:dbmail@localhost/dbmail"); ConnectionPool_T pool = ConnectionPool_new(url);
You should throw in a ConnectionPool_start(pool) before getting connections from the pool. Maybe you did not post the full test program? You may also want to catch the exception and use Connection_getLastError() to see if that says anything. If the problem persist, I hope Martin can help as he is responsible for the postgres implementation.
Ps. Also run with the debug flag set, ZBDEBUG = TRUE; Not that we output much debug info, but you may be lucky. Since postgres work with pool.c this sounds strange, though pool.c of course may not test all cases. Please double check how you call libzdb if you have not already done so first.
Maybe you have a dbmail repository we can look at for the implementation?
I've tried both with ZBDEBUG=TRUE and of course with getLastError, but both tell me nothing.
And I didn't miss the getConnection part. Normal queries using executeQuery run just fine, as do prepared statements with mysql.
I'm attaching my testzdb.c which doesnt work.
My git tree with zdb related work is visible at:
http://git.dbmail.eu/?p=paul/dbmail;a=shortlog;h=zdb
the calls that talk to zdb are all in dm_db.c starting around like 150.
thanks,
Jan-Henrik Haukeland wrote:
Ps. Also run with the debug flag set, ZBDEBUG = TRUE; Not that we output much debug info, but you may be lucky. Since postgres work with pool.c this sounds strange, though pool.c of course may not test all cases. Please double check how you call libzdb if you have not already done so first.
Maybe you have a dbmail repository we can look at for the implementation?
-- To unsubscribe: http://www.tildeslash.com/mailman/listinfo/libzdb-general
Got it, sort of!
Looks like it *does* work as long as the preparedstatement is not the very first query sent. As long as a Connection_executeQuery is sent first everything is peachy.
Smells like a small initialization bug in libzdb. But you guys are better suited to find out.
Paul J Stevens wrote:
I've tried both with ZBDEBUG=TRUE and of course with getLastError, but both tell me nothing.
And I didn't miss the getConnection part. Normal queries using executeQuery run just fine, as do prepared statements with mysql.
I'm attaching my testzdb.c which doesnt work.
My git tree with zdb related work is visible at:
http://git.dbmail.eu/?p=paul/dbmail;a=shortlog;h=zdb
the calls that talk to zdb are all in dm_db.c starting around like 150.
thanks,
Jan-Henrik Haukeland wrote:
Ps. Also run with the debug flag set, ZBDEBUG = TRUE; Not that we output much debug info, but you may be lucky. Since postgres work with pool.c this sounds strange, though pool.c of course may not test all cases. Please double check how you call libzdb if you have not already done so first.
Maybe you have a dbmail repository we can look at for the implementation?
-- To unsubscribe: http://www.tildeslash.com/mailman/listinfo/libzdb-general
-- To unsubscribe: http://www.tildeslash.com/mailman/listinfo/libzdb-general
Okay, thanks we'll look into it.
A couple of comments to http://git.dbmail.eu/?p=paul/dbmail;a=shortlog;h=zdb .
URL_free must be called, unless you do not care about this memory leak since the app will be closed shortly. I'v updated the "constructor" and "desctructor" methods below.
- You are basically using one Connection which is never returned to the pool. I assume this is for testing, since this defer the purpose of a pool, also if you run in a single threaded application. Even if you only use one connection, returning it to the pool after use ensures that the connection does not go stale. If the connection has errors the pool will remove it if you run with a reaper thread, which is highly recommended. And each time you get a connection from the pool you are guaranteed that the connection is alive and connected to the database as the pool runs a ping test before it dishes out a connection.
- Apropos reaper thread. if your application is single threaded and not thread-safe nor reentrant, running a reaper thread in the pool is still completely safe since the pool itself is thread-safe and the reaper-thread will not affect your application at all. If dbmail will use tcp database connections, i.e. mysql or postgres then using a reaper thread is strongly recommended.
- Having Connection Pool as a global variable is okay, but Connection and ResultSet!?, but I assume this is also for testing.
- We could add a Connection_vexecuteQuery(T C, const char *sql, va_list ap) if you want to save those snprintf() calls to build the query and instead call executeQuery directly in the facade function db_query(), or you could skip the facade and call Connection_executeQuery() directly.
- If you compiled libzdb with exception (highly recommended) you need to enclose calls that can throw an exception, (basically all methods in Connection, PreparedStatement and ResultSet), in a try-catch statement. Below is an example of a mini-framework we use with libzdb and where we isolate database call and exception testing to a Service function, which may be helpful to take a look at.
int db_connect(void) { URL_T url = URL_create("%s://%s:%u/%s?user=%s&password=%s", _db_params->driver, _db_params->host, _db_params->port, _db_params->db, _db_params->user, _db_params->pass); if (! (pool = ConnectionPool_new(url))) { if (url) Url_free(&url); TRACE(TRACE_FATAL,"error creating connection pool"); return -1; }
ConnectionPool_start(pool); TRACE(TRACE_DEBUG, "connection pool started with [%d] connections", ConnectionPool_getInitialConnections(pool)); // Humm if (! (conn = ConnectionPool_getConnection(pool))) { TRACE(TRACE_FATAL, "error getting connection from the pool"); return -1; }
return 0; }
int db_disconnect(void) { URL_T url = ConnectionPool_getURL(pool); ConnectionPool_stop(pool); ConnectionPool_free(&pool); URL_free(&url); return 0; }
// Mini framework dispatcher example
struct T { Connection_T con; void (*service)(T t); ... };
// Dispatch database functions static void dispatcher(...) { struct T t = {.service = list}; if (...) { t.service = create; } else if (...) { t.service = update; } else if (...) { t.service = delete; } Service_do(&t); } ... static void update(T t) { int i; params_t params = t->model; PreparedStatement_T p = Connection_prepareStatement(t->con, SQL_UPDATE); for (i = 0; params[i].name; i++) PreparedStatement_setString(p, i, params[i].value); PreparedStatement_execute(p); }
... void Service_do(T t) { assert(t->service); if (! (t->con= ConnectionPool_getConnection(Connection_Pool))) { sendError(t, SC_INTERNAL_SERVER_ERROR, "SQLException: Connection not available\n"); return; } TRY { Connection_beginTransaction(t->con); t->service(t); Connection_commit(t->con); } CATCH(SQLExeption) { sendError(t, SC_INTERNAL_SERVER_ERROR, "SQLException: %s\n", Connection_getLastError(t->con)); Connection_rollback(t->con); } FINALLY { Connection_close(t->con); } END_TRY; }
On 24. feb.. 2008, at 22.21, Paul J Stevens wrote:
Got it, sort of!
Looks like it *does* work as long as the preparedstatement is not the very first query sent. As long as a Connection_executeQuery is sent first everything is peachy.
Smells like a small initialization bug in libzdb. But you guys are better suited to find out.
Paul J Stevens wrote:
I've tried both with ZBDEBUG=TRUE and of course with getLastError, but both tell me nothing.
And I didn't miss the getConnection part. Normal queries using executeQuery run just fine, as do prepared statements with mysql.
I'm attaching my testzdb.c which doesnt work.
My git tree with zdb related work is visible at:
http://git.dbmail.eu/?p=paul/dbmail;a=shortlog;h=zdb
the calls that talk to zdb are all in dm_db.c starting around like 150.
thanks,
Jan-Henrik Haukeland wrote:
Ps. Also run with the debug flag set, ZBDEBUG = TRUE; Not that we output much debug info, but you may be lucky. Since postgres work with pool.c this sounds strange, though pool.c of course may not test all cases. Please double check how you call libzdb if you have not already done so first.
Maybe you have a dbmail repository we can look at for the implementation?
Jan-Henrik Haukeland wrote:
Okay, thanks we'll look into it.
A couple of comments to http://git.dbmail.eu/?p=paul/dbmail;a=shortlog;h=zdb ..
Thanks for taking some time to look at my code. You're quite correct about all those globals. That code branch is very much work in progress. Up until very recently dbmail used a preforking model, with a single connector per process. However, with many concurrent client connections (mostly imap) that model was no longer tennable. So I refactored the server code last month to use libevent within a single process. This scales much better for network io and now the database io then became the main bottleneck.
Ultimately (code complete before the summer) plan is:
- replace the homegrown database modules with a generic layer (libzdb) still using a single connectionpool/connector/resultset etc. That is what I'm working on atm. - next, refactor the database layer to use local connections and result sets, so we can pull a connection from the pool and return it once we're done with it. - finally, start doing the command processing in threads so the database calls don't block the main thread anymore.
I expect and hope that by combining libevent and libzdb we'll achieve quite an interesting potential for scaling up the imap server. Both represent very exciting technologies that fit very well with what I'm trying to do.
After the above milestones have been completed, and if sponsoring is found we might even add multiple connection pools in the mix so different imap users are mapped to different database backends.
- We could add a Connection_vexecuteQuery(T C, const char *sql,
va_list ap) if you want to save those snprintf() calls to build the query and instead call executeQuery directly in the facade function db_query(), or you could skip the facade and call Connection_executeQuery() directly.
Thanks for the offer. I'll give it some thought.
- If you compiled libzdb with exception (highly recommended) you need
to enclose calls that can throw an exception, (basically all methods in Connection, PreparedStatement and ResultSet), in a try-catch statement. Below is an example of a mini-framework we use with libzdb and where we isolate database call and exception testing to a Service function, which may be helpful to take a look at.
I'll make sure the debian packages are build with exceptions and protected. And I definitely plan on leveraging exceptions in dbmail.
On 25. feb.. 2008, at 09.42, Paul J Stevens wrote:
- finally, start doing the command processing in threads so the
database calls don't block the main thread anymore.
Yes, in a single threaded non-blocking i/o server, you must conduct database operations in a thread to avoid halting the server. The trick though is to do mostly CPU bound operations in the database thread and leave i/o bound operations to the libevent main thread. That way, it is possible to have a few threads in a pool handling many connections. But I guess you know that. I just completed a HTTP application server that does just that for HTTP requests/responses with database access, and I'm also using libevent and of course zdb. This architecture scales very well.
I expect and hope that by combining libevent and libzdb we'll achieve quite an interesting potential for scaling up the imap server. Both represent very exciting technologies that fit very well with what I'm trying to do.
After the above milestones have been completed, and if sponsoring is found we might even add multiple connection pools in the mix so different imap users are mapped to different database backends.
Good catch, I was about to suggest that. Its not obvious that it is possible to use several Connection Pool instances against different database back ends.
Good luck with the project!