From 7e68f36fd95894b3fd58fd48c020fad140eb5e7f Mon Sep 17 00:00:00 2001 From: dgala Date: Mon, 13 Apr 2026 19:13:53 +0530 Subject: [PATCH 1/3] set keep alive probes for mysql --- singlestoredb/mysql/connection.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/singlestoredb/mysql/connection.py b/singlestoredb/mysql/connection.py index 094fad68..e30275c1 100644 --- a/singlestoredb/mysql/connection.py +++ b/singlestoredb/mysql/connection.py @@ -1107,6 +1107,12 @@ def connect(self, sock=None): print('connected using socket') sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + + # setting TCP keepalive for mysql + # 60s idle, 30s interval, 5 times before close + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) sock.settimeout(None) self._sock = sock From fe2855feb6449ac594ab0cccbefe885843b01f97 Mon Sep 17 00:00:00 2001 From: dgala Date: Mon, 13 Apr 2026 20:48:22 +0530 Subject: [PATCH 2/3] socket_options --- singlestoredb/connection.py | 1 + singlestoredb/mysql/connection.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/singlestoredb/connection.py b/singlestoredb/connection.py index 942b2feb..1f656620 100644 --- a/singlestoredb/connection.py +++ b/singlestoredb/connection.py @@ -1320,6 +1320,7 @@ def connect( ssl_ca: Optional[str] = None, ssl_disabled: Optional[bool] = None, ssl_cipher: Optional[str] = None, ssl_verify_cert: Optional[bool] = None, tls_sni_servername: Optional[str] = None, + socket_options: Optional[Dict[int, Dict[int, Any]]] = None, ssl_verify_identity: Optional[bool] = None, conv: Optional[Dict[int, Callable[..., Any]]] = None, credential_type: Optional[str] = None, diff --git a/singlestoredb/mysql/connection.py b/singlestoredb/mysql/connection.py index e30275c1..7f376ae8 100644 --- a/singlestoredb/mysql/connection.py +++ b/singlestoredb/mysql/connection.py @@ -230,6 +230,10 @@ class Connection(BaseConnection): Set to true to check the server's identity. tls_sni_servername: str, optional Set server host name for TLS connection + socket_options: Dict[int, Dict[int, any]], optional + A dictionary of socket options to set on the connection. + The keys are the socket level constants (e.g., socket.SOL_SOCKET), + and the values are dictionaries mapping option names to values. read_default_group : str, optional Group to read from in the configuration file. autocommit : bool, optional @@ -341,6 +345,7 @@ def __init__( # noqa: C901 ssl_verify_cert=None, ssl_verify_identity=None, tls_sni_servername=None, + socket_options=None, parse_json=True, invalid_values=None, pure_python=None, @@ -477,7 +482,7 @@ def _config(key, arg): self.collation = collation self.use_unicode = use_unicode self.encoding_errors = encoding_errors - + self._socket_options = socket_options or {} self.encoding = charset_by_name(self.charset).encoding client_flag |= CLIENT.CAPABILITIES @@ -1110,9 +1115,10 @@ def connect(self, sock=None): # setting TCP keepalive for mysql # 60s idle, 30s interval, 5 times before close - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) + for level, options in self._socket_options.items(): + for opt, value in options.items(): + sock.setsockopt(level, opt, value) + sock.settimeout(None) self._sock = sock From 785be45fd459925ef7ef89aa597ba096e1b4ca5f Mon Sep 17 00:00:00 2001 From: dgala Date: Mon, 13 Apr 2026 21:18:18 +0530 Subject: [PATCH 3/3] build params --- singlestoredb/config.py | 6 ++++ singlestoredb/connection.py | 7 ++++- singlestoredb/mysql/connection.py | 2 -- singlestoredb/utils/config.py | 49 +++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/singlestoredb/config.py b/singlestoredb/config.py index 044af2a5..594b06cf 100644 --- a/singlestoredb/config.py +++ b/singlestoredb/config.py @@ -9,6 +9,7 @@ from .utils.config import check_float # noqa: F401 from .utils.config import check_int # noqa: F401 from .utils.config import check_optional_bool # noqa: F401 +from .utils.config import check_socket_options # noqa: F401 from .utils.config import check_str # noqa: F401 from .utils.config import check_url # noqa: F401 from .utils.config import describe_option # noqa: F401 @@ -263,6 +264,11 @@ environ='SINGLESTOREDB_FUSION_ENABLED', ) +register_option( + 'socket_options', 'dict', check_socket_options, None, + 'Format for socket options', +) + # # Query results options # diff --git a/singlestoredb/connection.py b/singlestoredb/connection.py index 1f656620..9e42c7da 100644 --- a/singlestoredb/connection.py +++ b/singlestoredb/connection.py @@ -1320,7 +1320,6 @@ def connect( ssl_ca: Optional[str] = None, ssl_disabled: Optional[bool] = None, ssl_cipher: Optional[str] = None, ssl_verify_cert: Optional[bool] = None, tls_sni_servername: Optional[str] = None, - socket_options: Optional[Dict[int, Dict[int, Any]]] = None, ssl_verify_identity: Optional[bool] = None, conv: Optional[Dict[int, Callable[..., Any]]] = None, credential_type: Optional[str] = None, @@ -1341,6 +1340,7 @@ def connect( vector_data_format: Optional[str] = None, parse_json: Optional[bool] = None, interpolate_query_with_empty_args: Optional[bool] = None, + socket_options: Optional[Dict[int, Dict[int, Any]]] = None, ) -> Connection: """ Return a SingleStoreDB connection. @@ -1429,6 +1429,11 @@ def connect( interpolate_query_with_empty_args : bool, optional Should the connector apply parameter interpolation even when the parameters are empty? This corresponds to pymysql/mysqlclient's handling + socket_options : dict, optional + Socket options to set on the underlying socket. The keys should be + socket level constants (e.g., socket.SOL_SOCKET) and the values should be + dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE) to + the desired value for that option. Examples -------- diff --git a/singlestoredb/mysql/connection.py b/singlestoredb/mysql/connection.py index 7f376ae8..c4fc6417 100644 --- a/singlestoredb/mysql/connection.py +++ b/singlestoredb/mysql/connection.py @@ -1113,8 +1113,6 @@ def connect(self, sock=None): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - # setting TCP keepalive for mysql - # 60s idle, 30s interval, 5 times before close for level, options in self._socket_options.items(): for opt, value in options.items(): sock.setsockopt(level, opt, value) diff --git a/singlestoredb/utils/config.py b/singlestoredb/utils/config.py index 6b7cdd9f..bd2cc232 100644 --- a/singlestoredb/utils/config.py +++ b/singlestoredb/utils/config.py @@ -646,6 +646,55 @@ def check_str( return out +def check_socket_options( + value: Any, +) -> Optional[Dict[int, Dict[int, Any]]]: + """ + Validate socket options. + + Parameters + ---------- + value : dict + The value to validate. It must be a dictionary where the keys are + socket level constants (e.g., socket.SOL_SOCKET) and the values are + dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE) + to the desired value for that option. + + Returns + ------- + dict + The validated socket options + + """ + if value is None: + return None + + if not isinstance(value, Mapping): + raise ValueError( + 'value {} must be of type dict'.format(value), + ) + + out: dict[int, dict[int, Any]] = {} + for level, options in value.items(): + if not isinstance(level, int): + raise ValueError( + f'keys in {value} must be integers corresponding to socket levels', + ) + if not isinstance(options, Mapping): + raise ValueError( + f'values in {value} must be dicts.', + ) + out[level] = {} + for opt, val in options.items(): + if not isinstance(opt, int): + raise ValueError( + f'keys in sub-dicts of {value} must be integers.', + ) + out[level][opt] = val + + return out + + def check_dict_str_str( value: Any, ) -> Optional[Dict[str, str]]: