11import base64
22import json
3+ import logging
34import socket
5+ import threading
46import time
57from pathlib import Path
68from typing import Any , Callable , Dict , Iterable , List , Mapping , Optional , Union
8082 len_of ,
8183)
8284
85+ logger = logging .getLogger (__name__ )
86+
87+
88+ class ReconnectHandler :
89+ def __init__ (self , conns : object , connection_name : str , kwargs : object ) -> None :
90+ self .connection_name = connection_name
91+ self .conns = conns
92+ self ._kwargs = kwargs
93+ self .is_idle_state = False
94+ self .reconnect_lock = threading .Lock ()
95+
96+ def reset_db_name (self , db_name : str ):
97+ self ._kwargs ["db_name" ] = db_name
98+
99+ def check_state_and_reconnect_later (self ):
100+ check_after_seconds = 3
101+ logger .debug (f"state is idle, schedule reconnect in { check_after_seconds } seconds" )
102+ time .sleep (check_after_seconds )
103+ if not self .is_idle_state :
104+ logger .debug ("idle state changed, skip reconnect" )
105+ return
106+ with self .reconnect_lock :
107+ logger .info ("reconnect on idle state" )
108+ self .is_idle_state = False
109+ try :
110+ logger .debug ("try disconnecting old connection..." )
111+ self .conns .disconnect (self .connection_name )
112+ except Exception :
113+ logger .warning ("disconnect failed: {e}" )
114+ finally :
115+ reconnected = False
116+ while not reconnected :
117+ try :
118+ logger .debug ("try reconnecting..." )
119+ self .conns .connect (self .connection_name , ** self ._kwargs )
120+ reconnected = True
121+ except Exception as e :
122+ logger .warning (
123+ f"reconnect failed: { e } , try again after { check_after_seconds } seconds"
124+ )
125+ time .sleep (check_after_seconds )
126+ logger .info ("reconnected" )
127+
128+ def reconnect_on_idle (self , state : object ):
129+ logger .debug (f"state change to: { state } " )
130+ with self .reconnect_lock :
131+ if state .value [1 ] != "idle" :
132+ self .is_idle_state = False
133+ return
134+ self .is_idle_state = True
135+ threading .Thread (target = self .check_state_and_reconnect_later ).start ()
136+
83137
84138class GrpcHandler :
85139 # pylint: disable=too-many-instance-attributes
@@ -104,6 +158,12 @@ def __init__(
104158 self ._setup_grpc_channel ()
105159 self .callbacks = []
106160 self .schema_cache = {}
161+ self ._reconnect_handler = None
162+
163+ def register_reconnect_handler (self , handler : ReconnectHandler ):
164+ if handler is not None :
165+ self ._reconnect_handler = handler
166+ self .register_state_change_callback (handler .reconnect_on_idle )
107167
108168 def register_state_change_callback (self , callback : Callable ):
109169 self .callbacks .append (callback )
@@ -179,6 +239,8 @@ def reset_db_name(self, db_name: str):
179239 self ._setup_db_interceptor (db_name )
180240 self ._setup_grpc_channel ()
181241 self ._setup_identifier_interceptor (self ._user )
242+ if self ._reconnect_handler is not None :
243+ self ._reconnect_handler .reset_db_name (db_name )
182244
183245 def _setup_authorization_interceptor (self , user : str , password : str , token : str ):
184246 keys = []
0 commit comments