/* mongo-sync-pool.c - libmongo-client connection pool implementation * Copyright 2011, 2012 Gergely Nagy * * 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 #include #include #include #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; }