From: Cody Cutrer Date: Thu, 19 Jan 2012 23:01:30 +0000 (-0700) Subject: add per-user pool_mode X-Git-Tag: pgbouncer_1_6_rc1~28^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=92fa36c4abb131297f21145dfeebcbd2c7bc23cf;p=pgbouncer add per-user pool_mode --- diff --git a/doc/config.txt b/doc/config.txt index 8ca116f..3dcf789 100644 --- a/doc/config.txt +++ b/doc/config.txt @@ -558,6 +558,19 @@ Ask specific +datestyle+ from server. Ask specific +timezone+ from server. +== SECTION [users] == + +This contains key=value pairs where key will be taken as a user name and +value as a libpq connect-string style list of key=value pairs. As actual libpq is not +used, so not all features from libpq can be used. + +=== Pool configuration === + +==== pool_mode ==== + +Set the pool mode to be used for all connections from this user. If not set, the +database or default pool_mode is used. + == AUTHENTICATION FILE FORMAT == PgBouncer needs its own user database. The users are loaded from a text diff --git a/doc/todo.txt b/doc/todo.txt index 1425e33..5724092 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -22,9 +22,7 @@ Waiting for contributors... * hba-style access control - * per-db pool_mode - - * per-user pool_mode, other settings ([users], user=connstr?) + * other per-user settings * identd authentication diff --git a/doc/usage.txt b/doc/usage.txt index 3033e5d..f554b14 100644 --- a/doc/usage.txt +++ b/doc/usage.txt @@ -321,6 +321,8 @@ maxwait:: not handle requests quick enough. Reason may be either overloaded server or just too small of a +pool_size+ setting. +pool_mode:: + The pooling mode in use. ==== SHOW LISTS; ==== @@ -352,7 +354,11 @@ used_servers:: ==== SHOW USERS; ==== -Shows one line per user, under the +name+ column name. +name:: + The user name + +pool_mode:: + The user's override pool_mode, or NULL if the default will be used instead. ==== SHOW DATABASES; ==== @@ -377,7 +383,7 @@ pool_size:: Maximum number of server connections. pool_mode:: - The pooling mode in user for this database. + The database's override pool_mode, or NULL if the default will be used instead. ==== SHOW FDS; ==== diff --git a/include/bouncer.h b/include/bouncer.h index 3398e94..e2e963f 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -251,6 +251,7 @@ struct PgUser { struct AANode tree_node; /* used to attach user to tree */ char name[MAX_USERNAME]; char passwd[MAX_PASSWORD]; + int pool_mode; }; /* diff --git a/include/loader.h b/include/loader.h index 1400b61..b9f0277 100644 --- a/include/loader.h +++ b/include/loader.h @@ -19,6 +19,8 @@ /* connstring parsing */ bool parse_database(void *base, const char *name, const char *connstr); +bool parse_user(void *base, const char *name, const char *params); + /* user file parsing */ bool load_auth_file(const char *fn) /* _MUSTCHECK */; bool loader_users_check(void) /* _MUSTCHECK */; diff --git a/include/server.h b/include/server.h index 870a590..9e3490c 100644 --- a/include/server.h +++ b/include/server.h @@ -17,4 +17,4 @@ */ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; - +int pool_pool_mode(PgPool *pool) _MUSTCHECK; diff --git a/src/admin.c b/src/admin.c index a30eff1..5ae7697 100644 --- a/src/admin.c +++ b/src/admin.c @@ -441,8 +441,9 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) const char *f_user; PktBuf *buf; struct CfValue cv; - int pool_mode; + const char *pool_mode_str; + cv.extra = pool_mode_map; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); @@ -457,17 +458,16 @@ static bool admin_show_databases(PgSocket *admin, const char *arg) db = container_of(item, PgDatabase, head); f_user = db->forced_user ? db->forced_user->name : NULL; - pool_mode = db->pool_mode; - if (pool_mode == POOL_INHERIT) - pool_mode = cf_pool_mode; - cv.value_p = &pool_mode; - cv.extra = pool_mode_map; + pool_mode_str = NULL; + cv.value_p = &db->pool_mode; + if (db->pool_mode != POOL_INHERIT) + pool_mode_str = cf_get_lookup(&cv); pktbuf_write_DataRow(buf, "ssissiis", db->name, db->host, db->port, db->dbname, f_user, db->pool_size, db->res_pool_size, - cf_get_lookup(&cv)); + pool_mode_str); } admin_flush(admin, buf, "SHOW"); return true; @@ -510,14 +510,24 @@ static bool admin_show_users(PgSocket *admin, const char *arg) PgUser *user; struct List *item; PktBuf *buf = pktbuf_dynamic(256); + struct CfValue cv; + const char *pool_mode_str; + if (!buf) { admin_error(admin, "no mem"); return true; } - pktbuf_write_RowDescription(buf, "s", "name"); + cv.extra = pool_mode_map; + + pktbuf_write_RowDescription(buf, "ss", "name", "pool_mode"); statlist_for_each(item, &user_list) { user = container_of(item, PgUser, head); - pktbuf_write_DataRow(buf, "s", user->name); + pool_mode_str = NULL; + cv.value_p = &user->pool_mode; + if (user->pool_mode != POOL_INHERIT) + pool_mode_str = cf_get_lookup(&cv); + + pktbuf_write_DataRow(buf, "ss", user->name, pool_mode_str); } admin_flush(admin, buf, "SHOW"); return true; @@ -731,22 +741,28 @@ static bool admin_show_pools(PgSocket *admin, const char *arg) PktBuf *buf; PgSocket *waiter; usec_t now = get_cached_time(); + struct CfValue cv; + int pool_mode; + cv.extra = pool_mode_map; + cv.value_p = &pool_mode; buf = pktbuf_dynamic(256); if (!buf) { admin_error(admin, "no mem"); return true; } - pktbuf_write_RowDescription(buf, "ssiiiiiiii", + pktbuf_write_RowDescription(buf, "ssiiiiiiiis", "database", "user", "cl_active", "cl_waiting", "sv_active", "sv_idle", "sv_used", "sv_tested", - "sv_login", "maxwait"); + "sv_login", "maxwait", + "pool_mode"); statlist_for_each(item, &pool_list) { pool = container_of(item, PgPool, head); waiter = first_socket(&pool->waiting_client_list); - pktbuf_write_DataRow(buf, "ssiiiiiiii", + pool_mode = pool_pool_mode(pool); + pktbuf_write_DataRow(buf, "ssiiiiiiiis", pool->db->name, pool->user->name, statlist_count(&pool->active_client_list), statlist_count(&pool->waiting_client_list), @@ -757,7 +773,8 @@ static bool admin_show_pools(PgSocket *admin, const char *arg) statlist_count(&pool->new_server_list), /* how long is the oldest client waited */ (waiter && waiter->query_start) - ? (int)((now - waiter->query_start) / USEC) : 0); + ? (int)((now - waiter->query_start) / USEC) : 0, + cf_get_lookup(&cv)); } admin_flush(admin, buf, "SHOW"); return true; diff --git a/src/loader.c b/src/loader.c index e7db4fd..62b2056 100644 --- a/src/loader.c +++ b/src/loader.c @@ -374,6 +374,58 @@ fail: return true; } +bool parse_user(void *base, const char *name, const char *connstr) +{ + char *p, *key, *val, *tmp_connstr; + PgUser *user; + struct CfValue cv; + int pool_mode = POOL_INHERIT; + + cv.value_p = &pool_mode; + cv.extra = (const void *)pool_mode_map; + + tmp_connstr = strdup(connstr); + if (!tmp_connstr) + return false; + + p = tmp_connstr; + while (*p) { + p = cstr_get_pair(p, &key, &val); + if (p == NULL) { + log_error("%s: syntax error in user settings", name); + goto fail; + } else if (!key[0]) + break; + + if (strcmp("pool_mode", key) == 0) { + if (!cf_set_lookup(&cv, val)) { + log_error("skipping user %s because" + " of invalid pool mode: %s", name, val); + goto fail; + } + } else { + log_error("skipping user %s because" + " of unknown parameter in settings: %s", name, key); + goto fail; + } + } + + user = find_user(name); + if (!user) { + user = add_user(name, ""); + if (!user) { + log_error("cannot create user, no memory?"); + goto fail; + } + } + + user->pool_mode = pool_mode; + +fail: + free(tmp_connstr); + return true; +} + /* * User file parsing */ diff --git a/src/main.c b/src/main.c index 923b530..f74bd7b 100644 --- a/src/main.c +++ b/src/main.c @@ -235,6 +235,9 @@ static const struct CfSect config_sects [] = { }, { .sect_name = "databases", .set_key = parse_database, + }, { + .sect_name = "users", + .set_key = parse_user, }, { .sect_name = NULL, } diff --git a/src/objects.c b/src/objects.c index d56dcd8..b48f774 100644 --- a/src/objects.c +++ b/src/objects.c @@ -361,6 +361,7 @@ PgUser *add_user(const char *name, const char *passwd) put_in_order(&user->head, &user_list, cmp_user); aatree_insert(&user_tree, (uintptr_t)user->name, &user->tree_node); + user->pool_mode = POOL_INHERIT; } safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); return user; @@ -376,6 +377,7 @@ PgUser *force_user(PgDatabase *db, const char *name, const char *passwd) return NULL; list_init(&user->head); list_init(&user->pool_list); + user->pool_mode = POOL_INHERIT; } safe_strcpy(user->name, name, sizeof(user->name)); safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); diff --git a/src/server.c b/src/server.c index ffa2515..f21ba7a 100644 --- a/src/server.c +++ b/src/server.c @@ -185,6 +185,16 @@ static bool handle_server_startup(PgSocket *server, PktHdr *pkt) return res; } +int pool_pool_mode(PgPool *pool) +{ + int pool_mode = pool->user->pool_mode; + if (pool_mode == POOL_INHERIT) + pool_mode = pool->db->pool_mode; + if (pool_mode == POOL_INHERIT) + pool_mode = cf_pool_mode; + return pool_mode; +} + /* process packets on logged in connection */ static bool handle_server_work(PgSocket *server, PktHdr *pkt) { @@ -210,10 +220,9 @@ static bool handle_server_work(PgSocket *server, PktHdr *pkt) return false; /* set ready only if no tx */ - if (state == 'I') { + if (state == 'I') ready = true; - } else if (server->pool->db->pool_mode == POOL_STMT || - (server->pool->db->pool_mode == POOL_INHERIT && cf_pool_mode == POOL_STMT)) { + else if (pool_pool_mode(server->pool) == POOL_STMT) { disconnect_server(server, true, "Long transactions not allowed"); return false; } else if (state == 'T' || state == 'E') { @@ -352,7 +361,6 @@ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) PgSocket *server = container_of(sbuf, PgSocket, sbuf); PgPool *pool = server->pool; PktHdr pkt; - int pool_mode; Assert(is_server_socket(server)); Assert(server->state != SV_FREE); @@ -420,10 +428,7 @@ bool server_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *data) break; } - pool_mode = server->pool->db->pool_mode; - if (pool_mode == POOL_INHERIT) - pool_mode = cf_pool_mode; - if (pool_mode != POOL_SESSION || server->state == SV_TESTED) { + if (pool_pool_mode(pool) != POOL_SESSION || server->state == SV_TESTED) { switch (server->state) { case SV_ACTIVE: case SV_TESTED: