Skip to content

Commit 58a1d2e

Browse files
authored
set keep alive probes for mysql connection (#122)
* set keep alive probes for mysql * socket_options * build params
1 parent dc1c156 commit 58a1d2e

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

singlestoredb/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .utils.config import check_float # noqa: F401
1010
from .utils.config import check_int # noqa: F401
1111
from .utils.config import check_optional_bool # noqa: F401
12+
from .utils.config import check_socket_options # noqa: F401
1213
from .utils.config import check_str # noqa: F401
1314
from .utils.config import check_url # noqa: F401
1415
from .utils.config import describe_option # noqa: F401
@@ -263,6 +264,11 @@
263264
environ='SINGLESTOREDB_FUSION_ENABLED',
264265
)
265266

267+
register_option(
268+
'socket_options', 'dict', check_socket_options, None,
269+
'Format for socket options',
270+
)
271+
266272
#
267273
# Query results options
268274
#

singlestoredb/connection.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ def connect(
13401340
vector_data_format: Optional[str] = None,
13411341
parse_json: Optional[bool] = None,
13421342
interpolate_query_with_empty_args: Optional[bool] = None,
1343+
socket_options: Optional[Dict[int, Dict[int, Any]]] = None,
13431344
) -> Connection:
13441345
"""
13451346
Return a SingleStoreDB connection.
@@ -1428,6 +1429,11 @@ def connect(
14281429
interpolate_query_with_empty_args : bool, optional
14291430
Should the connector apply parameter interpolation even when the
14301431
parameters are empty? This corresponds to pymysql/mysqlclient's handling
1432+
socket_options : dict, optional
1433+
Socket options to set on the underlying socket. The keys should be
1434+
socket level constants (e.g., socket.SOL_SOCKET) and the values should be
1435+
dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE) to
1436+
the desired value for that option.
14311437
14321438
Examples
14331439
--------

singlestoredb/mysql/connection.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class Connection(BaseConnection):
230230
Set to true to check the server's identity.
231231
tls_sni_servername: str, optional
232232
Set server host name for TLS connection
233+
socket_options: Dict[int, Dict[int, any]], optional
234+
A dictionary of socket options to set on the connection.
235+
The keys are the socket level constants (e.g., socket.SOL_SOCKET),
236+
and the values are dictionaries mapping option names to values.
233237
read_default_group : str, optional
234238
Group to read from in the configuration file.
235239
autocommit : bool, optional
@@ -341,6 +345,7 @@ def __init__( # noqa: C901
341345
ssl_verify_cert=None,
342346
ssl_verify_identity=None,
343347
tls_sni_servername=None,
348+
socket_options=None,
344349
parse_json=True,
345350
invalid_values=None,
346351
pure_python=None,
@@ -477,7 +482,7 @@ def _config(key, arg):
477482
self.collation = collation
478483
self.use_unicode = use_unicode
479484
self.encoding_errors = encoding_errors
480-
485+
self._socket_options = socket_options or {}
481486
self.encoding = charset_by_name(self.charset).encoding
482487

483488
client_flag |= CLIENT.CAPABILITIES
@@ -1107,6 +1112,11 @@ def connect(self, sock=None):
11071112
print('connected using socket')
11081113
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
11091114
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
1115+
1116+
for level, options in self._socket_options.items():
1117+
for opt, value in options.items():
1118+
sock.setsockopt(level, opt, value)
1119+
11101120
sock.settimeout(None)
11111121

11121122
self._sock = sock

singlestoredb/utils/config.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,55 @@ def check_str(
646646
return out
647647

648648

649+
def check_socket_options(
650+
value: Any,
651+
) -> Optional[Dict[int, Dict[int, Any]]]:
652+
"""
653+
Validate socket options.
654+
655+
Parameters
656+
----------
657+
value : dict
658+
The value to validate. It must be a dictionary where the keys are
659+
socket level constants (e.g., socket.SOL_SOCKET) and the values are
660+
dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE)
661+
to the desired value for that option.
662+
663+
Returns
664+
-------
665+
dict
666+
The validated socket options
667+
668+
"""
669+
if value is None:
670+
return None
671+
672+
if not isinstance(value, Mapping):
673+
raise ValueError(
674+
'value {} must be of type dict'.format(value),
675+
)
676+
677+
out: dict[int, dict[int, Any]] = {}
678+
for level, options in value.items():
679+
if not isinstance(level, int):
680+
raise ValueError(
681+
f'keys in {value} must be integers corresponding to socket levels',
682+
)
683+
if not isinstance(options, Mapping):
684+
raise ValueError(
685+
f'values in {value} must be dicts.',
686+
)
687+
out[level] = {}
688+
for opt, val in options.items():
689+
if not isinstance(opt, int):
690+
raise ValueError(
691+
f'keys in sub-dicts of {value} must be integers.',
692+
)
693+
out[level][opt] = val
694+
695+
return out
696+
697+
649698
def check_dict_str_str(
650699
value: Any,
651700
) -> Optional[Dict[str, str]]:

0 commit comments

Comments
 (0)