ConnectionPool.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2008 National Institute For Space Research (INPE) - Brazil.
2 
3  This file is part of the TerraLib - a Framework for building GIS enabled applications.
4 
5  TerraLib is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  TerraLib is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public License
16  along with TerraLib. See COPYING. If not, write to
17  TerraLib Team at <terralib-team@terralib.org>.
18  */
19 
20 /*!
21  \file terralib/postgis/ConnectionPool.cpp
22 
23  \brief A class that implements a connection pool for PostGIS.
24 */
25 
26 // TerraLib
27 #include "../common/Enums.h"
28 #include "../common/STLUtils.h"
29 #include "../core/translator/Translator.h"
30 #include "../core/uri/URI.h"
31 #include "../core/uri/Utils.h"
32 #include "Connection.h"
33 #include "ConnectionPool.h"
34 #include "DataSource.h"
35 #include "Exception.h"
36 #include "ScopedConnection.h"
37 #include "Utils.h"
38 
39 // STL
40 #include <chrono>
41 #include <ctime>
42 #include <list>
43 #include <iostream>
44 
45 // BOOST
46 #include <boost/thread.hpp>
47 
48 // libpq
49 #include <libpq-fe.h>
50 
52 {
53  public:
54 
56  : m_ds(ds),
60  m_poolSize(0),
62  m_initialized(false)
63  {
64  }
65 
67  {
69  }
70 
71  std::string m_conninfo; //!< The connection info.
72  std::string m_cencoding; //!< Client encoding.
73  te::pgis::DataSource* m_ds; //!< The data source using the pool.
74  std::size_t m_initialPoolSize; //!< It indicates the initial number of connections opened by the pool.
75  std::size_t m_minPoolSize; //!< It indicates the minimum number of connections in the pool.
76  std::size_t m_maxPoolSize; //!< It indicates the maximum number of connections in the pool.
77  std::size_t m_poolSize; //!< It indicates the maximum number of connections in the pool.
78  unsigned int m_maxIdleTime; //!< The number of seconds waiting for releasing connections.
79  std::list<te::pgis::Connection*> m_connections; //!< The list of available connections ready to be used.
80  boost::mutex m_mtx; //!< A mutex for preventing reading/writing in the connection list.
81  bool m_initialized; //!< A flag that indicates if the pool was initialized or not.
82  std::vector<int> m_availableConnectionsIds; //!< A vector with the ids of available connections.
83 };
84 
86 {
87 // acquire lock
88  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
89 
90  if(isInitialized())
91  throw Exception(TE_TR("The connection pool is already initialized!"));
92 
93 // check for pool parameters...
94  const te::core::URI& connInfo = m_pImpl->m_ds->getConnectionInfo();
95  std::map<std::string, std::string> kvp = te::core::Expand(connInfo.query());
96  std::map<std::string, std::string>::const_iterator it = kvp.begin();
97  std::map<std::string, std::string>::const_iterator itend = kvp.end();
98 
99  it = kvp.find("PG_INITIAL_POOL_SIZE");
100  m_pImpl->m_initialPoolSize = (it != itend && !it->second.empty()) ? atoi(it->second.c_str()) : PGIS_DEFAULT_INITIAL_POOL_SIZE;
101 
102  it = kvp.find("PG_MIN_POOL_SIZE");
103  m_pImpl->m_minPoolSize = (it != itend && !it->second.empty()) ? atoi(it->second.c_str()) : PGIS_DEFAULT_MIN_POOL_SIZE;
104 
105  it = kvp.find("PG_MAX_POOL_SIZE");
106  m_pImpl->m_maxPoolSize = (it != itend && !it->second.empty()) ? atoi(it->second.c_str()) : PGIS_DEFAULT_MAX_POOL_SIZE;
107 
108 // assure valid values for pool parameters
111 
116 
117  it = kvp.find("PG_MAX_IDLE_TIME");
118  m_pImpl->m_maxIdleTime = (it != itend && !it->second.empty()) ? atoi(it->second.c_str()) : PGIS_DEFAULT_MAX_IDLE_TIME;
119 
121 
122 // make connection info
123  m_pImpl->m_conninfo = MakeConnectionStr(connInfo);
124 
125 // try to open the connections
126  for(std::size_t i = 0; i < m_pImpl->m_initialPoolSize; ++i)
127  {
128  Connection* conn = new Connection(this, m_pImpl->m_conninfo, m_pImpl->m_cencoding, false);
129  m_pImpl->m_connections.push_back(conn);
130  setConnectionInUse(conn->m_id, false);
131  ++(m_pImpl->m_poolSize);
132  }
133 
134 // verify the internal date time storage
135  if(!m_pImpl->m_connections.empty())
136  {
137  std::string off = "off";
138 
139  std::string answer = PQparameterStatus(m_pImpl->m_connections.front()->m_pgconn, "integer_datetimes");
140 
141  if(answer == off)
142  m_pImpl->m_ds->setTimeAsInteger(false);
143  else
144  m_pImpl->m_ds->setTimeAsInteger(true);
145  }
146 
147  m_pImpl->m_initialized = true;
148 }
149 
151 {
152 // acquire lock
153  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
154 
155 // if there is a connection in use it will raise an exception!
156  std::list<te::pgis::Connection*>::iterator it = m_pImpl->m_connections.begin();
157  std::list<te::pgis::Connection*>::iterator itend = m_pImpl->m_connections.end();
158 
159  while(it != itend)
160  {
161  if((*it)->m_inuse)
162  throw Exception(TE_TR("There are opened connections. Please, close all connections before finalizing the connection pool."));
163 
164  ++it;
165  }
166 
167 // release all connections
170 
171  m_pImpl->m_connections.clear();
172  m_pImpl->m_poolSize = 0;
173 
174  m_pImpl->m_initialized = false;
175 }
176 
178 {
179 // acquire lock
180  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
181 
182 // must we check the pool?
183 // no: we don't care about monitoring
184 // no: we weren't initialized
185 // no: if the pool has a fixed size we don't need to care about it!
186 // no: we have just the minimum pool connections
187  if((m_pImpl->m_maxIdleTime == 0) ||
188  !isInitialized() ||
191  return;
192 
193 // what time is it?
194  boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
195 
196 // release all connections not in use whose time has expired
197  std::list<te::pgis::Connection*>::iterator it = m_pImpl->m_connections.begin();
198  std::list<te::pgis::Connection*>::iterator itend = m_pImpl->m_connections.end();
199 
200  while(it != itend)
201  {
202  if((*it)->m_inuse == false)
203  {
204  boost::posix_time::time_duration d = now - (*it)->m_lastuse;
205 
206  if(d.seconds() > static_cast<long>(m_pImpl->m_maxIdleTime))
207  {
208  std::list<te::pgis::Connection*>::iterator itaux = it;
209  ++it; // advance current iterator... we will release the connection pointed by the auxiliar iterator
210  delete *itaux;
211  m_pImpl->m_connections.erase(itaux);
212  --(m_pImpl->m_poolSize);
213 
214  if(m_pImpl->m_poolSize == m_pImpl->m_minPoolSize) // have we reached the minimum pool size?
215  break;
216 
217  continue; // we have already go on... (++it)!
218  }
219  }
220 
221  ++it;
222  }
223 }
224 
226 {
227 // acquire lock
228  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
229 
230  std::list<te::pgis::Connection*>::iterator it = m_pImpl->m_connections.begin();
231  std::list<te::pgis::Connection*>::iterator itend = m_pImpl->m_connections.end();
232 
233  while(it != itend)
234  {
235  ConnStatusType status = PQstatus((*it)->m_pgconn);
236 
237  if(status != CONNECTION_OK)
238  return false;
239 
240  ++it;
241  }
242 
243  return true;
244 }
245 
247 {
248  return m_pImpl->m_initialized;
249 }
250 
252 {
253  return m_pImpl->m_ds;
254 }
255 
257 {
258  return m_pImpl->m_poolSize;
259 }
260 
262 {
263  return m_pImpl->m_initialPoolSize;
264 }
265 
267 {
268 // acquire lock
269  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
270 
271  m_pImpl->m_initialPoolSize = size;
272 }
273 
275 {
276  return m_pImpl->m_minPoolSize;
277 }
278 
280 {
281 // acquire lock
282  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
283 
284  m_pImpl->m_minPoolSize = size;
285 }
286 
288 {
289  return m_pImpl->m_maxPoolSize;
290 }
291 
293 {
294 // acquire lock
295  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
296 
297  m_pImpl->m_maxPoolSize = size;
298 }
299 
301 {
302  // timer declaration to calculate the timeout to wait a release connection.
303  std::chrono::time_point<std::chrono::system_clock> start, end;
304  start = std::chrono::system_clock::now();
305 
306  bool gettingConnection = true;
307  while (gettingConnection)
308  {
309  // acquire lock
310  m_pImpl->m_mtx.lock();
311 
312  // if the id is not defined by de caller it returns an available id connection.
313  if (id == -1)
315 
316  // verifies if id is valid.
317  if (id != -1)
318  {
320  if (conn != nullptr && !conn->m_inuse)
321  {
322  setConnectionInUse(id, true);
323  m_pImpl->m_mtx.unlock();
324  return conn;
325  }
326  }
327 
328  // acquire unlock
329  m_pImpl->m_mtx.unlock();
330 
331  // calculation time in seconds to compare with defined timeout.
332  end = std::chrono::system_clock::now();
333  std::chrono::duration<double> elapsed_seconds = end - start;
334 
335  if (elapsed_seconds.count() >= m_pImpl->m_maxIdleTime)
336  gettingConnection = false;
337 
338  // converting sleep from 100 Miliconds to Nanoseconds.
339  boost::int_least64_t sleepTime = boost::int_least64_t(100) * boost::int_least64_t(1000000);
340  boost::this_thread::sleep_for(boost::chrono::nanoseconds(sleepTime));
341  }
342 
343 // if we couldn't find an opened connection maybe we may create one and return it
345  {
346  Connection* newConn =
347  new Connection(this, m_pImpl->m_conninfo, m_pImpl->m_cencoding, true);
348  m_pImpl->m_connections.push_back(newConn);
349 
350  setConnectionInUse(newConn->m_id, false);
351 
352  ++(m_pImpl->m_poolSize);
353 
354  return newConn;
355  }
356 
357  throw Exception(TE_TR("The connection pool has reached its maximum size!"), te::common::NO_CONNECTION_AVAILABLE);
358 }
359 
361 {
362 // acquire lock
363  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
364 
365  setConnectionInUse(conn->m_id, false);
366 
367  conn->m_lastuse = boost::posix_time::second_clock::local_time();
368 }
369 
371 {
372  return m_pImpl->m_maxIdleTime;
373 }
374 
376 {
377 // acquire lock
378  boost::lock_guard<boost::mutex> lock(m_pImpl->m_mtx);
379 
380  m_pImpl->m_maxIdleTime = t;
381 }
382 
383 void te::pgis::ConnectionPool::setConnectionInUse(const int& id, const bool& inUse)
384 {
385  std::list<te::pgis::Connection*>::iterator it = m_pImpl->m_connections.begin();
386  std::list<te::pgis::Connection*>::iterator itend = m_pImpl->m_connections.end();
387 
388  while (it != itend)
389  {
390  if ((*it)->m_id == id)
391  {
392  (*it)->m_inuse = inUse;
393 
394  if (inUse)
395  {
396  auto position = std::find(m_pImpl->m_availableConnectionsIds.begin(),
398  id);
399  m_pImpl->m_availableConnectionsIds.erase(position);
400  }
401  else
402  {
403  m_pImpl->m_availableConnectionsIds.push_back(id);
404  }
405  }
406  ++it;
407  }
408 }
409 
411 {
412  if (m_pImpl->m_availableConnectionsIds.empty())
413  return -1;
414 
416 }
417 
419 {
420  std::list<te::pgis::Connection*>::iterator it = m_pImpl->m_connections.begin();
421  std::list<te::pgis::Connection*>::iterator itend = m_pImpl->m_connections.end();
422  while (it != itend)
423  {
424  if ((*it)->m_id == id)
425  return (*it);
426 
427  ++it;
428  }
429  return nullptr;
430 }
431 
433  : m_pImpl(nullptr)
434 {
435  m_pImpl = new ConnectionPoolImpl(ds);
436 }
437 
439 {
440  delete m_pImpl;
441 }
unsigned int m_maxIdleTime
The number of seconds waiting for releasing connections.
unsigned int getMaxIdleTime() const
It returns the maximum idle time in seconds that a connection can be maintained in the pool without b...
void idle()
It releases the connections that are not in use for a long time.
std::vector< int > m_availableConnectionsIds
A vector with the ids of available connections.
std::string MakeConnectionStr(const te::core::URI &connInfo)
bool isValid() const
It checks if all the connections in the pool are valid (the communication channel is ok)...
void setInitialPoolSize(std::size_t size)
It sets the initial number of connections opened by the pool at its startup.
A class that control the use of connection to a PostgreSQL database.
std::string m_cencoding
Client encoding.
std::list< te::pgis::Connection * > m_connections
The list of available connections ready to be used.
#define PGIS_DEFAULT_INITIAL_POOL_SIZE
This sets the default initial number of connections opened by a connection pool.
bool isInitialized() const
It returns true if the connection pool is initialized, otherwise it returns false.
Base exception class for plugin module.
void setMaxIdleTime(unsigned int t)
It sets the maximum idle time that a connection can be maintained in the pool without being used...
std::size_t getPoolSize() const
It returns the number of connections in the pool.
void setMaxPoolSize(std::size_t size)
It sets the maximum number of connections managed by the pool.
boost::mutex m_mtx
A mutex for preventing reading/writing in the connection list.
A class that implements a connection to a PostgreSQL database.
static te::dt::Date ds(2010, 01, 01)
#define TE_TR(message)
It marks a string in order to get translated.
Definition: Translator.h:242
std::string query() const
Retrieving the query.
Definition: URI.cpp:123
std::size_t getMinPoolSize() const
It returns the minimum number of connections managed by the pool.
virtual te::core::EncodingType getEncoding()
It return the DataSource current encoding.
void setTimeAsInteger(bool timeIsInteger)
void release(Connection *conn)
It brings the informed connection back to the pool.
#define PGIS_DEFAULT_MAX_POOL_SIZE
This sets the default maximum number of connections in the pool.
An exception class for the PostGIS driver.
static std::string getEncodingName(EncodingType et)
Retrive a string from a given character encoding type enum.
#define PGIS_DEFAULT_MAX_IDLE_TIME
This sets the default maximum time that a connection can be in the pool without being used...
boost::posix_time::ptime m_lastuse
It marks the last time this connection was used.
static te::dt::DateTime d(2010, 8, 9, 15, 58, 39)
void initialize()
It initializes the connections to be managed by the pool.
std::size_t getInitialPoolSize() const
It returns the initial number of connections opened by the pool at its startup.
int getAvailableConnectionId()
Gets the first available connection.
void setMinPoolSize(std::size_t size)
It sets the minimum number of connections managed by the pool.
ConnectionPoolImpl * m_pImpl
A pointer to the pool implementation.
A class that implements a connection to a PostgreSQL database.
#define PGIS_DEFAULT_MIN_POOL_SIZE
This sets the default minimum number of connections to be kept in the pool.
std::size_t m_maxPoolSize
It indicates the maximum number of connections in the pool.
A class for representing an Uniform Resource Identifier (URI).
Definition: URI.h:49
Connection * getConnection(int id=-1)
It returns a connection from the pool.
This file contains utility functions used to manipulate data from a URI.
int m_id
The connection Id.
const te::core::URI & getConnectionInfo() const
An Uniform Resource Identifier used to describe the datasource connection.
TECOREEXPORT std::map< std::string, std::string > Expand(const std::string &query_str)
Split a query string into its components.
te::pgis::DataSource * m_ds
The data source using the pool.
te::pgis::Connection * getConnectionById(const int &id)
Gets a connection by id.
std::size_t m_initialPoolSize
It indicates the initial number of connections opened by the pool.
std::size_t m_minPoolSize
It indicates the minimum number of connections in the pool.
std::string m_conninfo
The connection info.
te::pgis::DataSource * getDataSource() const
A class that implements a connection pool for PostGIS.
std::size_t getMaxPoolSize() const
It returns the maximum number of connections managed by the pool.
ConnectionPool(DataSource *ds)
It creates a new connection pool for the database informed.
void FreeContents(boost::unordered_map< K, V * > &m)
This function can be applied to a map of pointers. It will delete each pointer in the map...
Definition: BoostUtils.h:55
void setConnectionInUse(const int &id, const bool &inUse)
Sets the state of the connection by id.
Implementation of the data source for the PostGIS driver.
void finalize()
It closes all connections and clears all resources managed by the pool.
bool m_inuse
Tells if the connection is in use or not.
std::size_t m_poolSize
It indicates the maximum number of connections in the pool.
bool m_initialized
A flag that indicates if the pool was initialized or not.