Add support for specifying the ping interval for pools.

This commit is contained in:
Anthony Tuininga 2021-04-28 15:30:44 -06:00
parent 932b413226
commit 5680620f02
6 changed files with 94 additions and 25 deletions

View File

@ -213,7 +213,7 @@ Module Interface
homogeneous=True, externalauth=False, encoding=None, nencoding=None, \
edition=None, timeout=0, wait_timeout=0, max_lifetime_session=0, \
session_callback=None, max_sessions_per_shard=0, \
soda_metadata_cache=False, stmtcachesize=20)
soda_metadata_cache=False, stmtcachesize=20, ping_interval=60)
Create and return a :ref:`session pool object <sesspool>`. Session pooling
(also known as connection pooling) creates a pool of available connections
@ -333,19 +333,22 @@ Module Interface
The stmtcachesize parameter, if specified, is expected to be an integer
which specifies the initial value of :data:`~SessionPool.stmtcachesize`.
The ping_interval parameter, if specified, is expected to be an integer
which specifies the initial value of :data:`~SessionPool.ping_interval`.
.. note::
This method is an extension to the DB API definition.
.. versionchanged:: 8.2
The parameters `soda_metadata_cache` and `stmtcachesize` were added.
For consistency and compliance with the PEP 8 naming style, the
parameter `waitTimeout` was renamed to `wait_timeout`, the parameter
`maxLifetimeSession` was renamed to `max_lifetime_session`, the
parameter `sessionCallback` was renamed to `session_callback` and the
parameter `maxSessionsPerShard` was renamed to
`max_sessions_per_shard`. The old names will continue to work as
The parameters `soda_metadata_cache`, `stmtcachesize` and
`ping_interval` were added. For consistency and compliance with the PEP
8 naming style, the parameter `waitTimeout` was renamed to
`wait_timeout`, the parameter `maxLifetimeSession` was renamed to
`max_lifetime_session`, the parameter `sessionCallback` was renamed to
`session_callback` and the parameter `maxSessionsPerShard` was renamed
to `max_sessions_per_shard`. The old names will continue to work as
keyword parameters for a period of time. The `threaded` parameter value
is ignored and threading is always enabled.

View File

@ -123,6 +123,23 @@ SessionPool Object
the session pool.
.. attribute:: SessionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
seconds. When a connection is acquired from the pool, a check is first made
to see how long it has been since the connection was put into the pool. If
this idle time exceeds ``ping_interval``, then a :ref:`round-trip <roundtrips>`
ping to the database is performed. If the connection is unusable, it is
discarded and a different connection is selected to be returned by
:meth:`SessionPool.acquire()`. Setting ``ping_interval`` to a negative
value disables pinging. Setting it to 0 forces a ping for every
``aquire()`` and is not recommended.
Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds.
.. versionadded:: 8.2
.. method:: SessionPool.release(connection, tag=None)
Release the connection back to the pool now, rather than whenever __del__

View File

@ -26,6 +26,11 @@ Version 8.2 (TBD)
:meth:`cx_Oracle.SessionPool()` in order to permit specifying the size of
the statement cache during the creation of pools and standalone
connections.
#) Added parameter `ping_interval` to :meth:`cx_Oracle.SessionPool()` to specify
the ping interval when acquiring pooled connections. In addition, the
attribute :data:`SessionPool.ping_interval` was added in order to permit
making adjustments after the pool has been created. In previous cx_Oracle
releases a fixed ping interval of 60 seconds was used.
#) Added parameter `bypass_decode` to :meth:`Cursor.var()` in order to allow
the `decode` step to be bypassed when converting data from Oracle Database
into Python strings

View File

@ -327,18 +327,19 @@ return a different one. This check will not detect cases such as where the
database session has been killed by the DBA, or reached a database resource
manager quota limit. To help in those cases, :meth:`~SessionPool.acquire()`
will also do a full :ref:`round-trip <roundtrips>` ping to the database when it
is about to return a connection that was unused in the pool for 60 seconds. If
the ping fails, the connection will be discarded and another one obtained before
:meth:`~SessionPool.acquire()` returns to the application. Because this full
ping is time based, it won't catch every failure. Also network timeouts and
session kills may occur after :meth:`~SessionPool.acquire()` and before
:meth:`Cursor.execute()`. To handle these cases, applications need to check
for errors after each :meth:`~Cursor.execute()` and make application-specific
decisions about retrying work if there was a connection failure. Oracle's
:ref:`Application Continuity <highavailability>` can do this automatically in
some cases. Note both the lightweight and full ping connection checks can mask
performance-impacting configuration issues, for example firewalls killing
connections, so monitor the connection rate in `AWR
is about to return a connection that was unused in the pool for
:data:`SessionPool.ping_interval` seconds. If the ping fails, the connection
will be discarded and another one obtained before :meth:`~SessionPool.acquire()`
returns to the application. Because this full ping is time based, it won't
catch every failure. Also network timeouts and session kills may occur after
:meth:`~SessionPool.acquire()` and before :meth:`Cursor.execute()`. To handle
these cases, applications need to check for errors after each
:meth:`~Cursor.execute()` and make application-specific decisions about retrying
work if there was a connection failure. Oracle's :ref:`Application Continuity
<highavailability>` can do this automatically in some cases. Note both the
lightweight and full ping connection checks can mask performance-impacting
configuration issues, for example firewalls killing connections, so monitor the
connection rate in `AWR
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
for an unexpected value. You can explicitly initiate a full ping to check
connection liveness with :meth:`Connection.ping()` but overuse will impact

View File

@ -259,6 +259,7 @@ Some general tips for reducing round-trips are:
* Make use of PL/SQL procedures which execute multiple SQL statements instead of executing them individually from cx_Oracle.
* Use scalar types instead of Oracle Database object types.
* Avoid overuse of :meth:`Connection.ping()`.
* Avoid setting :data:`SessionPool.ping_interval` to 0 or a small value.
* When using SODA, use pooled connections and enable the :ref:`SODA metadata cache <sodametadatacache>`.
Finding the Number of Round-Trips

View File

@ -52,8 +52,9 @@ static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
"homogeneous", "externalauth", "encoding", "nencoding", "edition",
"timeout", "wait_timeout", "max_lifetime_session",
"session_callback", "max_sessions_per_shard",
"soda_metadata_cache", "stmtcachesize", "waitTimeout",
"maxLifetimeSession", "sessionCallback", "maxSessionsPerShard",
"soda_metadata_cache", "stmtcachesize", "ping_interval",
"waitTimeout", "maxLifetimeSession", "sessionCallback",
"maxSessionsPerShard",
NULL };
// parse arguments and keywords
@ -74,7 +75,7 @@ static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
if (dpiContext_initPoolCreateParams(cxoDpiContext, &dpiCreateParams) < 0)
return cxoError_raiseAndReturnInt();
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
"|OOOiiiOpbpppssOiiiOipIiiOi", keywordList, &usernameObj,
"|OOOiiiOpbpppssOiiiOipIiiiOi", keywordList, &usernameObj,
&passwordObj, &dsnObj, &minSessions, &maxSessions,
&sessionIncrement, &connectionType, &threaded,
&dpiCreateParams.getMode, &events, &dpiCreateParams.homogeneous,
@ -83,8 +84,9 @@ static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
&dpiCreateParams.waitTimeout, &dpiCreateParams.maxLifetimeSession,
&sessionCallbackObj, &maxSessionsPerShard,
&dpiCommonParams.sodaMetadataCache, &stmtCacheSize,
&waitTimeoutDeprecated, &maxLifetimeSessionDeprecated,
&sessionCallbackObjDeprecated, &maxSessionsPerShardDeprecated))
&dpiCreateParams.pingInterval, &waitTimeoutDeprecated,
&maxLifetimeSessionDeprecated, &sessionCallbackObjDeprecated,
&maxSessionsPerShardDeprecated))
return -1;
if (!PyType_Check(connectionType)) {
cxoError_raiseFromString(cxoProgrammingErrorException,
@ -463,6 +465,21 @@ static PyObject *cxoSessionPool_getOpenCount(cxoSessionPool *pool, void *unused)
}
//-----------------------------------------------------------------------------
// cxoSessionPool_getPingInterval()
// Return the current value of the ping interval set for the pool.
//-----------------------------------------------------------------------------
static PyObject *cxoSessionPool_getPingInterval(cxoSessionPool *pool,
void *unused)
{
int value;
if (dpiPool_getPingInterval(pool->handle, &value) < 0)
return cxoError_raiseAndReturnNull();
return PyLong_FromLong(value);
}
//-----------------------------------------------------------------------------
// cxoSessionPool_getSodaMetadataCache()
// Return a boolean indicating if the SODA metadata cache is enabled or not.
@ -542,6 +559,29 @@ static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool *pool,
}
//-----------------------------------------------------------------------------
// cxoSessionPool_setPingInterval()
// Set the value of the OCI attribute.
//-----------------------------------------------------------------------------
static int cxoSessionPool_setPingInterval(cxoSessionPool *pool,
PyObject *value, void *unused)
{
long cValue;
if (!PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
cValue = PyLong_AsLong(value);
if (PyErr_Occurred())
return -1;
if (dpiPool_setPingInterval(pool->handle, (int) cValue) < 0)
return cxoError_raiseAndReturnInt();
return 0;
}
//-----------------------------------------------------------------------------
// cxoSessionPool_setSodaMetadataCache()
// Set whether the SODA metadata cache is enabled or not.
@ -644,6 +684,8 @@ static PyGetSetDef cxoCalcMembers[] = {
(setter) cxoSessionPool_setGetMode, 0, 0 },
{ "max_lifetime_session", (getter) cxoSessionPool_getMaxLifetimeSession,
(setter) cxoSessionPool_setMaxLifetimeSession, 0, 0 },
{ "ping_interval", (getter) cxoSessionPool_getPingInterval,
(setter) cxoSessionPool_setPingInterval, 0, 0 },
{ "soda_metadata_cache", (getter) cxoSessionPool_getSodaMetadataCache,
(setter) cxoSessionPool_setSodaMetadataCache, 0, 0 },
{ "stmtcachesize", (getter) cxoSessionPool_getStmtCacheSize,