diff options
Diffstat (limited to 'src/mongo-sync-pool.c')
-rw-r--r-- | src/mongo-sync-pool.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/mongo-sync-pool.c b/src/mongo-sync-pool.c new file mode 100644 index 0000000..52f5042 --- /dev/null +++ b/src/mongo-sync-pool.c @@ -0,0 +1,269 @@ +/* mongo-sync-pool.c - libmongo-client connection pool implementation + * Copyright 2011, 2012 Gergely Nagy <algernon@balabit.hu> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file src/mongo-sync-pool.c + * MongoDB connection pool API implementation. + */ + +#include <errno.h> +#include <string.h> +#include <glib.h> +#include <mongo.h> +#include "libmongo-private.h" + +/** @internal A connection pool object. */ +struct _mongo_sync_pool +{ + gint nmasters; /**< Number of master connections in the pool. */ + gint nslaves; /**< Number of slave connections in the pool. */ + + GList *masters; /**< List of master connections in the pool. */ + GList *slaves; /**< List of slave connections in the pool. */ +}; + +static mongo_sync_pool_connection * +_mongo_sync_pool_connect (const gchar *host, gint port, gboolean slaveok) +{ + mongo_sync_connection *c; + mongo_sync_pool_connection *conn; + + c = mongo_sync_connect (host, port, slaveok); + if (!c) + return NULL; + conn = g_realloc (c, sizeof (mongo_sync_pool_connection)); + conn->pool_id = 0; + conn->in_use = FALSE; + + return conn; +} + +mongo_sync_pool * +mongo_sync_pool_new (const gchar *host, + gint port, + gint nmasters, gint nslaves) +{ + mongo_sync_pool *pool; + mongo_sync_pool_connection *conn; + gint i, j = 0; + + if (!host || port < 0) + { + errno = EINVAL; + return NULL; + } + if (nmasters < 0 || nslaves < 0) + { + errno = ERANGE; + return NULL; + } + if (nmasters + nslaves <= 0) + { + errno = EINVAL; + return NULL; + } + + conn = _mongo_sync_pool_connect (host, port, FALSE); + if (!conn) + return FALSE; + + if (!mongo_sync_cmd_is_master ((mongo_sync_connection *)conn)) + { + mongo_sync_disconnect ((mongo_sync_connection *)conn); + errno = EPROTO; + return NULL; + } + + pool = g_new0 (mongo_sync_pool, 1); + pool->nmasters = nmasters; + pool->nslaves = nslaves; + + for (i = 0; i < pool->nmasters; i++) + { + mongo_sync_pool_connection *c; + + c = _mongo_sync_pool_connect (host, port, FALSE); + c->pool_id = i; + + pool->masters = g_list_append (pool->masters, c); + } + + for (i = 0; i < pool->nslaves; i++) + { + mongo_sync_pool_connection *c; + gchar *shost = NULL; + gint sport = 27017; + GList *l; + gboolean found = FALSE; + gboolean need_restart = (j != 0); + + /* Select the next secondary */ + l = g_list_nth (conn->super.rs.hosts, j); + + do + { + j++; + if (l && mongo_util_parse_addr ((gchar *)l->data, &shost, &sport)) + { + if (sport != port || strcmp (host, shost) != 0) + { + found = TRUE; + break; + } + } + l = g_list_next (l); + if (!l && need_restart) + { + need_restart = FALSE; + j = 0; + l = g_list_nth (conn->super.rs.hosts, j); + } + } + while (l); + + if (!found) + { + pool->nslaves = i - 1; + break; + } + + /* Connect to it*/ + c = _mongo_sync_pool_connect (shost, sport, TRUE); + c->pool_id = pool->nmasters + i + 1; + + pool->slaves = g_list_append (pool->slaves, c); + } + + mongo_sync_disconnect ((mongo_sync_connection *)conn); + return pool; +} + +void +mongo_sync_pool_free (mongo_sync_pool *pool) +{ + GList *l; + + if (!pool) + return; + + l = pool->masters; + while (l) + { + mongo_sync_disconnect ((mongo_sync_connection *)l->data); + l = g_list_delete_link (l, l); + } + + l = pool->slaves; + while (l) + { + mongo_sync_disconnect ((mongo_sync_connection *)l->data); + l = g_list_delete_link (l, l); + } + + g_free (pool); +} + +mongo_sync_pool_connection * +mongo_sync_pool_pick (mongo_sync_pool *pool, + gboolean want_master) +{ + GList *l; + + if (!pool) + { + errno = ENOTCONN; + return NULL; + } + + if (!want_master) + { + l = pool->slaves; + + while (l) + { + mongo_sync_pool_connection *c; + + c = (mongo_sync_pool_connection *)l->data; + if (!c->in_use) + { + c->in_use = TRUE; + return c; + } + l = g_list_next (l); + } + } + + l = pool->masters; + while (l) + { + mongo_sync_pool_connection *c; + + c = (mongo_sync_pool_connection *)l->data; + if (!c->in_use) + { + c->in_use = TRUE; + return c; + } + l = g_list_next (l); + } + + errno = EAGAIN; + return NULL; +} + +gboolean +mongo_sync_pool_return (mongo_sync_pool *pool, + mongo_sync_pool_connection *conn) +{ + if (!pool) + { + errno = ENOTCONN; + return FALSE; + } + if (!conn) + { + errno = EINVAL; + return FALSE; + } + + if (conn->pool_id > pool->nmasters) + { + mongo_sync_pool_connection *c; + + if (conn->pool_id - pool->nmasters > pool->nslaves || + pool->nslaves == 0) + { + errno = ERANGE; + return FALSE; + } + + c = (mongo_sync_pool_connection *)g_list_nth_data + (pool->slaves, conn->pool_id - pool->nmasters - 1); + c->in_use = FALSE; + return TRUE; + } + else + { + mongo_sync_pool_connection *c; + + c = (mongo_sync_pool_connection *)g_list_nth_data (pool->masters, + conn->pool_id); + c->in_use = FALSE; + return TRUE; + } + + errno = ENOENT; + return FALSE; +} |