From 9c9d462640dc5b029dcacce4c5f4999a51172409 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 19 Nov 2008 12:50:05 +0000 Subject: [PATCH] Dynamic database creation for non-defined databases. Activated by "*" entry in [databases] section. Patch by David Galoyan --- include/bouncer.h | 6 ++++++ include/objects.h | 2 ++ src/client.c | 10 ++++++++-- src/janitor.c | 40 +++++++++++++++++++++++++++++++++---- src/loader.c | 22 +++++++++++++++++++- src/main.c | 5 +++++ src/objects.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 128 insertions(+), 8 deletions(-) diff --git a/include/bouncer.h b/include/bouncer.h index c34bc0a..b4239ac 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -220,6 +220,7 @@ struct PgDatabase { 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 */ uint8_t startup_params[256]; /* partial StartupMessage (without user) be sent to server */ @@ -236,6 +237,8 @@ struct PgDatabase { /* 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) */ }; @@ -307,6 +310,9 @@ extern int cf_pool_mode; extern int cf_max_client_conn; 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; extern usec_t cf_server_idle_timeout; diff --git a/include/objects.h b/include/objects.h index 374e22b..8bb0e01 100644 --- a/include/objects.h +++ b/include/objects.h @@ -20,6 +20,7 @@ extern StatList user_list; extern Tree user_tree; extern StatList pool_list; extern StatList database_list; +extern StatList autodatabase_idle_list; extern StatList login_client_list; extern ObjectCache *client_cache; extern ObjectCache *server_cache; @@ -40,6 +41,7 @@ void disconnect_server(PgSocket *server, bool notify, const char *reason); void disconnect_client(PgSocket *client, bool notify, const char *reason); 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 --git a/src/client.c b/src/client.c index 4b8af73..583bdee 100644 --- a/src/client.c +++ b/src/client.c @@ -57,8 +57,14 @@ bool set_pool(PgSocket *client, const char *dbname, const char *username) /* find database */ 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 ); + } } /* find user */ diff --git a/src/janitor.c b/src/janitor.c index 939df82..c8e1217 100644 --- a/src/janitor.c +++ b/src/janitor.c @@ -476,20 +476,49 @@ static void cleanup_client_logins(void) } } +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) continue; 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(); if (cf_shutdown && get_active_server_count() == 0) { @@ -535,7 +564,7 @@ static void kill_database(PgDatabase *db) PgPool *pool; 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) { pool = container_of(item, PgPool, head); @@ -546,7 +575,10 @@ static void kill_database(PgDatabase *db) obj_free(user_cache, db->forced_user); 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 --git a/src/loader.c b/src/loader.c index 93ad2b9..20147bc 100644 --- a/src/loader.c +++ b/src/loader.c @@ -152,6 +152,18 @@ static void set_connect_query(PgDatabase *db, const char *new) log_error("no memory, cannot assign connect_query for %s", db->name); } +static void set_autodb(char *connstr) +{ + char *tmp = strdup(connstr); + if (!tmp) { + log_warning("no mem to change autodb_connstr"); + return; + } + if (cf_autodb_connstr) + free(cf_autodb_connstr); + cf_autodb_connstr = tmp; +} + /* fill PgDatabase from connstr */ void parse_database(char *name, char *connstr) { @@ -174,6 +186,11 @@ void parse_database(char *name, char *connstr) in_addr_t v_addr = INADDR_NONE; int v_port; + if (strcmp(name, "*") == 0) { + set_autodb(connstr); + return; + } + p = connstr; while (*p) { p = cstr_get_pair(p, &key, &val); @@ -264,6 +281,9 @@ void parse_database(char *name, char *connstr) /* 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 */ if (db->dbname) { @@ -701,7 +721,7 @@ bool iniparser(const char *fn, ConfSection *sect_list, bool reload) klen = strlen(keybuf); } else { key = p; - while (*p && (isalnum(*p) || strchr("_.-", *p))) p++; + while (*p && (isalnum(*p) || strchr("_.-*", *p))) p++; klen = p - key; } diff --git a/src/main.c b/src/main.c index 1973837..152f071 100644 --- a/src/main.c +++ b/src/main.c @@ -97,6 +97,9 @@ int cf_server_round_robin = 0; char *cf_ignore_startup_params = ""; +char *cf_autodb_connstr = NULL; +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; usec_t cf_server_connect_timeout = 15*USEC; @@ -140,6 +143,8 @@ ConfElem bouncer_params[] = { {"syslog_facility", true, CF_STR, &cf_syslog_facility}, {"user", false, CF_STR, &cf_username}, +{"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}, {"server_check_delay", true, CF_TIME, &cf_server_check_delay}, diff --git a/src/objects.c b/src/objects.c index 9178ff2..8799ddd 100644 --- a/src/objects.c +++ b/src/objects.c @@ -51,6 +51,9 @@ ObjectCache *iobuf_cache; static STATLIST(justfree_client_list); 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) { @@ -304,6 +307,35 @@ PgDatabase *add_database(const char *name) return db; } +/* register new auto database */ +PgDatabase *register_auto_database(const char *name) +{ + PgDatabase *db; + int len; + char *cs; + + if (!cf_autodb_connstr) + return NULL; + + len = strlen(cf_autodb_connstr); + cs = malloc(len + 1); + if (!cs) + return NULL; + memcpy(cs, cf_autodb_connstr, len + 1); + 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) { @@ -345,13 +377,23 @@ PgUser *force_user(PgDatabase *db, const char *name, const char *passwd) /* find an existing database */ PgDatabase *find_database(const char *name) { - List *item; + List *item, *tmp; PgDatabase *db; statlist_for_each(item, &database_list) { db = container_of(item, PgDatabase, head); if (strcmp(db->name, name) == 0) 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; } @@ -951,6 +993,13 @@ bool use_server_socket(int fd, PgAddr *addr, PgSocket *server; 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) user = db->forced_user; -- 2.39.5