]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1074] implemented connection pool for pgsql host mgr
authorRazvan Becheriu <razvan@isc.org>
Fri, 10 Jan 2020 18:57:42 +0000 (20:57 +0200)
committerRazvan Becheriu <razvan@isc.org>
Wed, 5 Feb 2020 21:00:32 +0000 (23:00 +0200)
src/lib/dhcpsrv/pgsql_host_data_source.cc
src/lib/dhcpsrv/pgsql_host_data_source.h
src/lib/dhcpsrv/pgsql_lease_mgr.cc
src/lib/dhcpsrv/pgsql_lease_mgr.h

index 692d71ce3d246d072c314e9de69d98c2cea7cfdd..8e6e0208fa576926ab48a0748723b9289f95c9c9 100644 (file)
@@ -15,6 +15,7 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/pgsql_host_data_source.h>
 #include <util/buffer.h>
+#include <util/multi_threading_mgr.h>
 #include <util/optional.h>
 
 #include <boost/algorithm/string/split.hpp>
@@ -24,6 +25,8 @@
 #include <boost/static_assert.hpp>
 
 #include <stdint.h>
+
+#include <mutex>
 #include <string>
 
 using namespace isc;
@@ -188,8 +191,7 @@ public:
     /// bound values extracted from host
     ///
     /// @throw DbOperationError if bind_array cannot be populated.
-    PsqlBindArrayPtr
-    createBindForSend(const HostPtr& host) {
+    PsqlBindArrayPtr createBindForSend(const HostPtr& host) {
         if (!host) {
             isc_throw(BadValue, "createBindForSend:: host object is NULL");
         }
@@ -311,6 +313,7 @@ public:
     /// a second time.
     ///
     /// @return HostPtr to the newly created Host object
+    ///
     /// @throw DbOperationError if the host cannot be created.
     HostPtr retrieveHost(const PgSqlResult& r, int row,
                          const HostID& peeked_host_id = 0) {
@@ -1173,10 +1176,9 @@ public:
     ///
     /// @return pointer to newly constructed bind_array containing the
     /// bound values extracted from host
-    PsqlBindArrayPtr
-    createBindForSend(const OptionDescriptor& opt_desc,
-                      const std::string& opt_space,
-                      const HostID& host_id) {
+    PsqlBindArrayPtr createBindForSend(const OptionDescriptor& opt_desc,
+                                       const std::string& opt_space,
+                                       const HostID& host_id) {
         // Hold pointer to the option to make sure it remains valid until
         // we complete a query.
         option_ = opt_desc.option_;
@@ -1263,11 +1265,72 @@ private:
     OptionPtr option_;
 };
 
-} // end of anonymous namespace
+}  // namespace
 
 namespace isc {
 namespace dhcp {
 
+/// @brief PostgreSQL Host Context
+///
+/// This class stores the thread context for the manager pool.
+class PgSqlHostContext {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param parameters See PgSqlHostMgr constructor.
+    PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters);
+
+    /// The exchange objects are used for transfer of data to/from the database.
+    /// They are pointed-to objects as the contents may change in "const" calls,
+    /// while the rest of this object does not.  (At alternative would be to
+    /// declare them as "mutable".)
+
+    /// @brief Pointer to the object representing an exchange which
+    /// can be used to retrieve hosts and DHCPv4 options.
+    boost::shared_ptr<PgSqlHostWithOptionsExchange> host_exchange_;
+
+    /// @brief Pointer to an object representing an exchange which can
+    /// be used to retrieve hosts, DHCPv6 options and IPv6 reservations.
+    boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
+
+    /// @brief Pointer to an object representing an exchange which can
+    /// be used to retrieve hosts, DHCPv4 and DHCPv6 options, and
+    /// IPv6 reservations using a single query.
+    boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
+
+    /// @brief Pointer to an object representing an exchange which can
+    /// be used to insert new IPv6 reservation.
+    boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
+
+    /// @brief Pointer to an object representing an exchange which can
+    /// be used to insert DHCPv4 or DHCPv6 option into dhcp4_options
+    /// or dhcp6_options table.
+    boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
+
+    /// @brief PostgreSQL connection
+    PgSqlConnection conn_;
+
+    /// @brief Indicates if the database is opened in read only mode.
+    bool is_readonly_;
+};
+
+/// @brief PostgreSQL Host Context Pool
+///
+/// This class provides a pool of contexts.
+class PgSqlHostContextPool {
+public:
+
+    /// @brief The vector of available contexts.
+    std::vector<PgSqlHostContextPtr> pool_;
+
+    /// @brief The mutex to protect pool access.
+    std::mutex mutex_;
+};
+
+/// @brief Type of pointers to context pools.
+typedef boost::shared_ptr<PgSqlHostContextPool> PgSqlHostContextPoolPtr;
+
 /// @brief Implementation of the @ref PgSqlHostDataSource.
 class PgSqlHostDataSourceImpl {
 public:
@@ -1321,8 +1384,20 @@ public:
     /// @brief Destructor.
     ~PgSqlHostDataSourceImpl();
 
+    /// @brief Create a new context.
+    ///
+    /// The database is opened with all the SQL commands pre-compiled.
+    ///
+    /// @return A new (never null) context.
+    ///
+    /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given.
+    /// @throw isc::db::DbOperationError An operation on the open database has
+    /// failed.
+    PgSqlHostContextPtr createContext() const;
+
     /// @brief Executes statements which insert a row into one of the tables.
     ///
+    /// @param ctx Context
     /// @param stindex Index of a statement being executed.
     /// @param bind Vector of PgsqlBindArray objects to be used for the query
     /// @param return_last_id flag indicating whether or not the insert
@@ -1336,33 +1411,42 @@ public:
     /// the value in the result set in the first col of the first row.
     ///
     /// @throw isc::db::DuplicateEntry Database throws duplicate entry error
-    uint64_t addStatement(PgSqlHostDataSourceImpl::StatementIndex stindex,
+    uint64_t addStatement(PgSqlHostContextPtr& ctx,
+                          PgSqlHostDataSourceImpl::StatementIndex stindex,
                           PsqlBindArrayPtr& bind,
                           const bool return_last_id = false);
 
     /// @brief Executes statements that delete records.
     ///
+    /// @param ctx Context
     /// @param stindex Index of a statement being executed.
     /// @param bind pointer to PsqlBindArray objects to be used for the query
+    ///
     /// @return true if any records were deleted, false otherwise
-    bool delStatement(PgSqlHostDataSourceImpl::StatementIndex stindex,
+    bool delStatement(PgSqlHostContextPtr& ctx,
+                      PgSqlHostDataSourceImpl::StatementIndex stindex,
                       PsqlBindArrayPtr& bind);
 
     /// @brief Inserts IPv6 Reservation into ipv6_reservation table.
     ///
+    /// @param ctx Context
     /// @param resv IPv6 Reservation to be added
     /// @param id ID of a host owning this reservation
-    void addResv(const IPv6Resrv& resv, const HostID& id);
+    void addResv(PgSqlHostContextPtr& ctx,
+                 const IPv6Resrv& resv,
+                 const HostID& id);
 
     /// @brief Inserts a single DHCP option into the database.
     ///
+    /// @param ctx Context
     /// @param stindex Index of a statement being executed.
     /// @param opt_desc Option descriptor holding information about an option
     /// to be inserted into the database.
     /// @param opt_space Option space name.
     /// @param subnet_id Subnet identifier.
     /// @param host_id Host identifier.
-    void addOption(const PgSqlHostDataSourceImpl::StatementIndex& stindex,
+    void addOption(PgSqlHostContextPtr& ctx,
+                   const PgSqlHostDataSourceImpl::StatementIndex& stindex,
                    const OptionDescriptor& opt_desc,
                    const std::string& opt_space,
                    const Optional<SubnetID>& subnet_id,
@@ -1370,12 +1454,14 @@ public:
 
     /// @brief Inserts multiple options into the database.
     ///
+    /// @param ctx Context
     /// @param stindex Index of a statement being executed.
     /// @param options_cfg An object holding a collection of options to be
     /// inserted into the database.
     /// @param host_id Host identifier retrieved using getColumnValue
     ///                in addStatement method
-    void addOptions(const StatementIndex& stindex,
+    void addOptions(PgSqlHostContextPtr& ctx,
+                    const StatementIndex& stindex,
                     const ConstCfgOptionPtr& options_cfg,
                     const uint64_t host_id);
 
@@ -1389,6 +1475,7 @@ public:
     /// Whether IPv6 reservations and/or options are assigned to the
     /// @ref Host objects depends on the type of the exchange object.
     ///
+    /// @param ctx Context
     /// @param stindex Statement index.
     /// @param bind Pointer to an array of PgSQL bindings.
     /// @param exchange Pointer to the exchange object used for the
@@ -1396,7 +1483,8 @@ public:
     /// @param [out] result Reference to the collection of hosts returned.
     /// @param single A boolean value indicating if a single host is
     /// expected to be returned, or multiple hosts.
-    void getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind,
+    void getHostCollection(PgSqlHostContextPtr& ctx,
+                           StatementIndex stindex, PsqlBindArrayPtr bind,
                            boost::shared_ptr<PgSqlHostExchange> exchange,
                            ConstHostCollection& result, bool single) const;
 
@@ -1405,6 +1493,7 @@ public:
     /// This method is used by both PgSqlHostDataSource::get4 and
     /// PgSqlHostDataSource::get6 methods.
     ///
+    /// @param ctx Context
     /// @param subnet_id Subnet identifier.
     /// @param identifier_type Identifier type.
     /// @param identifier_begin Pointer to a beginning of a buffer containing
@@ -1416,7 +1505,8 @@ public:
     ///
     /// @return Pointer to const instance of Host or null pointer if
     /// no host found.
-    ConstHostPtr getHost(const SubnetID& subnet_id,
+    ConstHostPtr getHost(PgSqlHostContextPtr& ctx,
+                         const SubnetID& subnet_id,
                          const Host::IdentifierType& identifier_type,
                          const uint8_t* identifier_begin,
                          const size_t identifier_len,
@@ -1429,8 +1519,10 @@ public:
     /// database. If the backend is operating in read-only mode this
     /// method will throw exception.
     ///
+    /// @param ctx Context
+    ///
     /// @throw DbReadOnly if backend is operating in read only mode.
-    void checkReadOnly() const;
+    void checkReadOnly(PgSqlHostContextPtr& ctx) const;
 
     /// @brief Returns PostgreSQL schema version of the open database
     ///
@@ -1442,36 +1534,11 @@ public:
     ///        has failed.
     std::pair<uint32_t, uint32_t> getVersion() const;
 
-    /// @brief Pointer to the object representing an exchange which
-    /// can be used to retrieve hosts and DHCPv4 options.
-    boost::shared_ptr<PgSqlHostWithOptionsExchange> host_exchange_;
-
-    /// @brief Pointer to an object representing an exchange which can
-    /// be used to retrieve hosts, DHCPv6 options and IPv6 reservations.
-    boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
-
-    /// @brief Pointer to an object representing an exchange which can
-    /// be used to retrieve hosts, DHCPv4 and DHCPv6 options, and
-    /// IPv6 reservations using a single query.
-    boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
-
-    /// @brief Pointer to an object representing an exchange which can
-    /// be used to insert new IPv6 reservation.
-    boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
-
-    /// @brief Pointer to an object representing an exchange which can
-    /// be used to insert DHCPv4 or DHCPv6 option into dhcp4_options
-    /// or dhcp6_options table.
-    boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
-
     /// @brief The parameters
     PgSqlConnection::ParameterMap parameters_;
 
-    /// @brief PgSQL connection
-    PgSqlConnection conn_;
-
-    /// @brief Indicates if the database is opened in read only mode.
-    bool is_readonly_;
+    /// @brief The pool of contexts
+    PgSqlHostContextPoolPtr pool_;
 };
 
 namespace {
@@ -1658,7 +1725,8 @@ TaggedStatementArray tagged_statements = { {
     // results in multiple rows being returned for the same host. The hosts are
     // retrieved by subnet id.
     {1,
-     { OID_INT8 }, "get_host_subid4",
+     { OID_INT8 },
+     "get_host_subid4",
      "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
      "  h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
      "  h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
@@ -1683,7 +1751,8 @@ TaggedStatementArray tagged_statements = { {
     // are usually many hosts in a subnet. The amount of returned data may
     // be huge.
     {1,
-     { OID_INT8 }, "get_host_subid6",
+     { OID_INT8 },
+     "get_host_subid6",
      "SELECT h.host_id, h.dhcp_identifier, "
      "  h.dhcp_identifier_type, h.dhcp4_subnet_id, "
      "  h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
@@ -1857,7 +1926,7 @@ TaggedStatementArray tagged_statements = { {
     // Using fixed scope_id = 3, which associates an option with host.
     {7,
      { OID_INT2, OID_BYTEA, OID_TEXT,
-       OID_VARCHAR, OID_BOOL, OID_TEXT, OID_INT8},
+       OID_VARCHAR, OID_BOOL, OID_TEXT, OID_INT8 },
      "insert_v4_host_option",
      "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
      "  persistent, user_context, host_id, scope_id) "
@@ -1869,7 +1938,7 @@ TaggedStatementArray tagged_statements = { {
     // Using fixed scope_id = 3, which associates an option with host.
     {7,
      { OID_INT2, OID_BYTEA, OID_TEXT,
-       OID_VARCHAR, OID_BOOL, OID_TEXT, OID_INT8},
+       OID_VARCHAR, OID_BOOL, OID_TEXT, OID_INT8 },
      "insert_v6_host_option",
      "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
      "  persistent, user_context, host_id, scope_id) "
@@ -1906,19 +1975,47 @@ TaggedStatementArray tagged_statements = { {
 }
 };
 
-}; // end anonymous namespace
+}  // namespace
+
+// PgSqlHostContext Constructor
 
-PgSqlHostDataSourceImpl::
-PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
-    : host_exchange_(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY)),
-      host_ipv6_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY)),
-      host_ipv46_exchange_(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::
-                                                     DHCP4_AND_DHCP6)),
-      host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
-      host_option_exchange_(new PgSqlOptionExchange()),
-      parameters_(parameters),
-      conn_(parameters),
-      is_readonly_(false) {
+PgSqlHostContext::PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters)
+    : conn_(parameters), is_readonly_(true) {
+}
+
+// PgSqlHostContextAlloc Constructor and Destructor
+
+PgSqlHostDataSource::PgSqlHostContextAlloc::PgSqlHostContextAlloc(
+    const PgSqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
+
+    if (MultiThreadingMgr::instance().getMode()) {
+        {
+            lock_guard<mutex> lock(mgr_.pool_->mutex_);
+            if (!mgr_.pool_->pool_.empty()) {
+                ctx_ = mgr_.pool_->pool_.back();
+                mgr_.pool_->pool_.pop_back();
+            }
+        }
+        if (!ctx_) {
+            ctx_ = mgr_.createContext();
+        }
+    } else {
+        if (mgr_.pool_->pool_.empty()) {
+            isc_throw(Unexpected, "No available PostgreSQL lease context?!");
+        }
+        ctx_ = mgr_.pool_->pool_.back();
+    }
+}
+
+PgSqlHostDataSource::PgSqlHostContextAlloc::~PgSqlHostContextAlloc() {
+    if (MultiThreadingMgr::instance().getMode()) {
+        lock_guard<mutex> lock(mgr_.pool_->mutex_);
+        mgr_.pool_->pool_.push_back(ctx_);
+    }
+}
+
+PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
+    : parameters_(parameters) {
 
     // Validate the schema version first.
     std::pair<uint32_t, uint32_t> code_version(PG_SCHEMA_VERSION_MAJOR,
@@ -1928,41 +2025,61 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
         isc_throw(DbOpenError,
                   "PostgreSQL schema version mismatch: need version: "
                       << code_version.first << "." << code_version.second
-                      << " found version:  " << db_version.first << "."
+                      << " found version: " << db_version.first << "."
                       << db_version.second);
     }
 
+    // Create an initial context.
+    pool_.reset(new PgSqlHostContextPool());
+    pool_->pool_.push_back(createContext());
+}
+
+// Create context.
+
+PgSqlHostContextPtr
+PgSqlHostDataSourceImpl::createContext() const {
+    PgSqlHostContextPtr ctx(new PgSqlHostContext(parameters_));
+
     // Open the database.
-    conn_.openDatabase();
+    ctx->conn_.openDatabase();
 
     // Now prepare the SQL statements.
-    conn_.prepareStatements(tagged_statements.begin(),
-                            tagged_statements.begin() + WRITE_STMTS_BEGIN);
+    ctx->conn_.prepareStatements(tagged_statements.begin(),
+                                 tagged_statements.begin() + WRITE_STMTS_BEGIN);
 
     // Check if the backend is explicitly configured to operate with
     // read only access to the database.
-    is_readonly_ = conn_.configuredReadOnly();
+    ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
 
     // If we are using read-write mode for the database we also prepare
     // statements for INSERTS etc.
-    if (!is_readonly_) {
-        conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
-                                tagged_statements.end());
+    if (!ctx->is_readonly_) {
+        ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
+                                     tagged_statements.end());
 
     } else {
         LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB_READONLY);
     }
+
+    ctx->host_exchange_.reset(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY));
+    ctx->host_ipv6_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY));
+    ctx->host_ipv46_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
+    ctx->host_ipv6_reservation_exchange_.reset(new PgSqlIPv6ReservationExchange());
+    ctx->host_option_exchange_.reset(new PgSqlOptionExchange());
+
+    return (ctx);
 }
 
 PgSqlHostDataSourceImpl::~PgSqlHostDataSourceImpl() {
 }
 
 uint64_t
-PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
+PgSqlHostDataSourceImpl::addStatement(PgSqlHostContextPtr& ctx,
+                                      StatementIndex stindex,
                                       PsqlBindArrayPtr& bind_array,
                                       const bool return_last_id) {
     uint64_t last_id = 0;
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
@@ -1972,13 +2089,13 @@ PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
 
     if (s != PGRES_COMMAND_OK) {
         // Failure: check for the special case of duplicate entry.
-        if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
+        if (ctx->conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
             isc_throw(DuplicateEntry, "Database duplicate entry error");
         }
 
         // Connection determines if the error is fatal or not, and
         // throws the appropriate exception
-        conn_.checkStatementError(r, tagged_statements[stindex]);
+        ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
     }
 
     if (return_last_id) {
@@ -1989,9 +2106,10 @@ PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
 }
 
 bool
-PgSqlHostDataSourceImpl::delStatement(StatementIndex stindex,
+PgSqlHostDataSourceImpl::delStatement(PgSqlHostContextPtr& ctx,
+                                      StatementIndex stindex,
                                       PsqlBindArrayPtr& bind_array) {
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
@@ -2002,7 +2120,7 @@ PgSqlHostDataSourceImpl::delStatement(StatementIndex stindex,
     if (s != PGRES_COMMAND_OK) {
         // Connection determines if the error is fatal or not, and
         // throws the appropriate exception
-        conn_.checkStatementError(r, tagged_statements[stindex]);
+        ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
     }
 
     // Now check how many rows (hosts) were deleted. This should be either
@@ -2016,27 +2134,29 @@ PgSqlHostDataSourceImpl::delStatement(StatementIndex stindex,
 }
 
 void
-PgSqlHostDataSourceImpl::addResv(const IPv6Resrv& resv,
+PgSqlHostDataSourceImpl::addResv(PgSqlHostContextPtr& ctx,
+                                 const IPv6Resrv& resv,
                                  const HostID& id) {
-    PsqlBindArrayPtr bind_array;
-    bind_array = host_ipv6_reservation_exchange_->createBindForSend(resv, id);
-    addStatement(INSERT_V6_RESRV, bind_array);
+    PsqlBindArrayPtr bind_array = ctx->host_ipv6_reservation_exchange_->createBindForSend(resv, id);
+
+    addStatement(ctx, INSERT_V6_RESRV, bind_array);
 }
 
 void
-PgSqlHostDataSourceImpl::addOption(const StatementIndex& stindex,
+PgSqlHostDataSourceImpl::addOption(PgSqlHostContextPtr& ctx,
+                                   const StatementIndex& stindex,
                                    const OptionDescriptor& opt_desc,
                                    const std::string& opt_space,
                                    const Optional<SubnetID>&,
                                    const HostID& id) {
-    PsqlBindArrayPtr bind_array;
-    bind_array = host_option_exchange_->createBindForSend(opt_desc, opt_space,
-                                                          id);
-    addStatement(stindex, bind_array);
+    PsqlBindArrayPtr bind_array = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, id);
+
+    addStatement(ctx, stindex, bind_array);
 }
 
 void
-PgSqlHostDataSourceImpl::addOptions(const StatementIndex& stindex,
+PgSqlHostDataSourceImpl::addOptions(PgSqlHostContextPtr& ctx,
+                                    const StatementIndex& stindex,
                                     const ConstCfgOptionPtr& options_cfg,
                                     const uint64_t host_id) {
     // Get option space names and vendor space names and combine them within a
@@ -2048,36 +2168,35 @@ PgSqlHostDataSourceImpl::addOptions(const StatementIndex& stindex,
 
     // For each option space retrieve all options and insert them into the
     // database.
-    for (std::list<std::string>::const_iterator space = option_spaces.begin();
-         space != option_spaces.end(); ++space) {
+    for (auto space = option_spaces.begin(); space != option_spaces.end(); ++space) {
         OptionContainerPtr options = options_cfg->getAll(*space);
         if (options && !options->empty()) {
-            for (OptionContainer::const_iterator opt = options->begin();
-                 opt != options->end(); ++opt) {
-                addOption(stindex, *opt, *space, Optional<SubnetID>(),
-                          host_id);
+            for (auto opt = options->begin(); opt != options->end(); ++opt) {
+                addOption(ctx, stindex, *opt, *space, Optional<SubnetID>(), host_id);
             }
         }
     }
 }
 
 void
-PgSqlHostDataSourceImpl::
-getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind_array,
-                  boost::shared_ptr<PgSqlHostExchange> exchange,
-                  ConstHostCollection& result, bool single) const {
+PgSqlHostDataSourceImpl::getHostCollection(PgSqlHostContextPtr& ctx,
+                                           StatementIndex stindex,
+                                           PsqlBindArrayPtr bind_array,
+                                           boost::shared_ptr<PgSqlHostExchange> exchange,
+                                           ConstHostCollection& result,
+                                           bool single) const {
 
     exchange->clear();
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
                                  &bind_array->formats_[0], 0));
 
-    conn_.checkStatementError(r, tagged_statements[stindex]);
+    ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
 
     int rows = r.getRows();
-    for(int row = 0; row < rows; ++row) {
+    for (int row = 0; row < rows; ++row) {
         exchange->processRowData(result, r, row);
 
         if (single && result.size() > 1) {
@@ -2089,13 +2208,13 @@ getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind_array,
 }
 
 ConstHostPtr
-PgSqlHostDataSourceImpl::
-getHost(const SubnetID& subnet_id,
-        const Host::IdentifierType& identifier_type,
-        const uint8_t* identifier_begin,
-        const size_t identifier_len,
-        StatementIndex stindex,
-        boost::shared_ptr<PgSqlHostExchange> exchange) const {
+PgSqlHostDataSourceImpl::getHost(PgSqlHostContextPtr& ctx,
+                                 const SubnetID& subnet_id,
+                                 const Host::IdentifierType& identifier_type,
+                                 const uint8_t* identifier_begin,
+                                 const size_t identifier_len,
+                                 StatementIndex stindex,
+                                 boost::shared_ptr<PgSqlHostExchange> exchange) const {
 
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
@@ -2110,25 +2229,27 @@ getHost(const SubnetID& subnet_id,
     bind_array->add(identifier_begin, identifier_len);
 
     ConstHostCollection collection;
-    getHostCollection(stindex, bind_array, exchange, collection, true);
+    getHostCollection(ctx, stindex, bind_array, exchange, collection, true);
 
     // Return single record if present, else clear the host.
     ConstHostPtr result;
-    if (!collection.empty())
+    if (!collection.empty()) {
         result = *collection.begin();
+    }
 
     return (result);
 }
 
-std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
+std::pair<uint32_t, uint32_t>
+PgSqlHostDataSourceImpl::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_PGSQL_HOST_DB_GET_VERSION);
     return (PgSqlConnection::getVersion(parameters_));
 }
 
 void
-PgSqlHostDataSourceImpl::checkReadOnly() const {
-    if (is_readonly_) {
+PgSqlHostDataSourceImpl::checkReadOnly(PgSqlHostContextPtr& ctx) const {
+    if (ctx->is_readonly_) {
         isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
                   " to operate in read only mode");
     }
@@ -2136,44 +2257,46 @@ PgSqlHostDataSourceImpl::checkReadOnly() const {
 
 /*********** PgSqlHostDataSource *********************/
 
-PgSqlHostDataSource::
-PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
+PgSqlHostDataSource::PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
     : impl_(new PgSqlHostDataSourceImpl(parameters)) {
 }
 
 PgSqlHostDataSource::~PgSqlHostDataSource() {
-    delete impl_;
 }
 
 void
 PgSqlHostDataSource::add(const HostPtr& host) {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // If operating in read-only mode, throw exception.
-    impl_->checkReadOnly();
+    impl_->checkReadOnly(ctx);
 
     // Initiate PostgreSQL transaction as we will have to make multiple queries
     // to insert host information into multiple tables. If that fails on
     // any stage, the transaction will be rolled back by the destructor of
     // the PgSqlTransaction class.
-    PgSqlTransaction transaction(impl_->conn_);
+    PgSqlTransaction transaction(ctx->conn_);
 
     // Create the PgSQL Bind array for the host
-    PsqlBindArrayPtr bind_array = impl_->host_exchange_->createBindForSend(host);
+    PsqlBindArrayPtr bind_array = ctx->host_exchange_->createBindForSend(host);
 
     // ... and insert the host.
-    uint32_t host_id = impl_->addStatement(PgSqlHostDataSourceImpl::INSERT_HOST,
+    uint32_t host_id = impl_->addStatement(ctx, PgSqlHostDataSourceImpl::INSERT_HOST,
                                            bind_array, true);
 
     // Insert DHCPv4 options.
     ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
     if (cfg_option4) {
-        impl_->addOptions(PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
+        impl_->addOptions(ctx, PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
                           cfg_option4, host_id);
     }
 
     // Insert DHCPv6 options.
     ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
     if (cfg_option6) {
-        impl_->addOptions(PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
+        impl_->addOptions(ctx, PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
                           cfg_option6, host_id);
     }
 
@@ -2182,7 +2305,7 @@ PgSqlHostDataSource::add(const HostPtr& host) {
     if (std::distance(v6resv.first, v6resv.second) > 0) {
         for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
              ++resv) {
-            impl_->addResv(resv->second, host_id);
+            impl_->addResv(ctx, resv->second, host_id);
         }
     }
 
@@ -2192,14 +2315,18 @@ PgSqlHostDataSource::add(const HostPtr& host) {
 
 bool
 PgSqlHostDataSource::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // If operating in read-only mode, throw exception.
-    impl_->checkReadOnly();
+    impl_->checkReadOnly(ctx);
 
     if (addr.isV4()) {
         PsqlBindArrayPtr bind_array(new PsqlBindArray());
         bind_array->add(subnet_id);
         bind_array->add(addr);
-        return (impl_->delStatement(PgSqlHostDataSourceImpl::DEL_HOST_ADDR4,
+        return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_ADDR4,
                                     bind_array));
     }
 
@@ -2217,6 +2344,12 @@ PgSqlHostDataSource::del4(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
                           const uint8_t* identifier_begin,
                           const size_t identifier_len) {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
+    // If operating in read-only mode, throw exception.
+    impl_->checkReadOnly(ctx);
 
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2229,7 +2362,7 @@ PgSqlHostDataSource::del4(const SubnetID& subnet_id,
     // identifier
     bind_array->add(identifier_begin, identifier_len);
 
-    return (impl_->delStatement(PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID,
+    return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID,
                                 bind_array));
 }
 
@@ -2238,6 +2371,13 @@ PgSqlHostDataSource::del6(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
                           const uint8_t* identifier_begin,
                           const size_t identifier_len) {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
+    // If operating in read-only mode, throw exception.
+    impl_->checkReadOnly(ctx);
+
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
     // Subnet-id
@@ -2249,7 +2389,7 @@ PgSqlHostDataSource::del6(const SubnetID& subnet_id,
     // identifier
     bind_array->add(identifier_begin, identifier_len);
 
-    return (impl_->delStatement(PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID,
+    return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID,
                                 bind_array));
 }
 
@@ -2257,6 +2397,10 @@ ConstHostCollection
 PgSqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
                             const uint8_t* identifier_begin,
                             const size_t identifier_len) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2267,14 +2411,18 @@ PgSqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
     bind_array->add(static_cast<uint8_t>(identifier_type));
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_DHCPID,
-                             bind_array, impl_->host_ipv46_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_DHCPID,
+                             bind_array, ctx->host_ipv46_exchange_, result, false);
+
     return (result);
 }
 
 ConstHostCollection
 PgSqlHostDataSource::getAll4(const SubnetID& subnet_id) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2282,15 +2430,18 @@ PgSqlHostDataSource::getAll4(const SubnetID& subnet_id) const {
     bind_array->add(subnet_id);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID4,
-                             bind_array, impl_->host_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4,
+                             bind_array, ctx->host_exchange_, result, false);
 
     return (result);
 }
 
 ConstHostCollection
 PgSqlHostDataSource::getAll6(const SubnetID& subnet_id) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2298,15 +2449,18 @@ PgSqlHostDataSource::getAll6(const SubnetID& subnet_id) const {
     bind_array->add(subnet_id);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID6,
-                             bind_array, impl_->host_ipv6_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6,
+                             bind_array, ctx->host_ipv6_exchange_, result, false);
 
     return (result);
 }
 
 ConstHostCollection
 PgSqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2314,15 +2468,19 @@ PgSqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
     bind_array->add(hostname);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME,
-                             bind_array, impl_->host_ipv46_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME,
+                             bind_array, ctx->host_ipv46_exchange_, result, false);
+
     return (result);
 }
 
 ConstHostCollection
 PgSqlHostDataSource::getAllbyHostname4(const std::string& hostname,
                                        const SubnetID& subnet_id) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2333,9 +2491,8 @@ PgSqlHostDataSource::getAllbyHostname4(const std::string& hostname,
     bind_array->add(subnet_id);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4,
-                             bind_array, impl_->host_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4,
+                             bind_array, ctx->host_exchange_, result, false);
 
     return (result);
 }
@@ -2343,6 +2500,10 @@ PgSqlHostDataSource::getAllbyHostname4(const std::string& hostname,
 ConstHostCollection
 PgSqlHostDataSource::getAllbyHostname6(const std::string& hostname,
                                        const SubnetID& subnet_id) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2353,9 +2514,8 @@ PgSqlHostDataSource::getAllbyHostname6(const std::string& hostname,
     bind_array->add(subnet_id);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6,
-                             bind_array, impl_->host_ipv6_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6,
+                             bind_array, ctx->host_ipv6_exchange_, result, false);
 
     return (result);
 }
@@ -2365,6 +2525,10 @@ PgSqlHostDataSource::getPage4(const SubnetID& subnet_id,
                               size_t& /*source_index*/,
                               uint64_t lower_host_id,
                               const HostPageSize& page_size) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2380,9 +2544,8 @@ PgSqlHostDataSource::getPage4(const SubnetID& subnet_id,
     bind_array->add(page_size_data);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE,
-                             bind_array, impl_->host_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE,
+                             bind_array, ctx->host_exchange_, result, false);
 
     return (result);
 }
@@ -2392,6 +2555,10 @@ PgSqlHostDataSource::getPage6(const SubnetID& subnet_id,
                               size_t& /*source_index*/,
                               uint64_t lower_host_id,
                               const HostPageSize& page_size) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
 
@@ -2407,15 +2574,17 @@ PgSqlHostDataSource::getPage6(const SubnetID& subnet_id,
     bind_array->add(page_size_data);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE,
-                             bind_array, impl_->host_ipv6_exchange_,
-                             result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE,
+                             bind_array, ctx->host_ipv6_exchange_, result, false);
 
     return (result);
 }
 
 ConstHostCollection
 PgSqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
 
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
@@ -2424,8 +2593,8 @@ PgSqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
     bind_array->add(address);
 
     ConstHostCollection result;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_ADDR, bind_array,
-                             impl_->host_exchange_, result, false);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_ADDR,
+                             bind_array, ctx->host_exchange_, result, false);
 
     return (result);
 }
@@ -2435,16 +2604,22 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
                           const uint8_t* identifier_begin,
                           const size_t identifier_len) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
 
-    return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
-                           identifier_len,
+    return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
                            PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID,
-                           impl_->host_exchange_));
+                           ctx->host_exchange_));
 }
 
 ConstHostPtr
 PgSqlHostDataSource::get4(const SubnetID& subnet_id,
                           const asiolink::IOAddress& address) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     if (!address.isV4()) {
         isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
                   " wrong address type, address supplied is an IPv6 address");
@@ -2460,14 +2635,14 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id,
     bind_array->add(address);
 
     ConstHostCollection collection;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
-                             bind_array, impl_->host_exchange_, collection,
-                             true);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
+                             bind_array, ctx->host_exchange_, collection, true);
 
     // Return single record if present, else clear the host.
     ConstHostPtr result;
-    if (!collection.empty())
+    if (!collection.empty()) {
         result = *collection.begin();
+    }
 
     return (result);
 }
@@ -2477,16 +2652,26 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
                           const uint8_t* identifier_begin,
                           const size_t identifier_len) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
 
-    return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
-                   identifier_len, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID,
-                   impl_->host_ipv6_exchange_));
+    return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
+                           PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID,
+                           ctx->host_ipv6_exchange_));
 }
 
 ConstHostPtr
 PgSqlHostDataSource::get6(const asiolink::IOAddress& prefix,
                           const uint8_t prefix_len) const {
-    /// @todo: Check that prefix is v6 address, not v4.
+    if (!prefix.isV6()) {
+        isc_throw(BadValue, "PgSqlHostDataSource::get6(prefix, prefix_len): "
+                  "wrong address type, address supplied is an IPv4 address");
+    }
+
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
 
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
@@ -2498,9 +2683,8 @@ PgSqlHostDataSource::get6(const asiolink::IOAddress& prefix,
     bind_array->add(prefix_len);
 
     ConstHostCollection collection;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_PREFIX,
-                             bind_array, impl_->host_ipv6_exchange_,
-                             collection, true);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PREFIX,
+                             bind_array, ctx->host_ipv6_exchange_, collection, true);
 
     // Return single record if present, else clear the host.
     ConstHostPtr result;
@@ -2514,7 +2698,14 @@ PgSqlHostDataSource::get6(const asiolink::IOAddress& prefix,
 ConstHostPtr
 PgSqlHostDataSource::get6(const SubnetID& subnet_id,
                           const asiolink::IOAddress& address) const {
-    /// @todo: Check that prefix is v6 address, not v4.
+    if (!address.isV6()) {
+        isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
+                  "wrong address type, address supplied is an IPv4 address");
+    }
+
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
 
     // Set up the WHERE clause value
     PsqlBindArrayPtr bind_array(new PsqlBindArray());
@@ -2526,9 +2717,8 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id,
     bind_array->add(address);
 
     ConstHostCollection collection;
-    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
-                             bind_array, impl_->host_ipv6_exchange_,
-                             collection, true);
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
+                             bind_array, ctx->host_ipv6_exchange_, collection, true);
 
     // Return single record if present, else clear the host.
     ConstHostPtr result;
@@ -2541,38 +2731,53 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id,
 
 // Miscellaneous database methods.
 
-std::string PgSqlHostDataSource::getName() const {
+std::string
+PgSqlHostDataSource::getName() const {
     std::string name = "";
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     try {
-        name = impl_->conn_.getParameter("name");
+        name = ctx->conn_.getParameter("name");
     } catch (...) {
         // Return an empty name
     }
     return (name);
 }
 
-std::string PgSqlHostDataSource::getDescription() const {
+std::string
+PgSqlHostDataSource::getDescription() const {
     return (std::string("Host data source that stores host information"
                         "in PostgreSQL database"));
 }
 
-std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
+std::pair<uint32_t, uint32_t>
+PgSqlHostDataSource::getVersion() const {
     return(impl_->getVersion());
 }
 
 void
 PgSqlHostDataSource::commit() {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // If operating in read-only mode, throw exception.
-    impl_->checkReadOnly();
-    impl_->conn_.commit();
+    impl_->checkReadOnly(ctx);
+    ctx->conn_.commit();
 }
 
 void
 PgSqlHostDataSource::rollback() {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
     // If operating in read-only mode, throw exception.
-    impl_->checkReadOnly();
-    impl_->conn_.rollback();
+    impl_->checkReadOnly(ctx);
+    ctx->conn_.rollback();
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index 4b4a662784d4d3757b095c8432515c163ce09a0f..42c211789aa1de8a5e08a2c38538374c02c4bc3c 100644 (file)
@@ -17,6 +17,15 @@ namespace dhcp {
 /// Forward declaration to the implementation of the @ref PgSqlHostDataSource.
 class PgSqlHostDataSourceImpl;
 
+/// @brief Type of pointers to PgSqlHostDataSourceImpl.
+typedef boost::shared_ptr<PgSqlHostDataSourceImpl> PgSqlHostDataSourceImplPtr;
+
+/// Forward declaration for the thread context for the manager pool.
+class PgSqlHostContext;
+
+/// @brief Type of pointers to contexts.
+typedef boost::shared_ptr<PgSqlHostContext> PgSqlHostContextPtr;
+
 /// @brief PostgreSQL Host Data Source
 ///
 /// This class implements the @ref isc::dhcp::BaseHostDataSource interface to
@@ -32,7 +41,7 @@ class PgSqlHostDataSourceImpl;
 /// - IDENT_CIRCUIT_ID
 /// - IDENT_CLIENT_ID
 ///
-class PgSqlHostDataSource: public BaseHostDataSource {
+class PgSqlHostDataSource : public BaseHostDataSource {
 public:
 
     /// @brief Constructor
@@ -107,7 +116,8 @@ public:
     /// @param addr specified address.
     /// @return true if deletion was successful, false if the host was not there.
     /// @throw various exceptions in case of errors
-    virtual bool del(const SubnetID& subnet_id, const asiolink::IOAddress& addr);
+    virtual bool del(const SubnetID& subnet_id,
+                     const asiolink::IOAddress& addr);
 
     /// @brief Attempts to delete a host by (subnet4-id, identifier type, identifier)
     ///
@@ -123,7 +133,8 @@ public:
     /// @throw various exceptions in case of errors
     virtual bool del4(const SubnetID& subnet_id,
                       const Host::IdentifierType& identifier_type,
-                      const uint8_t* identifier_begin, const size_t identifier_len);
+                      const uint8_t* identifier_begin,
+                      const size_t identifier_len);
 
     /// @brief Attempts to delete a host by (subnet6-id, identifier type, identifier)
     ///
@@ -139,7 +150,8 @@ public:
     /// @throw various exceptions in case of errors
     virtual bool del6(const SubnetID& subnet_id,
                       const Host::IdentifierType& identifier_type,
-                      const uint8_t* identifier_begin, const size_t identifier_len);
+                      const uint8_t* identifier_begin,
+                      const size_t identifier_len);
 
     /// @brief Return all hosts connected to any subnet for which reservations
     /// have been made using a specified identifier.
@@ -154,9 +166,9 @@ public:
     /// @param identifier_len Identifier length.
     ///
     /// @return Collection of const @c Host objects.
-    virtual ConstHostCollection
-    getAll(const Host::IdentifierType& identifier_type,
-           const uint8_t* identifier_begin, const size_t identifier_len) const;
+    virtual ConstHostCollection getAll(const Host::IdentifierType& identifier_type,
+                                       const uint8_t* identifier_begin,
+                                       const size_t identifier_len) const;
 
     /// @brief Return all hosts in a DHCPv4 subnet.
     ///
@@ -167,8 +179,7 @@ public:
     /// @param subnet_id subnet identifier to filter by
     ///
     /// @return Collection of const @ref Host objects.
-    virtual ConstHostCollection
-    getAll4(const SubnetID& subnet_id) const;
+    virtual ConstHostCollection getAll4(const SubnetID& subnet_id) const;
 
     /// @brief Return all hosts in a DHCPv6 subnet.
     ///
@@ -179,8 +190,7 @@ public:
     /// @param subnet_id subnet identifier to filter by
     ///
     /// @return Collection of const @ref Host objects.
-    virtual ConstHostCollection
-    getAll6(const SubnetID& subnet_id) const;
+    virtual ConstHostCollection getAll6(const SubnetID& subnet_id) const;
 
     /// @brief Return all hosts with a hostname.
     ///
@@ -192,8 +202,7 @@ public:
     /// @param hostname The lower case hostname.
     ///
     /// @return Collection of const @c Host objects.
-    virtual ConstHostCollection
-    getAllbyHostname(const std::string& hostname) const;
+    virtual ConstHostCollection getAllbyHostname(const std::string& hostname) const;
 
     /// @brief Return all hosts with a hostname in a DHCPv4 subnet.
     ///
@@ -204,8 +213,8 @@ public:
     /// @param subnet_id Subnet identifier.
     ///
     /// @return Collection of const @c Host objects.
-    virtual ConstHostCollection
-    getAllbyHostname4(const std::string& hostname, const SubnetID& subnet_id) const;
+    virtual ConstHostCollection getAllbyHostname4(const std::string& hostname,
+                                                  const SubnetID& subnet_id) const;
 
     /// @brief Return all hosts with a hostname in a DHCPv6 subnet.
     ///
@@ -216,8 +225,8 @@ public:
     /// @param subnet_id Subnet identifier.
     ///
     /// @return Collection of const @c Host objects.
-    virtual ConstHostCollection
-    getAllbyHostname6(const std::string& hostname, const SubnetID& subnet_id) const;
+    virtual ConstHostCollection getAllbyHostname6(const std::string& hostname,
+                                                  const SubnetID& subnet_id) const;
 
     /// @brief Returns range of hosts in a DHCPv4 subnet.
     ///
@@ -236,11 +245,10 @@ public:
     /// @param page_size maximum size of the page returned.
     ///
     /// @return Collection of const @c Host objects (may be empty).
-    virtual ConstHostCollection
-    getPage4(const SubnetID& subnet_id,
-             size_t& source_index,
-             uint64_t lower_host_id,
-             const HostPageSize& page_size) const;
+    virtual ConstHostCollection getPage4(const SubnetID& subnet_id,
+                                         size_t& source_index,
+                                         uint64_t lower_host_id,
+                                         const HostPageSize& page_size) const;
 
     /// @brief Returns range of hosts in a DHCPv6 subnet.
     ///
@@ -259,11 +267,10 @@ public:
     /// @param page_size maximum size of the page returned.
     ///
     /// @return Collection of const @c Host objects (may be empty).
-    virtual ConstHostCollection
-    getPage6(const SubnetID& subnet_id,
-             size_t& source_index,
-             uint64_t lower_host_id,
-             const HostPageSize& page_size) const;
+    virtual ConstHostCollection getPage6(const SubnetID& subnet_id,
+                                         size_t& source_index,
+                                         uint64_t lower_host_id,
+                                         const HostPageSize& page_size) const;
 
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
@@ -273,8 +280,7 @@ public:
     /// @param address IPv4 address for which the @c Host object is searched.
     ///
     /// @return Collection of const @c Host objects.
-    virtual ConstHostCollection
-    getAll4(const asiolink::IOAddress& address) const;
+    virtual ConstHostCollection getAll4(const asiolink::IOAddress& address) const;
 
     /// @brief Returns a host connected to the IPv4 subnet.
     ///
@@ -286,9 +292,10 @@ public:
     ///
     /// @return Const @c Host object for which reservation has been made using
     /// the specified identifier.
-    virtual ConstHostPtr
-    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
-         const uint8_t* identifier_begin, const size_t identifier_len) const;
+    virtual ConstHostPtr get4(const SubnetID& subnet_id,
+                              const Host::IdentifierType& identifier_type,
+                              const uint8_t* identifier_begin,
+                              const size_t identifier_len) const;
 
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
@@ -304,8 +311,8 @@ public:
     ///
     /// @return Const @c Host object using a specified IPv4 address.
     /// @throw BadValue is given an IPv6 address
-    virtual ConstHostPtr
-    get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
+    virtual ConstHostPtr get4(const SubnetID& subnet_id,
+                              const asiolink::IOAddress& address) const;
 
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
@@ -317,9 +324,10 @@ public:
     ///
     /// @return Const @c Host object for which reservation has been made using
     /// the specified identifier.
-    virtual ConstHostPtr
-    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
-         const uint8_t* identifier_begin, const size_t identifier_len) const;
+    virtual ConstHostPtr get6(const SubnetID& subnet_id,
+                              const Host::IdentifierType& identifier_type,
+                              const uint8_t* identifier_begin,
+                              const size_t identifier_len) const;
 
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
@@ -327,8 +335,8 @@ public:
     /// @param prefix_len IPv6 prefix length.
     ///
     /// @return Const @c Host object using a specified IPv6 prefix.
-    virtual ConstHostPtr
-    get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
+    virtual ConstHostPtr get6(const asiolink::IOAddress& prefix,
+                              const uint8_t prefix_len) const;
 
     /// @brief Returns a host connected to the IPv6 subnet and having
     /// a reservation for a specified IPv6 address or prefix.
@@ -337,8 +345,8 @@ public:
     /// @param address reserved IPv6 address/prefix.
     ///
     /// @return Const @c Host object using a specified IPv6 address/prefix.
-    virtual ConstHostPtr
-    get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
+    virtual ConstHostPtr get6(const SubnetID& subnet_id,
+                              const asiolink::IOAddress& address) const;
 
     /// @brief Return backend type
     ///
@@ -387,9 +395,34 @@ public:
     /// Rolls back all pending database operations.
     virtual void rollback();
 
+    /// @brief Context RAII Allocator.
+    class PgSqlHostContextAlloc {
+    public:
+
+        /// @brief Constructor
+        ///
+        /// This constructor takes a context of the pool if one is available
+        /// or creates a new one.
+        ///
+        /// @param mgr A parent instance
+        PgSqlHostContextAlloc(const PgSqlHostDataSourceImpl& mgr);
+
+        /// @brief Destructor
+        ///
+        /// This destructor puts back the context in the pool.
+        ~PgSqlHostContextAlloc();
+
+        /// @brief The context
+        PgSqlHostContextPtr ctx_;
+
+    private:
+        /// @brief The manager
+        const PgSqlHostDataSourceImpl& mgr_;
+    };
+
 private:
     /// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
-    PgSqlHostDataSourceImpl* impl_;
+    PgSqlHostDataSourceImplPtr impl_;
 };
 
 }
index 7ef9ab42f07d1023014e8278a9b128abc60515ce..b0af21717c0a27eafd8ad57acf3d43def261a909 100644 (file)
@@ -841,7 +841,7 @@ public:
             /// prevent cryptic errors during conversions from NULL
             /// to actual values.
 
-            isc::asiolink::IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
+            IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
 
             convertFromBytea(r, row, DUID_COL, duid_buffer_, sizeof(duid_buffer_), duid_length_);
             DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
@@ -1147,8 +1147,8 @@ protected:
 
 // PgSqlLeaseContext Constructor
 
-PgSqlLeaseContext::PgSqlLeaseContext(
-    const DatabaseConnection::ParameterMap& parameters) : conn_(parameters) {
+PgSqlLeaseContext::PgSqlLeaseContext(const DatabaseConnection::ParameterMap& parameters)
+    : conn_(parameters) {
 }
 
 // PgSqlLeaseContextAlloc Constructor and Destructor
@@ -1216,7 +1216,6 @@ PgSqlLeaseMgr::~PgSqlLeaseMgr() {
 
 PgSqlLeaseContextPtr
 PgSqlLeaseMgr::createContext() const {
-
     PgSqlLeaseContextPtr ctx(new PgSqlLeaseContext(parameters_));
 
     // Open the database.
@@ -1252,7 +1251,7 @@ PgSqlLeaseMgr::getDBVersion() {
 }
 
 bool
-PgSqlLeaseMgr::addLeaseCommon(PgSqlLeaseContextPtr ctx,
+PgSqlLeaseMgr::addLeaseCommon(PgSqlLeaseContextPtr& ctx,
                               StatementIndex stindex,
                               PsqlBindArray& bind_array) {
     PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
@@ -1318,7 +1317,7 @@ PgSqlLeaseMgr::addLease(const Lease6Ptr& lease) {
 
 template <typename Exchange, typename LeaseCollection>
 void
-PgSqlLeaseMgr::getLeaseCollection(PgSqlLeaseContextPtr ctx,
+PgSqlLeaseMgr::getLeaseCollection(PgSqlLeaseContextPtr& ctx,
                                   StatementIndex stindex,
                                   PsqlBindArray& bind_array,
                                   Exchange& exchange,
@@ -1346,7 +1345,7 @@ PgSqlLeaseMgr::getLeaseCollection(PgSqlLeaseContextPtr ctx,
 }
 
 void
-PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr ctx,
+PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr& ctx,
                         StatementIndex stindex, PsqlBindArray& bind_array,
                         Lease4Ptr& result) const {
     // Create appropriate collection object and get all leases matching
@@ -1367,7 +1366,7 @@ PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr ctx,
 }
 
 void
-PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr ctx,
+PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr& ctx,
                         StatementIndex stindex, PsqlBindArray& bind_array,
                         Lease6Ptr& result) const {
     // Create appropriate collection object and get all leases matching
@@ -1388,7 +1387,7 @@ PgSqlLeaseMgr::getLease(PgSqlLeaseContextPtr ctx,
 }
 
 Lease4Ptr
-PgSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+PgSqlLeaseMgr::getLease4(const IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR4)
         .arg(addr.toText());
 
@@ -1597,7 +1596,7 @@ PgSqlLeaseMgr::getLeases4() const {
 }
 
 Lease4Collection
-PgSqlLeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address,
+PgSqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address,
                           const LeasePageSize& page_size) const {
     // Expecting IPv4 address.
     if (!lower_bound_address.isV4()) {
@@ -1635,7 +1634,7 @@ PgSqlLeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address,
 
 Lease6Ptr
 PgSqlLeaseMgr::getLease6(Lease::Type lease_type,
-                         const isc::asiolink::IOAddress& addr) const {
+                         const IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR6)
         .arg(addr.toText())
         .arg(lease_type);
@@ -1824,7 +1823,7 @@ PgSqlLeaseMgr::getLeases6() const {
 }
 
 Lease6Collection
-PgSqlLeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address,
+PgSqlLeaseMgr::getLeases6(const IOAddress& lower_bound_address,
                           const LeasePageSize& page_size) const {
     // Expecting IPv6 address.
     if (!lower_bound_address.isV6()) {
@@ -1915,7 +1914,7 @@ PgSqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
 
 template<typename LeasePtr>
 void
-PgSqlLeaseMgr::updateLeaseCommon(PgSqlLeaseContextPtr ctx,
+PgSqlLeaseMgr::updateLeaseCommon(PgSqlLeaseContextPtr& ctx,
                                  StatementIndex stindex,
                                  PsqlBindArray& bind_array,
                                  const LeasePtr& lease) {
@@ -2028,7 +2027,7 @@ PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
 
 bool
 PgSqlLeaseMgr::deleteLease(const Lease4Ptr& lease) {
-    const isc::asiolink::IOAddress& addr = lease->addr_;
+    const IOAddress& addr = lease->addr_;
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_DELETE_ADDR)
         .arg(addr.toText());
 
@@ -2062,7 +2061,7 @@ PgSqlLeaseMgr::deleteLease(const Lease4Ptr& lease) {
 
 bool
 PgSqlLeaseMgr::deleteLease(const Lease6Ptr& lease) {
-    const isc::asiolink::IOAddress& addr = lease->addr_;
+    const IOAddress& addr = lease->addr_;
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_PGSQL_DELETE_ADDR)
         .arg(addr.toText());
index 5a955efa58b2f687d48142724a23d7edd0958856..6339592b09a1ad7903bcca4d703ac5698693be5b 100644 (file)
@@ -668,8 +668,9 @@ private:
     ///
     /// @throw isc::db::DbOperationError An operation on the open database has
     ///        failed.
-    bool addLeaseCommon(PgSqlLeaseContextPtr ctx,
-                        StatementIndex stindex, db::PsqlBindArray& bind_array);
+    bool addLeaseCommon(PgSqlLeaseContextPtr& ctx,
+                        StatementIndex stindex,
+                        db::PsqlBindArray& bind_array);
 
     /// @brief Get Lease Collection Common Code
     ///
@@ -693,7 +694,7 @@ private:
     /// @throw isc::db::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
     template <typename Exchange, typename LeaseCollection>
-    void getLeaseCollection(PgSqlLeaseContextPtr ctx,
+    void getLeaseCollection(PgSqlLeaseContextPtr& ctx,
                             StatementIndex stindex,
                             db::PsqlBindArray& bind_array,
                             Exchange& exchange, LeaseCollection& result,
@@ -716,7 +717,7 @@ private:
     ///        failed.
     /// @throw isc::db::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
-    void getLeaseCollection(PgSqlLeaseContextPtr ctx,
+    void getLeaseCollection(PgSqlLeaseContextPtr& ctx,
                             StatementIndex stindex,
                             db::PsqlBindArray& bind_array,
                             Lease4Collection& result) const {
@@ -739,7 +740,7 @@ private:
     ///        failed.
     /// @throw isc::db::MultipleRecords Multiple records were retrieved
     ///        from the database where only one was expected.
-    void getLeaseCollection(PgSqlLeaseContextPtr ctx,
+    void getLeaseCollection(PgSqlLeaseContextPtr& ctx,
                             StatementIndex stindex,
                             db::PsqlBindArray& bind_array,
                             Lease6Collection& result) const {
@@ -756,7 +757,7 @@ private:
     /// @param stindex Index of statement being executed
     /// @param bind_array array for input parameters
     /// @param result Lease4 object returned
-    void getLease(PgSqlLeaseContextPtr ctx,
+    void getLease(PgSqlLeaseContextPtr& ctx,
                   StatementIndex stindex,
                   db::PsqlBindArray& bind_array,
                   Lease4Ptr& result) const;
@@ -771,7 +772,7 @@ private:
     /// @param stindex Index of statement being executed
     /// @param bind_array array for input parameters
     /// @param result Lease6 object returned
-    void getLease(PgSqlLeaseContextPtr ctx,
+    void getLease(PgSqlLeaseContextPtr& ctx,
                   StatementIndex stindex,
                   db::PsqlBindArray& bind_array,
                   Lease6Ptr& result) const;
@@ -812,7 +813,7 @@ private:
     /// @throw isc::db::DbOperationError An operation on the open database has
     ///        failed.
     template <typename LeasePtr>
-    void updateLeaseCommon(PgSqlLeaseContextPtr ctx,
+    void updateLeaseCommon(PgSqlLeaseContextPtr& ctx,
                            StatementIndex stindex,
                            db::PsqlBindArray& bind_array,
                            const LeasePtr& lease);