diff --git a/python/core/auto_generated/qgsfeaturerequest.sip.in b/python/core/auto_generated/qgsfeaturerequest.sip.in index 888595316cd..d0a98681c83 100644 --- a/python/core/auto_generated/qgsfeaturerequest.sip.in +++ b/python/core/auto_generated/qgsfeaturerequest.sip.in @@ -657,6 +657,40 @@ at this moment. A negative value (which is set by default) will wait forever. Only works if the provider supports this option. .. versionadded:: 3.0 +%End + + int freeConnectionsRequirement() const; +%Docstring +The amount of free connections required to start this request. +The system will block the request until the specified amount of connections +is available for usage. + +By default this amount is 3. This makes sure, that we have 2 spare connections +that might be used by "nested" requests which are executed while iterating +over the results of this request. + +This number should be changed to one, when we know that no nested requests happen +and that this request might happen in a nested way. This is for example given for +expression functions that do internal requests. + +.. versionadded:: 3.4 +%End + + void setFreeConnectionsRequirement( int freeConnectionsRequirement ); +%Docstring +The amount of free connections required to start this request. +The system will block the request until the specified amount of connections +is available for usage. + +By default this amount is 3. This makes sure, that we have 2 spare connections +that might be used by "nested" requests which are executed while iterating +over the results of this request. + +This number should be changed to one, when we know that no nested requests happen +and that this request might happen in a nested way. This is for example given for +expression functions that do internal requests. + +.. versionadded:: 3.4 %End protected: diff --git a/src/core/qgsconnectionpool.h b/src/core/qgsconnectionpool.h index ddfd35ac9fb..6da60664506 100644 --- a/src/core/qgsconnectionpool.h +++ b/src/core/qgsconnectionpool.h @@ -29,7 +29,7 @@ #include -#define CONN_POOL_MAX_CONCURRENT_CONNS 4 +#define CONN_POOL_MAX_CONCURRENT_CONNS 6 #define CONN_POOL_EXPIRATION_TIME 60 // in seconds @@ -93,12 +93,12 @@ class QgsConnectionPoolGroup * * \returns initialized connection or nullptr if unsuccessful */ - T acquire( int timeout ) + T acquire( int timeout, int freeConnectionRequirement ) { // we are going to acquire a resource - if no resource is available, we will block here if ( timeout >= 0 ) { - if ( !sem.tryAcquire( 1, timeout ) ) + if ( !sem.tryAcquire( freeConnectionRequirement, timeout ) ) return nullptr; } else @@ -107,8 +107,9 @@ class QgsConnectionPoolGroup // tryAcquire is broken on Qt > 5.8 with negative timeouts - see // https://bugreports.qt.io/browse/QTBUG-64413 // https://lists.osgeo.org/pipermail/qgis-developer/2017-November/050456.html - sem.acquire( 1 ); + sem.acquire( freeConnectionRequirement ); } + sem.release( freeConnectionRequirement - 1 ); // quick (preferred) way - use cached connection { @@ -283,9 +284,11 @@ class QgsConnectionPool * If \a timeout is a negative value the calling thread will be blocked * until a connection becomes available. This is the default behavior. * + * + * * \returns initialized connection or nullptr if unsuccessful */ - T acquireConnection( const QString &connInfo, int timeout = -1 ) + T acquireConnection( const QString &connInfo, int timeout = -1, int freeConnectionRequirement = 3 ) { mMutex.lock(); typename T_Groups::iterator it = mGroups.find( connInfo ); @@ -296,7 +299,7 @@ class QgsConnectionPool T_Group *group = *it; mMutex.unlock(); - return group->acquire( timeout ); + return group->acquire( timeout, freeConnectionRequirement ); } //! Release an existing connection so it will get back into the pool and can be reused diff --git a/src/core/qgsfeaturerequest.cpp b/src/core/qgsfeaturerequest.cpp index 03ca00e1d89..2eaead3cf2a 100644 --- a/src/core/qgsfeaturerequest.cpp +++ b/src/core/qgsfeaturerequest.cpp @@ -86,6 +86,7 @@ QgsFeatureRequest &QgsFeatureRequest::operator=( const QgsFeatureRequest &rh ) mCrs = rh.mCrs; mTransformErrorCallback = rh.mTransformErrorCallback; mConnectionTimeout = rh.mConnectionTimeout; + mFreeConnectionsRequirement = rh.mFreeConnectionsRequirement; return *this; } @@ -298,6 +299,16 @@ void QgsFeatureRequest::setConnectionTimeout( int connectionTimeout ) mConnectionTimeout = connectionTimeout; } +int QgsFeatureRequest::freeConnectionsRequirement() const +{ + return mFreeConnectionsRequirement; +} + +void QgsFeatureRequest::setFreeConnectionsRequirement( int freeConnectionsRequirement ) +{ + mFreeConnectionsRequirement = freeConnectionsRequirement; +} + #include "qgsfeatureiterator.h" #include "qgslogger.h" diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h index fdb823f93a1..bcaa124d081 100644 --- a/src/core/qgsfeaturerequest.h +++ b/src/core/qgsfeaturerequest.h @@ -636,6 +636,40 @@ class CORE_EXPORT QgsFeatureRequest */ void setConnectionTimeout( int connectionTimeout ); + /** + * The amount of free connections required to start this request. + * The system will block the request until the specified amount of connections + * is available for usage. + * + * By default this amount is 3. This makes sure, that we have 2 spare connections + * that might be used by "nested" requests which are executed while iterating + * over the results of this request. + * + * This number should be changed to one, when we know that no nested requests happen + * and that this request might happen in a nested way. This is for example given for + * expression functions that do internal requests. + * + * \since QGIS 3.4 + */ + int freeConnectionsRequirement() const; + + /** + * The amount of free connections required to start this request. + * The system will block the request until the specified amount of connections + * is available for usage. + * + * By default this amount is 3. This makes sure, that we have 2 spare connections + * that might be used by "nested" requests which are executed while iterating + * over the results of this request. + * + * This number should be changed to one, when we know that no nested requests happen + * and that this request might happen in a nested way. This is for example given for + * expression functions that do internal requests. + * + * \since QGIS 3.4 + */ + void setFreeConnectionsRequirement( int freeConnectionsRequirement ); + protected: FilterType mFilter = FilterNone; QgsRectangle mFilterRect; @@ -654,6 +688,7 @@ class CORE_EXPORT QgsFeatureRequest QgsCoordinateReferenceSystem mCrs; QgsCoordinateTransformContext mTransformContext; int mConnectionTimeout = -1; + int mFreeConnectionsRequirement = 3; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags ) diff --git a/src/providers/postgres/qgspostgresfeatureiterator.cpp b/src/providers/postgres/qgspostgresfeatureiterator.cpp index da9e75d1026..59979fe2a1f 100644 --- a/src/providers/postgres/qgspostgresfeatureiterator.cpp +++ b/src/providers/postgres/qgspostgresfeatureiterator.cpp @@ -38,7 +38,7 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource if ( !source->mTransactionConnection ) { - mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo ); + mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo, request.connectionTimeout(), request.freeConnectionsRequirement() ); mIsTransactionConnection = false; } else