Skip to content

Commit cfa2750

Browse files
Add support for pool reconfiguration.
1 parent d5461bd commit cfa2750

File tree

6 files changed

+321
-17
lines changed

6 files changed

+321
-17
lines changed

doc/src/api_manual/module.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,12 @@ Module Interface
320320
id=GUID-B853A020-752F-494A-8D88-D0396EF57177>`__ for more information.
321321

322322
The max_sessions_per_shard parameter is expected to be an integer, if
323-
specified, and sets the maximum number of sessions in the pool that can be
324-
used for any given shard in a sharded database. This value is ignored if
325-
the Oracle client library version is less than 18.3.
323+
specified. Setting this greater than zero specifies the maximum number of
324+
sessions in the pool that can be used for any given shard in a sharded
325+
database. This lets connections in the pool be balanced across the shards.
326+
A value of zero will not set any maximum number of sessions for each shard.
327+
This value is ignored if the Oracle client library version is less than
328+
18.3.
326329

327330
The soda_metadata_cache parameter is expected to be a boolean expresion
328331
which indicates whether or not to enable the SODA metatata cache. This

doc/src/api_manual/session_pool.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,19 @@ SessionPool Object
118118
.. versionadded:: 5.3
119119

120120

121+
.. attribute:: SessionPool.max_sessions_per_shard
122+
123+
This read-write attribute returns the number of sessions that can be created
124+
per shard in the pool. Setting this attribute greater than zero specifies
125+
the maximum number of sessions in the pool that can be used for any given
126+
shard in a sharded database. This lets connections in the pool be balanced
127+
across the shards. A value of zero will not set any maximum number of
128+
sessions for each shard. This attribute is only available in Oracle Client
129+
18.3 and higher.
130+
131+
.. versionadded:: 8.2
132+
133+
121134
.. attribute:: SessionPool.min
122135

123136
This read-only attribute returns the number of sessions with which the
@@ -154,6 +167,60 @@ SessionPool Object
154167
.. versionadded:: 8.2
155168

156169

170+
.. method:: SessionPool.reconfigure([min, max, increment, getmode, timeout, \
171+
wait_timeout, max_lifetime_session, max_sessions_per_shard, \
172+
soda_metadata_cache, stmtcachesize, ping_interval])
173+
174+
Reconfigures various parameters of a connection pool. The pool size can be
175+
altered with ``reconfigure()`` by passing values for
176+
:data:`~SessionPool.min`, :data:`~SessionPool.max` or
177+
:data:`~SessionPool.increment`. The :data:`~SessionPool.getmode`,
178+
:data:`~SessionPool.timeout`, :data:`~SessionPool.wait_timeout`,
179+
:data:`~SessionPool.max_lifetime_session`,
180+
:data:`~SessionPool.max_sessions_per_shard`,
181+
:data:`~SessionPool.soda_metadata_cache`, :data:`~SessionPool.stmtcachesize`
182+
and :data:`~SessionPool.ping_interval` attributes can be set directly or
183+
with ``reconfigure()``.
184+
185+
All parameters are optional. Unspecified parameters will leave those pool
186+
attributes unchanged. The parameters are processed in two stages. After any
187+
size change has been processed, reconfiguration on the other parameters is
188+
done sequentially. If an error such as an invalid value occurs when changing
189+
one attribute, then an exception will be generated but any already changed
190+
attributes will retain their new values.
191+
192+
During reconfiguration of a pool's size, the behavior of
193+
:meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when
194+
``acquire()`` is called:
195+
196+
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET`, an ``acquire()`` call
197+
will wait until the pool has been reconfigured.
198+
199+
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`, an ``acquire()`` call
200+
will try to acquire a connection in the time specified by
201+
:data:`~SessionPool.wait_timeout` and return an error if the time taken
202+
exceeds that value.
203+
204+
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT`, an ``acquire()`` call
205+
will wait until after the pool has been reconfigured and a connection is
206+
available.
207+
208+
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT`, if the number of busy
209+
connections is less than the pool size, ``acquire()`` will return a new
210+
connection after pool reconfiguration is complete.
211+
212+
Closing connections with :meth:`SessionPool.release()` or
213+
:meth:`Connection.close()` will wait until any pool size reconfiguration is
214+
complete.
215+
216+
Closing the connection pool with :meth:`SessionPool.close()` will wait until
217+
reconfiguration is complete.
218+
219+
See :ref:`Connection Pool Reconfiguration <poolreconfiguration>`.
220+
221+
.. versionadded:: 8.2
222+
223+
157224
.. method:: SessionPool.release(connection, tag=None)
158225

159226
Release the connection back to the pool now, rather than whenever __del__

doc/src/release_notes.rst

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,33 @@ Version 8.2 (TBD)
1515
version-4-2-tbd>`__.
1616
#) Threaded mode is now always enabled when creating connection pools with
1717
:meth:`cx_Oracle.SessionPool()`. Any `threaded` parameter value is ignored.
18+
#) Added :meth:`SessionPool.reconfigure()` to support pool reconfiguration.
19+
This method provides the ability to change properties such as the size of
20+
existing pools instead of having to restart the application or create a new
21+
pool.
22+
#) Added parameter `max_sessions_per_shard` to :meth:`cx_Oracle.SessionPool()`
23+
to allow configuration of the maximum number of sessions per shard in the
24+
pool. In addition, the attribute
25+
:data:`SessionPool.max_sessions_per_shard` was added in order to permit
26+
making adjustments after the pool has been created. They are usable when
27+
using Oracle Client version 18.4 and higher.
1828
#) Added parameter `stmtcachesize` to :meth:`cx_Oracle.connect()` and
1929
:meth:`cx_Oracle.SessionPool()` in order to permit specifying the size of
2030
the statement cache during the creation of pools and standalone
2131
connections.
22-
#) Added parameter `ping_interval` to :meth:`cx_Oracle.SessionPool()` to specify
23-
the ping interval when acquiring pooled connections. In addition, the
24-
attribute :data:`SessionPool.ping_interval` was added in order to permit
25-
making adjustments after the pool has been created. In previous cx_Oracle
26-
releases a fixed ping interval of 60 seconds was used.
27-
#) Added parameter `soda_metadata_cache` to :meth:`cx_Oracle.SessionPool()` for
28-
:ref:`SODA metadata cache <sodametadatacache>` support. In addition, the
29-
attribute :data:`SessionPool.soda_metadata_cache` was added in order to
32+
#) Added parameter `ping_interval` to :meth:`cx_Oracle.SessionPool()` to
33+
specify the ping interval when acquiring pooled connections. In addition,
34+
the attribute :data:`SessionPool.ping_interval` was added in order to
35+
permit making adjustments after the pool has been created. In previous
36+
cx_Oracle releases a fixed ping interval of 60 seconds was used.
37+
#) Added parameter `soda_metadata_cache` to :meth:`cx_Oracle.SessionPool()`
38+
for :ref:`SODA metadata cache <sodametadatacache>` support. In addition,
39+
the attribute :data:`SessionPool.soda_metadata_cache` was added in order to
3040
permit making adjustments after the pool has been created. This feature
3141
significantly improves the performance of methods
32-
:meth:`SodaDatabase.createCollection()` (when not specifying a value for the
33-
metadata parameter) and :meth:`SodaDatabase.openCollection()`. Caching is
34-
available when using Oracle Client version 19.11 and higher.
42+
:meth:`SodaDatabase.createCollection()` (when not specifying a value for
43+
the metadata parameter) and :meth:`SodaDatabase.openCollection()`. Caching
44+
is available when using Oracle Client version 19.11 and higher.
3545
#) Added support for supplying hints to SODA operations. A new non-terminal
3646
method :meth:`~SodaOperation.hint()` was added and a `hint` parameter was
3747
added to the methods :meth:`SodaCollection.insertOneAndGet()`,
@@ -56,8 +66,8 @@ Version 8.2 (TBD)
5666
#) The distributed transaction handle assosciated with the connection is now
5767
cleared on commit or rollback (`issue 530
5868
<https://github.com/oracle/python-cx_Oracle/issues/530>`__).
59-
#) Added a check to ensure that when setting variables or object attributes, the
60-
type of the temporary LOB must match the expected type.
69+
#) Added a check to ensure that when setting variables or object attributes,
70+
the type of the temporary LOB must match the expected type.
6171
#) A small number of parameter, method, and attribute names were updated to
6272
follow the PEP 8 style guide. This brings better consistency to the
6373
cx_Oracle API. The old names are still usable but may be removed in a

doc/src/user_guide/connection_handling.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,42 @@ or user profile `IDLE_TIME
407407
do not expire idle sessions, since this will require connections be recreated,
408408
which will impact performance and scalability.
409409

410+
.. _poolreconfiguration:
411+
412+
Connection Pool Reconfiguration
413+
-------------------------------
414+
415+
Some pool settings can be changed dynamically with
416+
:meth:`SessionPool.reconfigure()`. This allows the pool size and other
417+
attributes to be changed during application runtime without needing to restart
418+
the pool or application.
419+
420+
For example a pool's size can be changed like:
421+
422+
.. code-block:: python
423+
424+
pool.reconfigure(min=10, max=10, increment=0)
425+
426+
After any size change has been processed, reconfiguration on the other
427+
parameters is done sequentially. If an error such as an invalid value occurs
428+
when changing one attribute, then an exception will be generated but any already
429+
changed attributes will retain their new values.
430+
431+
During reconfiguration of a pool's size, the behavior of
432+
:meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when
433+
``acquire()`` is called, see :meth:`SessionPool.reconfigure()`. Closing
434+
connections or closing the pool will wait until after pool reconfiguration is
435+
complete.
436+
437+
Calling ``reconfigure()`` is the only way to change a pool's ``min``, ``max``
438+
and ``increment`` values. Other attributes such as
439+
:data:`~SessionPool.wait_timeout` can also be passed to ``reconfigure()`` or
440+
they can be set directly:
441+
442+
.. code-block:: python
443+
444+
pool.wait_timeout = 1000
445+
410446
.. _sessioncallback:
411447

412448
Session CallBacks for Setting Pooled Connection State

src/cxoSessionPool.c

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//-----------------------------------------------------------------------------
2-
// Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2+
// Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
33
//
44
// Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
55
//
@@ -14,6 +14,11 @@
1414

1515
#include "cxoModule.h"
1616

17+
// forward declarations
18+
int cxoSessionPool_reconfigureHelper(cxoSessionPool *pool,
19+
const char *attrName, PyObject *value);
20+
21+
1722
//-----------------------------------------------------------------------------
1823
// cxoSessionPool_new()
1924
// Create a new session pool object.
@@ -338,6 +343,93 @@ static PyObject *cxoSessionPool_drop(cxoSessionPool *pool, PyObject *args)
338343
}
339344

340345

346+
//-----------------------------------------------------------------------------
347+
// cxoSessionPool_reconfigure()
348+
// Reconfigure properties of the session pool.
349+
//-----------------------------------------------------------------------------
350+
static PyObject *cxoSessionPool_reconfigure(cxoSessionPool *pool,
351+
PyObject *args, PyObject *keywordArgs)
352+
{
353+
PyObject *timeout, *waitTimeout, *maxLifetimeSession, *maxSessionsPerShard;
354+
PyObject *sodaMetadataCache, *stmtcachesize, *pingInterval, *getMode;
355+
uint32_t minSessions, maxSessions, sessionIncrement;
356+
357+
// define keyword arguments
358+
static char *keywordList[] = { "min", "max", "increment", "getmode",
359+
"timeout", "wait_timeout", "max_lifetime_session",
360+
"max_sessions_per_shard", "soda_metadata_cache", "stmtcachesize",
361+
"ping_interval", NULL };
362+
363+
// set up default values
364+
minSessions = pool->minSessions;
365+
maxSessions = pool->maxSessions;
366+
sessionIncrement = pool->sessionIncrement;
367+
timeout = waitTimeout = maxLifetimeSession = maxSessionsPerShard = NULL;
368+
sodaMetadataCache = stmtcachesize = pingInterval = getMode = NULL;
369+
370+
// parse arguments and keywords
371+
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|iiiOOOOOOOO",
372+
keywordList, &minSessions, &maxSessions, &sessionIncrement,
373+
&getMode, &timeout, &waitTimeout, &maxLifetimeSession,
374+
&maxSessionsPerShard, &sodaMetadataCache, &stmtcachesize,
375+
&pingInterval))
376+
return NULL;
377+
378+
// perform reconfiguration of the pool itself if needed
379+
if (minSessions != pool->minSessions || maxSessions != pool->maxSessions ||
380+
sessionIncrement != pool->sessionIncrement) {
381+
if (dpiPool_reconfigure(pool->handle, minSessions, maxSessions,
382+
sessionIncrement) < 0)
383+
return cxoError_raiseAndReturnNull();
384+
pool->minSessions = minSessions;
385+
pool->maxSessions = maxSessions;
386+
pool->sessionIncrement = sessionIncrement;
387+
}
388+
389+
// adjust attributes
390+
if (cxoSessionPool_reconfigureHelper(pool, "getmode", getMode) < 0)
391+
return NULL;
392+
if (cxoSessionPool_reconfigureHelper(pool, "timeout", timeout) < 0)
393+
return NULL;
394+
if (cxoSessionPool_reconfigureHelper(pool, "wait_timeout",
395+
waitTimeout) < 0)
396+
return NULL;
397+
if (cxoSessionPool_reconfigureHelper(pool, "max_lifetime_session",
398+
maxLifetimeSession) < 0)
399+
return NULL;
400+
if (cxoSessionPool_reconfigureHelper(pool, "max_sessions_per_shard",
401+
maxSessionsPerShard) < 0)
402+
return NULL;
403+
if (cxoSessionPool_reconfigureHelper(pool, "soda_metadata_cache",
404+
sodaMetadataCache) < 0)
405+
return NULL;
406+
if (cxoSessionPool_reconfigureHelper(pool, "stmtcachesize",
407+
stmtcachesize) < 0)
408+
return NULL;
409+
if (cxoSessionPool_reconfigureHelper(pool, "ping_interval",
410+
pingInterval) < 0)
411+
return NULL;
412+
413+
Py_RETURN_NONE;
414+
}
415+
416+
417+
//-----------------------------------------------------------------------------
418+
// cxoSessionPool_reconfigureHelpe()
419+
// Helper function that calls the setter for the session pool's property,
420+
// after first checking that a value was supplied and not None.
421+
//-----------------------------------------------------------------------------
422+
int cxoSessionPool_reconfigureHelper(cxoSessionPool *pool,
423+
const char *attrName, PyObject *value)
424+
{
425+
if (value != NULL && value != Py_None) {
426+
if (PyObject_SetAttrString((PyObject*) pool, attrName, value) < 0)
427+
return cxoError_raiseAndReturnInt();
428+
}
429+
return 0;
430+
}
431+
432+
341433
//-----------------------------------------------------------------------------
342434
// cxoSessionPool_release()
343435
// Release a connection back to the session pool.
@@ -455,6 +547,17 @@ static PyObject *cxoSessionPool_getMaxLifetimeSession(cxoSessionPool *pool,
455547
}
456548

457549

550+
//-----------------------------------------------------------------------------
551+
// cxoSessionPool_getMaxSessionsPerShard()
552+
// Return the maximum sessions per shard in the session pool.
553+
//-----------------------------------------------------------------------------
554+
static PyObject *cxoSessionPool_getMaxSessionsPerShard(cxoSessionPool *pool,
555+
void *unused)
556+
{
557+
return cxoSessionPool_getAttribute(pool, dpiPool_getMaxSessionsPerShard);
558+
}
559+
560+
458561
//-----------------------------------------------------------------------------
459562
// cxoSessionPool_getOpenCount()
460563
// Return the number of open connections in the session pool.
@@ -559,6 +662,18 @@ static int cxoSessionPool_setMaxLifetimeSession(cxoSessionPool *pool,
559662
}
560663

561664

665+
//-----------------------------------------------------------------------------
666+
// cxoSessionPool_setMaxSessionsPerShard()
667+
// Set the maximum lifetime for connections in the session pool.
668+
//-----------------------------------------------------------------------------
669+
static int cxoSessionPool_setMaxSessionsPerShard(cxoSessionPool *pool,
670+
PyObject *value, void *unused)
671+
{
672+
return cxoSessionPool_setAttribute(pool, value,
673+
dpiPool_setMaxSessionsPerShard);
674+
}
675+
676+
562677
//-----------------------------------------------------------------------------
563678
// cxoSessionPool_setPingInterval()
564679
// Set the value of the OCI attribute.
@@ -649,6 +764,8 @@ static PyMethodDef cxoMethods[] = {
649764
{ "close", (PyCFunction) cxoSessionPool_close,
650765
METH_VARARGS | METH_KEYWORDS },
651766
{ "drop", (PyCFunction) cxoSessionPool_drop, METH_VARARGS },
767+
{ "reconfigure", (PyCFunction) cxoSessionPool_reconfigure,
768+
METH_VARARGS | METH_KEYWORDS },
652769
{ "release", (PyCFunction) cxoSessionPool_release,
653770
METH_VARARGS | METH_KEYWORDS },
654771
{ NULL }
@@ -684,6 +801,8 @@ static PyGetSetDef cxoCalcMembers[] = {
684801
(setter) cxoSessionPool_setGetMode, 0, 0 },
685802
{ "max_lifetime_session", (getter) cxoSessionPool_getMaxLifetimeSession,
686803
(setter) cxoSessionPool_setMaxLifetimeSession, 0, 0 },
804+
{ "max_sessions_per_shard", (getter) cxoSessionPool_getMaxSessionsPerShard,
805+
(setter) cxoSessionPool_setMaxSessionsPerShard, 0, 0 },
687806
{ "ping_interval", (getter) cxoSessionPool_getPingInterval,
688807
(setter) cxoSessionPool_setPingInterval, 0, 0 },
689808
{ "soda_metadata_cache", (getter) cxoSessionPool_getSodaMetadataCache,

0 commit comments

Comments
 (0)