@@ -159,6 +159,10 @@ EXTENDED_PARAM_NAMES = set([
159159
160160])
161161
162+ # this cache stores the configurations acquired from one of the configuration
163+ # stores
164+ cdef dict cached_configs = {}
165+
162166# add all of the common parameters to the extended parameters using the
163167# python-oracledb specific name
164168for name in COMMON_PARAM_NAMES:
@@ -227,6 +231,43 @@ cdef class BaseParser:
227231 self .temp_pos += 1
228232
229233
234+ cdef int update_config_cache(str config_cache_key, dict config) except - 1 :
235+ """
236+ Updates the cache with the specified configuration.
237+ """
238+ cdef:
239+ double current_time, soft_expiry_time, hard_expiry_time
240+ uint32_t time_to_live, time_to_live_grace_period
241+ object setting
242+
243+ # the config can include config_time_to_live; the default value is 86400
244+ # (24 hours); an explicit value of 0 disables caching
245+ setting = config.get(" config_time_to_live" )
246+ if setting is None :
247+ time_to_live = 86400
248+ else :
249+ time_to_live = int (setting)
250+ if time_to_live == 0 :
251+ return 0
252+
253+ # the config settings can also include config_time_to_live_grace_period;
254+ # the default value is 1800 (30 minutes)
255+ setting = config.get(" config_time_to_live_grace_period" )
256+ if setting is None :
257+ time_to_live_grace_period = 1800
258+ else :
259+ time_to_live_grace_period = int (setting)
260+
261+ # calculate soft and hard expiry times and keep them with the config
262+ current_time = time.monotonic()
263+ soft_expiry_time = current_time + time_to_live
264+ hard_expiry_time = soft_expiry_time + time_to_live_grace_period
265+ config = copy.deepcopy(config)
266+ config[" config_cache_soft_expiry_time" ] = soft_expiry_time
267+ config[" config_cache_hard_expiry_time" ] = hard_expiry_time
268+ cached_configs[config_cache_key] = config
269+
270+
230271cdef class ConnectStringParser(BaseParser):
231272
232273 cdef:
@@ -237,6 +278,48 @@ cdef class ConnectStringParser(BaseParser):
237278 Description description
238279 dict parameters
239280
281+ cdef int _call_protocol_hook(self , str protocol, str arg,
282+ object fn) except - 1 :
283+ """
284+ Check if the config cache has an entry; if an extry exists and it has
285+ not expired, use it; otherwise, call the protocol hook function.
286+ """
287+ cdef:
288+ double current_time = 0 , expiry_time = 0
289+ dict config
290+
291+ # check to see if the cache has a value and that it has not reached the
292+ # soft expiry time
293+ config = cached_configs.get(self .data_as_str)
294+ if config is not None :
295+ current_time = time.monotonic()
296+ expiry_time = config[" config_cache_soft_expiry_time" ]
297+ if current_time <= expiry_time:
298+ self .params_impl.set_from_config(config, update_cache = False )
299+ return 0
300+
301+ # call the protocol hook function; the cache key is set on the
302+ # parameters instance so that calls by the hook function to
303+ # set_from_config() will update the cache
304+ params = self .params_impl._get_public_instance()
305+ self .params_impl._config_cache_key = self .data_as_str
306+ try :
307+ fn(protocol, arg, params)
308+ except Exception as e:
309+
310+ # if the hook fails but a config exists in the cache and the hard
311+ # expiry time has not been reached the existing config is used
312+ if config is not None :
313+ expiry_time = config[" config_cache_hard_expiry_time" ]
314+ if current_time <= expiry_time:
315+ self .params_impl.set_from_config(config, update_cache = False )
316+ return 0
317+ del cached_configs[self .params_impl._config_cache_key]
318+ errors._raise_err(errors.ERR_PROTOCOL_HANDLER_FAILED,
319+ protocol = protocol, arg = arg, cause = e)
320+ finally :
321+ self .params_impl._config_cache_key = None
322+
240323 cdef bint _is_host_or_service_name_char(self , Py_UCS4 ch):
241324 """
242325 Returns whether or not the given character is allowed to be used inside
@@ -377,19 +460,14 @@ cdef class ConnectStringParser(BaseParser):
377460 Parses an easy connect string.
378461 """
379462 cdef:
380- object params, fn
463+ object fn
381464 str protocol, arg
382465 protocol = self ._parse_easy_connect_protocol()
383466 if protocol is not None :
384467 fn = REGISTERED_PROTOCOLS.get(protocol)
385468 if fn is not None :
386469 arg = self .data_as_str[self .temp_pos:]
387- params = self .params_impl._get_public_instance()
388- try :
389- fn(protocol, arg, params)
390- except Exception as e:
391- errors._raise_err(errors.ERR_PROTOCOL_HANDLER_FAILED,
392- protocol = protocol, arg = arg, cause = e)
470+ self ._call_protocol_hook(protocol, arg, fn)
393471 self .description_list = self .params_impl.description_list
394472 self .pos = self .num_chars
395473 return 0
0 commit comments