diff --git a/rdbms/Conn.cpp b/rdbms/Conn.cpp index f70c14b855d45147253aa655e85826fc8f7cd47e..75462ba85ce3541ed045769df6bcd5a333bbd2e5 100644 --- a/rdbms/Conn.cpp +++ b/rdbms/Conn.cpp @@ -67,5 +67,19 @@ void Conn::executeNonQuery(const std::string &sql, const Stmt::AutocommitMode au } } +//------------------------------------------------------------------------------ +// setHealthy +//------------------------------------------------------------------------------ +void Conn::setHealthy(const bool value) { + m_healthy = value; +} + +//------------------------------------------------------------------------------ +// getHealthy +//------------------------------------------------------------------------------ +bool Conn::getHealthy() const { + return m_healthy; +} + } // namespace rdbms } // namespace cta diff --git a/rdbms/Conn.hpp b/rdbms/Conn.hpp index 7a9f08f051e170f5a4ce694ed0a8d35954f1836a..684b97e58cf65ea12cfd54c36a709605683d72fc 100644 --- a/rdbms/Conn.hpp +++ b/rdbms/Conn.hpp @@ -20,6 +20,7 @@ #include "Stmt.hpp" +#include <atomic> #include <list> #include <memory> #include <string> @@ -93,6 +94,29 @@ public: */ virtual std::list<std::string> getTableNames() = 0; + /** + * Returns true if the connection is healthy. + * + * @return True if the connection is healthy. + */ + bool getHealthy() const; + +protected: + + /** + * Sets the status of the connection to be either healthy or not healthy. + * + * @param value True if the connection is healthy. + */ + void setHealthy(const bool value); + +private: + + /** + * True if the connection's state is healthy. + */ + std::atomic<bool> m_healthy{true}; + }; // class Conn } // namespace rdbms diff --git a/rdbms/ConnPool.cpp b/rdbms/ConnPool.cpp index ed75b79df17cbb9bccb250e2eb501e64d850a44f..e9101aa26e179d4169b8bfc6fc32ecab0aad5742 100644 --- a/rdbms/ConnPool.cpp +++ b/rdbms/ConnPool.cpp @@ -69,10 +69,24 @@ PooledConn ConnPool::getConn() { // returnConn //------------------------------------------------------------------------------ void ConnPool::returnConn(Conn *const conn) { - conn->commit(); - std::unique_lock<std::mutex> lock(m_connsMutex); - m_conns.emplace_back(conn); - m_connsCv.notify_one(); + // If the connection is healthy + if(conn->getHealthy()) { + + // Commit the connection and put it back in the pool + conn->commit(); + std::unique_lock<std::mutex> lock(m_connsMutex); + m_conns.emplace_back(conn); + m_connsCv.notify_one(); + + // Else the connection is not healthy + } else { + + // Close the connection and put a brand new one in the pool + delete conn; + std::unique_lock<std::mutex> lock(m_connsMutex); + m_conns.push_back(m_connFactory.create()); + m_connsCv.notify_one(); + } } } // namespace rdbms diff --git a/rdbms/ConnPool.hpp b/rdbms/ConnPool.hpp index 7cf1c0c020787de63a0a7fa356411a6c9d508bad..c3fa7ce85344db60888b35bc0ee92ced0c0852be 100644 --- a/rdbms/ConnPool.hpp +++ b/rdbms/ConnPool.hpp @@ -60,8 +60,11 @@ private: friend PooledConn; /** - * Calls commit on the specified database connection and returns it to the - * pool. + * If the specified database connection is healthy, then this method calls + * commit on the connection and returns it to the pool. + * + * If the specified database connection is no healthy, then this method + * closes the connection and creates a new one in the connection pool. * * @param conn The connection to be commited and returned to the pool. */ diff --git a/rdbms/OcciConn.cpp b/rdbms/OcciConn.cpp index a379fab4a4672947349bec0eac3732b4c3811958..9b8cd0a8044cbc842bdef9ac6c88abc37e693338 100644 --- a/rdbms/OcciConn.cpp +++ b/rdbms/OcciConn.cpp @@ -138,5 +138,40 @@ std::list<std::string> OcciConn::getTableNames() { } } +//------------------------------------------------------------------------------ +// updateHealth +//------------------------------------------------------------------------------ +void OcciConn::updateHealth(const oracle::occi::SQLException &ex) { + using namespace oracle; + + // Error codes that identify an unhealthy connection + // The majority of these error codes were learnt from CASTOR + switch(ex.getErrorCode()) { + case 28: + case 1003: + case 1008: + case 1012: + case 1033: + case 1089: + case 2392: + case 2399: + case 3113: + case 3114: + case 3135: + case 12170: + case 12541: + case 12571: + case 24338: + case 12537: + case 25401: + case 25409: + case 32102: + setHealthy(false); + break; + default: + break; + }; +} + } // namespace rdbms } // namespace cta diff --git a/rdbms/OcciConn.hpp b/rdbms/OcciConn.hpp index d922bf7ce236b46a6d29e758318e765406de8460..59c01ee0faf6c2d43a0ec24e4d59d74e0b2b5b4c 100644 --- a/rdbms/OcciConn.hpp +++ b/rdbms/OcciConn.hpp @@ -105,6 +105,8 @@ public: private: + friend OcciStmt; + /** * Mutex used to serialize access to this object. */ @@ -120,6 +122,14 @@ private: */ oracle::occi::Connection *m_conn; + /** + * Determines whether or not the specified Oracle exception affects the status + * of the database connection and updates it accordingly. + * + * @param ex The Oracle exception. + */ + void updateHealth(const oracle::occi::SQLException &ex); + }; // class OcciConn } // namespace rdbms diff --git a/rdbms/OcciStmt.cpp b/rdbms/OcciStmt.cpp index af81221ba2c21d8d97aa2fc9002e665dec9e5561..2aaf5583aa11fa82b03c761e27d78a239fdfd6e6 100644 --- a/rdbms/OcciStmt.cpp +++ b/rdbms/OcciStmt.cpp @@ -23,7 +23,6 @@ #include "rdbms/OcciStmt.hpp" #include <cstring> -#include <iostream> #include <map> #include <sstream> #include <stdexcept> @@ -181,11 +180,9 @@ std::unique_ptr<Rset> OcciStmt::executeQuery() { try { return cta::make_unique<OcciRset>(*this, m_stmt->executeQuery()); - } catch(exception::Exception &ex) { - throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + - ex.getMessage().str()); - } catch(std::exception &se) { - throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + se.what()); + } catch(occi::SQLException &ex) { + m_conn.updateHealth(ex); + throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + ex.what()); } } @@ -197,11 +194,9 @@ void OcciStmt::executeNonQuery() { try { m_stmt->executeUpdate(); - } catch(exception::Exception &ex) { - throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + - ex.getMessage().str()); - } catch(std::exception &se) { - throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + se.what()); + } catch(occi::SQLException &ex) { + m_conn.updateHealth(ex); + throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + ex.what()); } }