diff -U2 -r pgbouncer-1.2.3/etc/pgbouncer.ini /home/davojan/pgbouncer/etc/pgbouncer.ini --- pgbouncer-1.2.3/etc/pgbouncer.ini 2008-08-04 16:26:55.000000000 +0400 +++ /home/davojan/pgbouncer/etc/pgbouncer.ini 2008-08-24 21:57:29.182109551 +0400 @@ -15,4 +15,16 @@ ;;; +;;; Autodb settings +;;; + +; If the client application is trying to connect to the database that is not +; listed in [databases] section, than this server connection settings are used. +; The format is the same as any connection string in the [databases] section +autodb_connstr = user=example port=6000 +; if an auto-database has no connections more than this many seconds +; it is dropped with it's pool (releasing resources) +autodb_idle_timeout = 3600 + +;;; ;;; Administrative settings ;;; diff -U2 -r pgbouncer-1.2.3/include/bouncer.h /home/davojan/pgbouncer/include/bouncer.h --- pgbouncer-1.2.3/include/bouncer.h 2008-08-06 12:25:10.000000000 +0400 +++ /home/davojan/pgbouncer/include/bouncer.h 2008-08-24 13:39:59.566109567 +0400 @@ -221,4 +221,5 @@ bool db_paused; /* PAUSE ; was issued */ bool db_dead; /* used on RELOAD/SIGHUP to later detect removed dbs */ + bool db_auto; /* is the database auto-created by autodb_connstr */ bool admin; /* internal console db */ @@ -237,4 +238,6 @@ /* startup commands to send to server after connect. malloc-ed */ const char *connect_query; + + usec_t inactive_time; /* when auto-database became inactive (to kill it after timeout) */ }; @@ -308,4 +311,7 @@ extern int cf_default_pool_size; +extern char * cf_autodb_connstr; +extern usec_t cf_autodb_idle_timeout; + extern usec_t cf_suspend_timeout; extern usec_t cf_server_lifetime; diff -U2 -r pgbouncer-1.2.3/include/objects.h /home/davojan/pgbouncer/include/objects.h --- pgbouncer-1.2.3/include/objects.h 2008-01-23 12:43:34.000000000 +0300 +++ /home/davojan/pgbouncer/include/objects.h 2008-08-25 23:00:21.982109262 +0400 @@ -21,4 +21,5 @@ extern StatList pool_list; extern StatList database_list; +extern StatList autodatabase_idle_list; extern StatList login_client_list; extern ObjectCache *client_cache; @@ -41,4 +42,5 @@ PgDatabase * add_database(const char *name) _MUSTCHECK; +PgDatabase *register_auto_database(const char *name); PgUser * add_user(const char *name, const char *passwd) _MUSTCHECK; PgUser * force_user(PgDatabase *db, const char *username, const char *passwd) _MUSTCHECK; diff -U2 -r pgbouncer-1.2.3/src/client.c /home/davojan/pgbouncer/src/client.c --- pgbouncer-1.2.3/src/client.c 2008-07-16 18:38:09.000000000 +0400 +++ /home/davojan/pgbouncer/src/client.c 2008-08-25 22:58:51.066109250 +0400 @@ -58,6 +58,12 @@ db = find_database(dbname); if (!db) { - disconnect_client(client, true, "No such database"); - return false; + db = register_auto_database(dbname); + if (!db) { + disconnect_client(client, true, "No such database"); + return false; + } + else { + slog_info(client, "registered new auto-database: db = %s", dbname ); + } } diff -U2 -r pgbouncer-1.2.3/src/janitor.c /home/davojan/pgbouncer/src/janitor.c --- pgbouncer-1.2.3/src/janitor.c 2008-08-04 14:44:56.000000000 +0400 +++ /home/davojan/pgbouncer/src/janitor.c 2008-08-24 21:31:59.554109446 +0400 @@ -477,11 +477,32 @@ } +static void kill_database(PgDatabase *db); +static void cleanup_inactive_autodatabases(void) +{ + List *item, *tmp; + PgDatabase *db; + usec_t age; + usec_t now = get_cached_time(); + + if (cf_autodb_idle_timeout <= 0) + return; + + statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { + db = container_of(item, PgDatabase, head); + age = now - db->inactive_time; + if (age > cf_autodb_idle_timeout) + kill_database(db); + else + break; + } +} + /* full-scale maintenance, done only occasionally */ static void do_full_maint(int sock, short flags, void *arg) { - List *item; + List *item, *tmp; PgPool *pool; - statlist_for_each(item, &pool_list) { + statlist_for_each_safe(item, &pool_list, tmp) { pool = container_of(item, PgPool, head); if (pool->db->admin) @@ -489,6 +510,14 @@ pool_server_maint(pool); pool_client_maint(pool); + if (pool->db->db_auto && pool->db->inactive_time == 0 && + pool_client_count(pool) == 0 && pool_server_count(pool) == 0 ) { + pool->db->inactive_time = get_cached_time(); + statlist_remove(&pool->db->head, &database_list); + statlist_append(&pool->db->head, &autodatabase_idle_list); + } } + cleanup_inactive_autodatabases(); + cleanup_client_logins(); @@ -536,5 +565,5 @@ List *item, *tmp; - log_warning("dropping database '%s' as it does not exist anymore", db->name); + log_warning("dropping database '%s' as it does not exist anymore or inactive auto-database", db->name); statlist_for_each_safe(item, &pool_list, tmp) { @@ -547,5 +576,8 @@ if (db->connect_query) free((void *)db->connect_query); - statlist_remove(&db->head, &database_list); + if (db->inactive_time) + statlist_remove(&db->head, &autodatabase_idle_list); + else + statlist_remove(&db->head, &database_list); obj_free(db_cache, db); } diff -U2 -r pgbouncer-1.2.3/src/loader.c /home/davojan/pgbouncer/src/loader.c --- pgbouncer-1.2.3/src/loader.c 2008-06-26 19:38:32.000000000 +0400 +++ /home/davojan/pgbouncer/src/loader.c 2008-08-24 14:16:15.666609398 +0400 @@ -265,4 +265,7 @@ /* tag the db as alive */ db->db_dead = 0; + /* assuming not an autodb */ + db->db_auto = 0; + db->inactive_time = 0; /* if updating old db, check if anything changed */ diff -U2 -r pgbouncer-1.2.3/src/main.c /home/davojan/pgbouncer/src/main.c --- pgbouncer-1.2.3/src/main.c 2008-08-08 17:33:29.000000000 +0400 +++ /home/davojan/pgbouncer/src/main.c 2008-08-24 13:37:47.127109420 +0400 @@ -97,4 +97,7 @@ char *cf_ignore_startup_params = ""; +char *cf_autodb_connstr = ""; +usec_t cf_autodb_idle_timeout = 3600*USEC; + usec_t cf_server_lifetime = 60*60*USEC; usec_t cf_server_idle_timeout = 10*60*USEC; @@ -140,4 +143,7 @@ {"user", false, CF_STR, &cf_username}, +{"autodb_connstr", true, CF_STR, &cf_autodb_connstr}, +{"autodb_idle_timeout", true, CF_TIME, &cf_autodb_idle_timeout}, + {"server_reset_query", true, CF_STR, &cf_server_reset_query}, {"server_check_query", true, CF_STR, &cf_server_check_query}, diff -U2 -r pgbouncer-1.2.3/src/objects.c /home/davojan/pgbouncer/src/objects.c --- pgbouncer-1.2.3/src/objects.c 2008-08-04 12:53:52.000000000 +0400 +++ /home/davojan/pgbouncer/src/objects.c 2008-08-26 01:05:52.034109129 +0400 @@ -52,4 +52,7 @@ static STATLIST(justfree_server_list); +/* init autodb idle list */ +STATLIST(autodatabase_idle_list); + /* fast way to get number of active clients */ int get_active_client_count(void) @@ -305,4 +308,25 @@ } +/* register new auto database */ +PgDatabase *register_auto_database(const char *name) +{ + PgDatabase *db; + char *cs = malloc(strlen(cf_autodb_connstr)+1); + + strcpy(cs, cf_autodb_connstr); + parse_database((char*)name, cs); + free(cs); + + db = find_database(name); + if (db) { + db->db_auto = 1; + /* do not forget to check pool_size like in config_postprocess */ + if (db->pool_size < 0) + db->pool_size = cf_default_pool_size; + } + + return db; +} + /* add or update client users */ PgUser *add_user(const char *name, const char *passwd) @@ -346,5 +370,5 @@ PgDatabase *find_database(const char *name) { - List *item; + List *item, *tmp; PgDatabase *db; statlist_for_each(item, &database_list) { @@ -353,4 +377,14 @@ return db; } + /* also trying to find in idle autodatabases list */ + statlist_for_each_safe(item, &autodatabase_idle_list, tmp) { + db = container_of(item, PgDatabase, head); + if (strcmp(db->name, name) == 0) { + db->inactive_time = 0; + statlist_remove(&db->head, &autodatabase_idle_list); + put_in_order(&db->head, &database_list, cmp_database); + return db; + } + } return NULL; } @@ -952,4 +986,11 @@ PktBuf tmp; bool res; + + /* if the database not found, it's an auto database -> registering... */ + if (!db) { + db = register_auto_database(dbname); + if (!db) + return true; + } if (db->forced_user)