# sources
SRCS = client.c loader.c objects.c pooler.c proto.c sbuf.c server.c util.c \
admin.c stats.c takeover.c md5.c janitor.c pktbuf.c system.c main.c \
- varcache.c aatree.c hash.c
+ varcache.c aatree.c hash.c slab.c
HDRS = client.h loader.h objects.h pooler.h proto.h sbuf.h server.h util.h \
admin.h stats.h takeover.h md5.h janitor.h pktbuf.h system.h bouncer.h \
- list.h mbuf.h varcache.h aatree.h hash.h
+ list.h mbuf.h varcache.h aatree.h hash.h slab.h
# data & dirs to include in tgz
DOCS = doc/overview.txt doc/usage.txt doc/config.txt doc/todo.txt
SENDLIST("databases", statlist_count(&database_list));
SENDLIST("users", statlist_count(&user_list));
SENDLIST("pools", statlist_count(&pool_list));
- SENDLIST("free_clients", statlist_count(&free_client_list));
- SENDLIST("used_clients", get_active_client_count());
+ SENDLIST("free_clients", objcache_free_count(client_cache));
+ SENDLIST("used_clients", objcache_active_count(client_cache));
SENDLIST("login_clients", statlist_count(&login_client_list));
- SENDLIST("free_servers", statlist_count(&free_server_list));
- SENDLIST("used_servers", get_active_server_count());
+ SENDLIST("free_servers", objcache_free_count(server_cache));
+ SENDLIST("used_servers", objcache_active_count(server_cache));
admin_flush(admin, buf, "SHOW");
return true;
}
#include "sbuf.h"
#include "pktbuf.h"
#include "varcache.h"
+#include "slab.h"
#include "admin.h"
#include "loader.h"
load_config(false);
+ init_caches();
+
/* need to do that after loading config */
check_limits();
* they are always in either active or free lists
* in addition to others.
*/
-STATLIST(free_client_list);
-STATLIST(free_server_list);
STATLIST(login_client_list);
+ObjectCache *server_cache;
+ObjectCache *client_cache;
+
/*
* libevent may still report events when event_del()
* is called from somewhere else. So hide just freed
static STATLIST(justfree_client_list);
static STATLIST(justfree_server_list);
-/* how many client sockets are allocated */
-static int absolute_client_count = 0;
-/* how many server sockets are allocated */
-static int absolute_server_count = 0;
-
/* fast way to get number of active clients */
int get_active_client_count(void)
{
- return absolute_client_count - statlist_count(&free_client_list);
+ return objcache_active_count(client_cache);
}
/* fast way to get number of active servers */
int get_active_server_count(void)
{
- return absolute_server_count - statlist_count(&free_server_list);
+ return objcache_active_count(server_cache);
}
/* this should be called on free socket that is put into use */
-static void clean_socket(PgSocket *sk)
+static void clean_socket(void *obj)
{
+ PgSocket *sk = obj;
+
sk->link = NULL;
sk->pool = NULL;
sk->setting_vars = 0;
}
-/* allocate & fill client socket */
-static PgSocket *new_client(void)
+static void construct_client(void *obj)
{
- PgSocket *client;
-
- /* get free PgSocket */
- client = first_socket(&free_client_list);
- if (client) {
- clean_socket(client);
- return client;
- }
-
- client = zmalloc(sizeof(*client) + cf_sbuf_len);
- if (!client)
- return NULL;
+ PgSocket *client = obj;
list_init(&client->head);
sbuf_init(&client->sbuf, client_proto, client);
- statlist_prepend(&client->head, &free_client_list);
client->state = CL_FREE;
-
- absolute_client_count++;
-
- return client;
}
-/* allocate & fill server socket */
-static PgSocket *new_server(void)
+static void construct_server(void *obj)
{
- PgSocket *server;
-
- /* get free PgSocket */
- server = first_socket(&free_server_list);
- if (server) {
- clean_socket(server);
- return server;
- }
-
- server = zmalloc(sizeof(*server) + cf_sbuf_len);
- if (!server)
- return NULL;
+ PgSocket *server = obj;
list_init(&server->head);
sbuf_init(&server->sbuf, server_proto, server);
- statlist_prepend(&server->head, &free_server_list);
server->state = SV_FREE;
+}
- absolute_server_count++;
+/* compare string with PgUser->name, for usage with btree */
+static int user_node_cmp(long userptr, Node *node)
+{
+ const char *name = (const char *)userptr;
+ PgUser *user = container_of(node, PgUser, tree_node);
+ return strcmp(name, user->name);
+}
+
+void init_objects(void)
+{
+ tree_init(&user_tree, user_node_cmp, NULL);
+}
- return server;
+void init_caches(void)
+{
+ server_cache = objcache_create("server_cache", sizeof(PgSocket) + cf_sbuf_len, 8,
+ construct_server, clean_socket);
+ client_cache = objcache_create("client_cache", sizeof(PgSocket) + cf_sbuf_len, 8,
+ construct_client, clean_socket);
}
/* state change means moving between lists */
/* remove from old location */
switch (client->state) {
case CL_FREE:
- statlist_remove(&client->head, &free_client_list);
break;
case CL_JUSTFREE:
statlist_remove(&client->head, &justfree_client_list);
/* put to new location */
switch (client->state) {
case CL_FREE:
- statlist_prepend(&client->head, &free_client_list);
+ obj_free(client_cache, client);
break;
case CL_JUSTFREE:
statlist_append(&client->head, &justfree_client_list);
/* remove from old location */
switch (server->state) {
case SV_FREE:
- statlist_remove(&server->head, &free_server_list);
break;
case SV_JUSTFREE:
statlist_remove(&server->head, &justfree_server_list);
/* put to new location */
switch (server->state) {
case SV_FREE:
- statlist_prepend(&server->head, &free_server_list);
+ obj_free(server_cache, server);
break;
case SV_JUSTFREE:
statlist_append(&server->head, &justfree_server_list);
return NULL;
}
-/* compare string with PgUser->name, for usage with btree */
-static int user_node_cmp(long userptr, Node *node)
-{
- const char *name = (const char *)userptr;
- PgUser *user = container_of(node, PgUser, tree_node);
- return strcmp(name, user->name);
-}
-
-void init_objects(void)
-{
- tree_init(&user_tree, user_node_cmp, NULL);
-}
-
/* find existing user */
PgUser *find_user(const char *name)
{
}
/* get free conn object */
- server = new_server();
+ server = obj_alloc(server_cache);
if (!server) {
log_debug("launch_new_connection: no memory");
return;
PgSocket *client;
/* get free PgSocket */
- client = new_client();
+ client = obj_alloc(client_cache);
if (!client)
return NULL;
if (!pool)
return false;
- server = new_server();
+ server = obj_alloc(server_cache);
if (!server)
return false;
extern StatList pool_list;
extern StatList database_list;
extern StatList login_client_list;
-extern StatList free_server_list;
-extern StatList free_client_list;
-extern StatList login_client_list;
+extern ObjectCache *client_cache;
+extern ObjectCache *server_cache;
PgDatabase *find_database(const char *name);
PgUser *find_user(const char *name);
void init_objects(void);
+void init_caches(void);
+
--- /dev/null
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * SLAB-like allocator, but without the complexities of deallocation...
+ *
+ * - On first alloc initializer is called for all objects.
+ * - On each release, cleaner is called.
+ * - When giving object out, nothing is done.
+ * - Writes obj header with List struct, expects it to be overwritten on use.
+ */
+
+#include "system.h"
+#include "list.h"
+#include "slab.h"
+
+/*
+ * Stores pre-initialized objects of one type.
+ */
+struct ObjectCache {
+ List head;
+ StatList freelist;
+ const char *name;
+ int obj_size;
+ int align;
+ int total_count;
+ obj_init_fn init_func;
+ obj_clean_fn clean_func;
+};
+
+/* keep track of all caches */
+static STATLIST(objcache_list);
+
+/* make new cache */
+ObjectCache * objcache_create(const char *name, int obj_size, int align,
+ obj_init_fn init_func,
+ obj_clean_fn clean_func)
+{
+ ObjectCache *cache = malloc(sizeof(*cache));
+ if (!cache)
+ return NULL;
+ list_init(&cache->head);
+ statlist_init(&cache->freelist, name);
+ cache->name = name;
+ cache->obj_size = obj_size;
+ cache->align = align;
+ cache->total_count = 0;
+ cache->init_func = init_func;
+ cache->clean_func = clean_func;
+ statlist_append(&cache->head, &objcache_list);
+
+ return cache;
+}
+
+/* add new block of objects to cache */
+static void grow(ObjectCache *cache)
+{
+ int count, i, real_size;
+ char *area;
+
+ real_size = (cache->obj_size + cache->align - 1) & ~(cache->align - 1);
+
+ count = 8192 / real_size;
+ if (count < 20)
+ count = 20;
+
+ area = malloc(count * real_size);
+ if (!area)
+ return;
+
+ memset(area, 0, count * real_size);
+ for (i = 0; i < count; i++) {
+ void *obj = area + i * real_size;
+ cache->init_func(obj);
+ statlist_append((List *)obj, &cache->freelist);
+ }
+
+ cache->total_count += count;
+}
+
+/* get free object from cache */
+void *obj_alloc(ObjectCache *cache)
+{
+ List *item;
+
+ item = statlist_pop(&cache->freelist);
+ if (item)
+ return item;
+
+ grow(cache);
+
+ return statlist_pop(&cache->freelist);
+}
+
+/* put object back to cache */
+void obj_free(ObjectCache *cache, void *obj)
+{
+ List *item = obj;
+ cache->clean_func(obj);
+ statlist_prepend(item, &cache->freelist);
+}
+
+/* total number of objects allocated from cache */
+int objcache_total_count(ObjectCache *cache)
+{
+ return cache->total_count;
+}
+
+/* free objects in cache */
+int objcache_free_count(ObjectCache *cache)
+{
+ return statlist_count(&cache->freelist);
+}
+
+/* number of objects in use */
+int objcache_active_count(ObjectCache *cache)
+{
+ return objcache_total_count(cache) - objcache_free_count(cache);
+}
+
--- /dev/null
+
+typedef struct ObjectCache ObjectCache;
+
+typedef void (*obj_init_fn)(void *obj);
+typedef void (*obj_clean_fn)(void *obj);
+
+ObjectCache *objcache_create(const char *name, int obj_size, int align,
+ obj_init_fn init_func, obj_clean_fn clean_func);
+
+void * obj_alloc(ObjectCache *cache);
+void obj_free(ObjectCache *cache, void *obj);
+
+int objcache_total_count(ObjectCache *cache);
+int objcache_free_count(ObjectCache *cache);
+int objcache_active_count(ObjectCache *cache);
+