PYTHON-1279 Deprecate socketKeepAlive and default to true.

Wherever possible set:
- keep-alive idle time to a maximum of 300 seconds.
- keep-alive interval time to a maximum of 10 seconds.
- keep-alive count to a maximum of 9 probes.
This commit is contained in:
Shane Harvey 2017-07-19 15:19:49 -07:00
parent 85e80bcc8b
commit 312408e041
6 changed files with 84 additions and 12 deletions

View File

@ -26,6 +26,10 @@ Changes and Deprecations:
Applications should use
:meth:`~pymongo.mongo_client.MongoClient.get_database` without the `name`
parameter instead.
- Deprecated the MongoClient option `socketKeepAlive`. It now defaults to true
and disabling it is not recommended, see `does TCP keepalive time affect
MongoDB Deployments?
<https://docs.mongodb.com/manual/faq/diagnostics/#does-tcp-keepalive-time-affect-mongodb-deployments>`_
- If a custom :class:`~bson.codec_options.CodecOptions` is passed to
:class:`RawBSONDocument`, its `document_class` must be
:class:`RawBSONDocument`.

View File

@ -106,7 +106,7 @@ def _parse_pool_options(options):
if max_pool_size is not None and min_pool_size > max_pool_size:
raise ValueError("minPoolSize must be smaller or equal to maxPoolSize")
connect_timeout = options.get('connecttimeoutms', common.CONNECT_TIMEOUT)
socket_keepalive = options.get('socketkeepalive', False)
socket_keepalive = options.get('socketkeepalive', True)
socket_timeout = options.get('sockettimeoutms')
wait_queue_timeout = options.get('waitqueuetimeoutms')
wait_queue_multiple = options.get('waitqueuemultiple')

View File

@ -196,9 +196,6 @@ class MongoClient(common.BaseObject):
- `waitQueueMultiple`: (integer or None) Multiplied by maxPoolSize
to give the number of threads allowed to wait for a socket at one
time. Defaults to ``None`` (no limit).
- `socketKeepAlive`: (boolean) Whether to send periodic keep-alive
packets on connected sockets. Defaults to ``False`` (do not send
keep-alive packets).
- `heartbeatFrequencyMS`: (optional) The number of milliseconds
between periodic server checks, or None to accept the default
frequency of 10 seconds.
@ -209,6 +206,10 @@ class MongoClient(common.BaseObject):
profile collections.
- `event_listeners`: a list or tuple of event listeners. See
:mod:`~pymongo.monitoring` for details.
- `socketKeepAlive`: (boolean) **DEPRECATED** Whether to send
periodic keep-alive packets on connected sockets. Defaults to
``True``. Disabling it is not recommended, see
https://docs.mongodb.com/manual/faq/diagnostics/#does-tcp-keepalive-time-affect-mongodb-deployments",
| **Write Concern options:**
| (Only set if passed. No default values.)
@ -340,6 +341,8 @@ class MongoClient(common.BaseObject):
Add ``username`` and ``password`` options. Document the
``authSource``, ``authMechanism``, and ``authMechanismProperties ``
options.
Deprecated the `socketKeepAlive` keyword argument and URI option.
`socketKeepAlive` now defaults to ``True``.
.. versionchanged:: 3.0
:class:`~pymongo.mongo_client.MongoClient` is now the one and only
@ -453,6 +456,13 @@ class MongoClient(common.BaseObject):
# Username and password passed as kwargs override user info in URI.
username = opts.get("username", username)
password = opts.get("password", password)
if 'socketkeepalive' in opts:
warnings.warn(
"The socketKeepAlive option is deprecated. It now"
"defaults to true and disabling it is not recommended, see "
"https://docs.mongodb.com/manual/faq/diagnostics/"
"#does-tcp-keepalive-time-affect-mongodb-deployments",
DeprecationWarning, stacklevel=2)
self.__options = options = ClientOptions(
username, password, dbase, opts)

View File

@ -30,7 +30,7 @@ except ImportError:
from bson import DEFAULT_CODEC_OPTIONS
from bson.py3compat import imap, itervalues, _unicode
from bson.py3compat import imap, itervalues, _unicode, integer_types
from bson.son import SON
from pymongo import auth, helpers, thread_util, __version__
from pymongo.common import MAX_MESSAGE_SIZE
@ -111,6 +111,53 @@ except ImportError:
"""Dummy function for platforms that don't provide fcntl."""
pass
_MAX_TCP_KEEPIDLE = 300
_MAX_TCP_KEEPINTVL = 10
_MAX_TCP_KEEPCNT = 9
if sys.platform == 'win32':
try:
import _winreg as winreg
except ImportError:
import winreg
try:
with winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters") as key:
_DEFAULT_TCP_IDLE_MS, _ = winreg.QueryValueEx(key, "KeepAliveTime")
_DEFAULT_TCP_INTERVAL_MS, _ = winreg.QueryValueEx(
key, "KeepAliveInterval")
# Make sure these are integers.
if not isinstance(_DEFAULT_TCP_IDLE_MS, integer_types):
raise ValueError
if not isinstance(_DEFAULT_TCP_INTERVAL_MS, integer_types):
raise ValueError
except (OSError, ValueError):
# We could not check the default values so do not attempt to override.
def _set_keepalive_times(dummy):
pass
else:
def _set_keepalive_times(sock):
idle_ms = min(_DEFAULT_TCP_IDLE_MS, _MAX_TCP_KEEPIDLE * 1000)
interval_ms = min(_DEFAULT_TCP_INTERVAL_MS,
_MAX_TCP_KEEPINTVL * 1000)
if (idle_ms < _DEFAULT_TCP_IDLE_MS or
interval_ms < _DEFAULT_TCP_INTERVAL_MS):
sock.ioctl(socket.SIO_KEEPALIVE_VALS,
(1, idle_ms, interval_ms))
else:
def _set_tcp_option(sock, tcp_option, max_value):
if hasattr(socket, tcp_option):
sockopt = getattr(socket, tcp_option)
default = sock.getsockopt(socket.SOL_TCP, sockopt)
if default > max_value:
sock.setsockopt(socket.SOL_TCP, sockopt, max_value)
def _set_keepalive_times(sock):
_set_tcp_option(sock, 'TCP_KEEPIDLE', _MAX_TCP_KEEPIDLE)
_set_tcp_option(sock, 'TCP_KEEPINTVL', _MAX_TCP_KEEPINTVL)
_set_tcp_option(sock, 'TCP_KEEPCNT', _MAX_TCP_KEEPCNT)
_METADATA = SON([
('driver', SON([('name', 'PyMongo'), ('version', __version__)])),
@ -223,7 +270,7 @@ class PoolOptions(object):
max_idle_time_ms=None, connect_timeout=None,
socket_timeout=None, wait_queue_timeout=None,
wait_queue_multiple=None, ssl_context=None,
ssl_match_hostname=True, socket_keepalive=False,
ssl_match_hostname=True, socket_keepalive=True,
event_listeners=None, appname=None):
self.__max_pool_size = max_pool_size
@ -619,6 +666,8 @@ def _create_connection(address, options):
sock.settimeout(options.connect_timeout)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE,
options.socket_keepalive)
if options.socket_keepalive:
_set_keepalive_times(sock)
sock.connect(sa)
return sock
except socket.error as e:

View File

@ -463,7 +463,6 @@ class Topology(object):
socket_timeout=options.connect_timeout,
ssl_context=options.ssl_context,
ssl_match_hostname=options.ssl_match_hostname,
socket_keepalive=True,
event_listeners=options.event_listeners,
appname=options.appname)

View File

@ -23,7 +23,7 @@ import socket
import struct
import sys
import time
import traceback
import warnings
sys.path[0:0] = [""]
@ -94,7 +94,6 @@ class ClientUnitTest(unittest.TestCase):
connectTimeoutMS=20000,
waitQueueTimeoutMS=None,
waitQueueMultiple=None,
socketKeepAlive=False,
replicaSet=None,
read_preference=ReadPreference.PRIMARY,
ssl=False,
@ -112,7 +111,7 @@ class ClientUnitTest(unittest.TestCase):
self.assertEqual(20.0, pool_opts.connect_timeout)
self.assertEqual(None, pool_opts.wait_queue_timeout)
self.assertEqual(None, pool_opts.wait_queue_multiple)
self.assertFalse(pool_opts.socket_keepalive)
self.assertTrue(pool_opts.socket_keepalive)
self.assertEqual(None, pool_opts.ssl_context)
self.assertEqual(None, options.replica_set_name)
self.assertEqual(ReadPreference.PRIMARY, client.read_preference)
@ -777,8 +776,19 @@ class TestClient(IntegrationTest):
self.assertEqual(pool._socket_semaphore.waiter_semaphore.counter, 6)
def test_socketKeepAlive(self):
client = rs_or_single_client(socketKeepAlive=True)
self.assertTrue(get_pool(client).opts.socket_keepalive)
for socketKeepAlive in [True, False]:
with warnings.catch_warnings(record=True) as ctx:
warnings.simplefilter("always")
client = rs_or_single_client(socketKeepAlive=socketKeepAlive)
self.assertIn("The socketKeepAlive option is deprecated",
str(ctx[0]))
pool = get_pool(client)
self.assertEqual(socketKeepAlive,
pool.opts.socket_keepalive)
with pool.get_socket({}) as sock_info:
keepalive = sock_info.sock.getsockopt(socket.SOL_SOCKET,
socket.SO_KEEPALIVE)
self.assertEqual(socketKeepAlive, bool(keepalive))
def test_tz_aware(self):
self.assertRaises(ValueError, MongoClient, tz_aware='foo')