/* Copyright (c) 2013, 2014 IOnU Security Inc. All rights reserved. Copyright (c) 2015, 2016 Sequence Logic, Inc. Created August 2013 by Kendrick Webster K2Daemon/MongoDB.cpp - implementation for MongoDB.h */ #include "global.h" #include "MongoDB.h" #include // module static (local) data namespace { mongoc_client_t *connection = NULL; std::string mongoServer; bool isConnected = false; bool isDown = false; // true during transient failures (i.e. replica set primary goes down, new primary not yet elected) CStopWatch stopwatch; } success_t MongoDB::Connect(const char* host, int port) { logprintf(Log::informational, "Connecting to MongoDB host %s port %d", host, port); std::stringstream hostURI; hostURI << "mongodb://" << host << ":" << port; mongoServer = hostURI.str(); connection = mongoc_client_new(mongoServer.c_str()); if (connection == NULL) { isConnected = false; logprintf(Log::error, "mongo_client (connect): \"mongodb://%s:%d\"", host, port); return FAILURE; } isConnected = true; isDown = false; logprintf(Log::debug, "connected to MongoDB"); return SUCCESS; } success_t MongoDB::ConnectReplica(const std::string& replica_set_name, const std::string& hosts) { if (isConnected) Disconnect(); logprintf(Log::informational, "Connecting to MongoDB replica set %s", replica_set_name.c_str()); std::stringstream repSetURI; repSetURI << "mongodb://" << hosts << "/?replicaSet=" << replica_set_name; mongoServer = repSetURI.str(); connection = mongoc_client_new(mongoServer.c_str()); if (connection == NULL) { logprintf(Log::error, "Cannot connect to replica set: \"mongodb://%s/?replicaSet=%s\"", hosts.c_str(), replica_set_name.c_str()); isConnected = false; return FAILURE; } isConnected = true; isDown = false; logprintf(Log::debug, "connected to MongoDB"); return SUCCESS; } void MongoDB::Disconnect(void) { if (isConnected) { logprintf(Log::debug, "logging out of MongoDB"); mongoc_client_destroy(connection); isConnected = false; connection = NULL; } } success_t MongoDB::Reconnect(void) { if (isConnected) { logprintf(Log::informational, "Re-connecting to MongoDB"); // It seems that the new C driver doesn't have a "reconnect" function!!! Disconnect(); connection = mongoc_client_new(mongoServer.c_str()); if (connection == NULL) { isDown = true; stopwatch.Reset(); logprintf(Log::error, "mongo_reconnect"); return FAILURE; } } return FAILURE; } mongoc_client_t* MongoDB::GetConnection(void) { if (isDown || !isConnected) { return NULL; } return connection; } bool MongoDB::IsTemporaryOutage(void) { return isDown && isConnected; } const char* MongoDB::ErrorString(mongoc_error_code_t e) { static char buf[48]; switch (e) { //case MONGO_CONN_SUCCESS: return "Connection success!"; //case MONGO_CONN_NO_SOCKET: return "Could not create a socket."; case MONGOC_ERROR_STREAM_CONNECT: return "An error occured while calling connect()."; case MONGOC_ERROR_STREAM_NAME_RESOLUTION: return "An error occured while calling getaddrinfo()."; //case MONGO_CONN_NOT_MASTER: return "Warning: connected to a non-master node (read-only)."; //case MONGO_CONN_BAD_SET_NAME: return "Given rs name doesn't match this replica set."; //case MONGO_CONN_NO_PRIMARY: return "Can't find primary in replica set. Connection closed."; //case MONGO_IO_ERROR: return "An error occurred while reading or writing on the socket."; //case MONGOC_ERROR_STREAM_SOCKET: return "Other socket error."; //case MONGO_READ_SIZE_ERROR: return "The response is not the expected length."; //case MONGO_COMMAND_FAILED: return "The command returned with 'ok' value of 0."; //case MONGO_WRITE_ERROR: return "Write with given write_concern returned an error."; //case MONGO_NS_INVALID: return "The name for the ns (database or collection) is invalid."; //case MONGO_BSON_INVALID: return "BSON not valid for the specified op."; //case MONGO_BSON_NOT_FINISHED: return "BSON object has not been finished."; //case MONGO_BSON_TOO_LARGE: return "BSON object exceeds max BSON size."; //case MONGO_WRITE_CONCERN_INVALID: return "Supplied write concern object is invalid."; default: snprintf(buf, sizeof(buf), "Unknown MongoDB error, code %d", static_cast(e)); return buf; } } void MongoDB::Timer(void) { if (isDown && (MONGODB_RECONNECT_DELAY_MILLISECONDS <= stopwatch.Milliseconds())) { logprintf(Log::informational, "timer initiated MongoDB reconnect"); Reconnect(); } }