summaryrefslogtreecommitdiff
path: root/src/mongo-sync-pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo-sync-pool.c')
-rw-r--r--src/mongo-sync-pool.c269
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;
+}