mod_annot editor

Annotate Section

Dynamic Resource Pools: apr_reslist

Implementation of connection pooling in practice is straightforward. An API for maintaining a dynamic respource pool is already provided by the apr_reslist module. All we need to do is provide a constructor and destructor for our resource, and functions to retrieve an instance of our resource from the pool, and release it back to the pool. The rest is managed by apr_reslist.

To illustrate this, here are the core functions of the MySQL implementation mod_mysql_pool. First, a constructor and destructor. We never call these directly; apr_reslist calls them when required.

/* an apr_reslist_constructor for MySQL connections
   Opens a MySQL connection and stores the handle in *db
static apr_status_t mysqlpool_construct(void** db, void* params, apr_pool_t* pool) {
  svr_cfg* svr = (svr_cfg*) params ;
  MYSQL* sql = NULL ;
  sql = mysql_init(sql) ;
  if ( sql == NULL ) {
    ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, pool, "mysql_init failed") ;
    return APR_EGENERAL ;
  *db = mysql_real_connect(sql, svr->host, svr->user, svr->pass,
        svr->db, svr->port, svr->sock, 0) ;

  if ( ! *db ) {
    ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, pool, "MySQL Error: %s",
        mysql_error(sql) ) ;
    return APR_EGENERAL ;
  return APR_SUCCESS ;
static apr_status_t mysqlpool_destruct(void* sql, void* params, apr_pool_t* pool)
  mysql_close((MYSQL*)sql) ;
  return APR_SUCCESS ;

Now, we need a function to register our dynamic resource list. This is called in a post_config hook, and calls apr_reslist_create to set up a pool of MySQL connections:

static int setup_db_pool(apr_pool_t* p, apr_pool_t* plog,
        apr_pool_t* ptemp, server_rec* s) {
  svr_cfg* svr = (svr_cfg*)
        ap_get_module_config(s->module_config, &mysql_pool_module) ;

  if ( apr_reslist_create(&svr->dbpool, svr->nmin, svr->nkeep,
        svr->nmax, svr->exptime, mysqlpool_construct, mysqlpool_destruct,
        svr, p) != APR_SUCCESS ) {
    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, "MySQLPool: failed to initialise"
) ;
    return 500 ;
  apr_pool_cleanup_register(p, svr->dbpool,
        apr_pool_cleanup_null) ;
  return OK ;

Finally, we provide functions other modules can use to acquire a connection from the pool and return it. We'll just show one function here: mysql_acquire is for the commonest case, where a connection is needed for the duration of a request. Note that:

  1. By registering a cleanup on the request pool, mysql_acquire guarantees the connection is returned to the pool at the end of the request.
  2. By storing the connection handle on our own request_config, we guarantee that the same connection is returned every time sql_acquire is called within a single request.

MYSQL* mysql_acquire(request_rec* r, unsigned int flags) {
  mysql_request* req = (mysql_request*)
        ap_get_module_config(r->request_config, &mysql_module) ;
  if ( ! req ) {	/* use pool if and only if we haven't already got one */
    svr_cfg* svr = (svr_cfg*)
        ap_get_module_config(r->server->module_config, &mysql_module) ;
    req = (mysql_request*) apr_palloc(r->pool, sizeof(mysql_request) ) ;
    req->flags = flags ;
    req->dbpool = svr->dbpool ;
    if ( apr_reslist_acquire(svr->dbpool, (void**)&req->sql)
        != APR_SUCCESS ) {
      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
        "Failed to acquire MySQL connection from pool") ;
      return NULL ;
    if ( mysql_ping(req->sql) != 0 ) {
      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "MySQL: %s",
	mysql_error(req->sql) ) ;
      apr_reslist_invalidate(svr->dbpool, req->sql) ;
      return NULL ;
    ap_set_module_config(r->request_config, &mysql_module, req) ;
    apr_pool_cleanup_register(r->pool, req,
        mysql_release, apr_pool_cleanup_null) ;
  } else {
    req->flags |= flags ;  /* ensure all required cleanups happen */
  return req->sql ;

The rest is housekeeping. The full code for this module, as well as a PostgreSQL companion module, is available at Web├×ing.