16161717
1818import errno
19- from datetime import datetime
20- from time import sleep
19+ import time
2120
2221from cortx .utils .conf_store .error import ConfError
2322from cortx .utils .conf_store .conf_cache import ConfCache
@@ -42,8 +41,9 @@ def __init__(self, delim='>'):
4241 self ._cache = {}
4342 self ._callbacks = {}
4443 self ._machine_id = self ._get_machine_id ()
45- self .default_owner = self .lock_owner = self ._machine_id .strip () if self ._machine_id else None
46- self .lock_key = const .LOCK_KEY
44+ self ._lock_owner = self ._get_machine_id ()
45+ self ._lock_domain = const .DEFAULT_LOCK_DOMAIN
46+ self ._lock_duration = const .DEFAULT_LOCK_DURATION
4747
4848 @property
4949 def machine_id (self ):
@@ -274,90 +274,115 @@ def _merge(self, dest_index, src_index, keys):
274274 self ._cache [dest_index ].set (key , self ._cache [src_index ].get (key ))
275275
276276 def lock (self , index : str , ** kwargs ):
277- """Acquire lock on config."""
277+ """
278+ Attempt to acquire the config lock.
279+
280+ Parameters:
281+ index(required): Identifier of the config.
282+ domain(optional): Identity of the lock holder.
283+ owner(optional): Instance of domain who is sending lock request.
284+ duration(optional): Obtains the lock for the give duration in terms of seconds.
285+
286+ return: True if the lock was successfully acquired,
287+ false if it is already acquired by someone.
288+ """
278289 if index not in self ._cache .keys ():
279290 raise ConfError (errno .EINVAL , "config index %s is not loaded" ,
280291 index )
281292
282- self .timeout = const .DEFAULT_LOCK_TIMEOUT
283- self .lock_owner = self .default_owner
284- self .lock_key = const .LOCK_KEY
285- allowed_keys = { 'lock_key' , 'lock_owner' , 'timeout' }
286- for key , value in kwargs .items ():
293+ allowed_keys = { 'domain' , 'owner' , 'duration' }
294+ for key , val in kwargs .items ():
287295 if key not in allowed_keys :
288296 raise ConfError (errno .EINVAL , "Invalid parameter %s" , key )
297+ if key == 'duration' and not isinstance (val , int ):
298+ raise ConfError (errno .EINVAL , "Invalid value %s for parameter %s" , val , key )
299+
300+ owner = self ._lock_owner if 'owner' not in kwargs else kwargs ['owner' ]
301+ domain = self ._lock_domain if 'domain' not in kwargs else kwargs ['domain' ]
302+ duration = self ._lock_duration if 'duration' not in kwargs else kwargs ['duration' ]
303+
304+ rc = False
305+ if self .test_lock (index , owner = owner , domain = domain ):
306+ self .set (index , const .LOCK_OWNER_KEY_PREFIX % domain , owner )
307+ lock_end_time = time .time () + duration
308+ self .set (index , const .LOCK_END_TIME_KEY_PREFIX % domain , str (lock_end_time ))
309+ # simulate a timedelay for checking race condition
310+ time .sleep (0.01 )
311+ # check weather lock requester is same is
312+ # current lock holder to avoid race condition
313+ if self .get (index , const .LOCK_OWNER_KEY_PREFIX % domain ) == owner :
314+ rc = True
315+ return rc
289316
290- if key == 'timeout' and not isinstance (value , int ):
291- raise ConfError (errno .EINVAL , "Invalid value %s for parameter %s" , value , key )
292-
293- setattr (self , key , value )
294-
295- who_owner = self ._get_lock_owner (index , self .lock_key )
296- if who_owner is not None :
297- return who_owner == self .lock_owner
298-
299- while self .timeout > 1 :
300- sleep (const .DEFAULT_RETRY_DELAY )
301- # TODO: Add condition_check scenario here
302- self .timeout -= 1
303-
304- if not self ._lock (index , self .lock_key , self .lock_owner ):
305- self .lock_owner = self .default_owner
306- return False
307- return True
308-
309- def _lock (self , index : str , lock_key : str , lock_owner : str ):
310- """Acquire lock on config."""
311- locked_at = str (datetime .timestamp (datetime .now ()))
312- self .set (index , const .LOCK_OWNER_KEY % lock_key , lock_owner )
313- self .set (index , const .LOCK_TIME_KEY % lock_key , locked_at )
317+ def unlock (self , index : str , ** kwargs ):
318+ """
319+ Attempt to release the config lock.
314320
315- return self ._get_lock_owner (index , lock_key ) == lock_owner
321+ Parameters:
322+ index(required): Identifier of the config.
323+ domain(optional): Identity of the Lock Holder.
324+ owner(optional): Instance of the domain sending unlock request.
325+ force(optional, default=False): When true, lock is forcefully released.
316326
317- def unlock (self , index : str , ** kwargs ):
318- """Release config lock."""
327+ return: True if the lock was successfully released,
328+ false if it there is no lock or acquired by someone else unless force=true.
329+ """
319330 if index not in self ._cache .keys ():
320331 raise ConfError (errno .EINVAL , "config index %s is not loaded" ,
321332 index )
322333
323- self .force = False
324- self .lock_owner = self .default_owner
325- self .lock_key = const .LOCK_KEY
326- allowed_keys = { 'lock_key' , 'lock_owner' , 'force' }
327- for key , value in kwargs .items ():
334+ force = False
335+ allowed_keys = { 'domain' , 'owner' , 'force' }
336+ for key , val in kwargs .items ():
328337 if key not in allowed_keys :
329338 raise ConfError (errno .EINVAL , "Invalid parameter %s" , key )
339+ if key == 'force' and not isinstance (val , bool ):
340+ raise ConfError (errno .EINVAL , "Invalid value %s for parameter %s" , val , key )
330341
331- if key == 'force' and not isinstance (value , bool ):
332- raise ConfError (
333- errno .EINVAL , "Invalid value %s for parameter %s" ,
334- value , key
335- )
336-
337- setattr (self , key , value )
342+ owner = self ._lock_owner if 'owner' not in kwargs else kwargs ['owner' ]
343+ domain = self ._lock_domain if 'domain' not in kwargs else kwargs ['domain' ]
338344
339- _is_locked = self ._get_lock_owner (index , self .lock_key ) == self .lock_owner
340- return self .delete (index , const .LOCK_OWNER_KEY % self .lock_key ) if _is_locked \
341- or self .force else False
345+ rc = False
346+ if self .get (index , const .LOCK_OWNER_KEY_PREFIX % domain ) == owner or force :
347+ self .delete (index , const .LOCK_OWNER_KEY_PREFIX % domain )
348+ self .delete (index , const .LOCK_END_TIME_KEY_PREFIX % domain )
349+ rc = True
350+ return rc
342351
343352 def test_lock (self , index : str , ** kwargs ):
344- """Check whether lock is acquired on the config."""
353+ """
354+ Check whether lock is acquired on the config.
355+
356+ Parameters:
357+ index(required): param index: Identifier of the config.
358+ domain(optional): Identity of the Lock Holder.
359+ owner(optional): Instance of domain who needs to test lock.
360+
361+ return: True if lock can be acquired by someone else False
362+ """
345363 if index not in self ._cache .keys ():
346364 raise ConfError (errno .EINVAL , "config index %s is not loaded" ,
347365 index )
348- allowed_keys = { 'lock_key' }
349- self . lock_key = const . LOCK_KEY
350- for key , value in kwargs .items ():
366+
367+ allowed_keys = { 'domain' , 'owner' }
368+ for key , _ in kwargs .items ():
351369 if key not in allowed_keys :
352370 raise ConfError (errno .EINVAL , "Invalid parameter %s" , key )
353371
354- setattr (self , key , value )
372+ owner = self ._lock_owner if 'owner' not in kwargs else kwargs ['owner' ]
373+ domain = self ._lock_domain if 'domain' not in kwargs else kwargs ['domain' ]
374+
375+ _current_owner = self .get (index , const .LOCK_OWNER_KEY_PREFIX % domain )
376+ if _current_owner in [None , "" , owner ]:
377+ return True
355378
356- return False if self ._get_lock_owner (index , self .lock_key ) is None else True
379+ _end_time = self .get (index , const .LOCK_END_TIME_KEY_PREFIX % domain )
380+ if _end_time in [None , "" ]:
381+ return True
357382
358- def _get_lock_owner ( self , index : str , lock_key : str ):
359- """Get owner of the config lock."""
360- return self . get ( index , const . LOCK_OWNER_KEY % lock_key )
383+ if float ( _end_time ) < time . time ( ):
384+ return True
385+ return False
361386
362387
363388class Conf :
@@ -471,9 +496,9 @@ def lock(index: str, **kwargs):
471496 """
472497 Attempt to acquire the config lock.
473498 :param index(required): Identifier of the config.
474- :param lock_key (optional): Lock related key ex: conf>service> lock.
475- :param lock_owner (optional, default=machine_id ): Identity of the lock holder .
476- :param timeout (optional): Time delay before attempting to acquire lock .
499+ :param domain (optional): Identity of the lock holder .
500+ :param owner (optional): Instance of domain sending lock request .
501+ :param duration (optional): Obtains the lock for the give duration in terms of seconds .
477502
478503 :return: True if the lock was successfully acquired,
479504 false if it is already acquired by someone.
@@ -485,8 +510,8 @@ def unlock(index: str, **kwargs):
485510 """
486511 Attempt to release the config lock.
487512 :param index(required): Identifier of the config.
488- :param lock_key (optional): Lock related key ex: conf>service>lock .
489- :param lock_owner (optional, default=machine_id ): Identity of the lock holder .
513+ :param domain (optional): Identity of the Lock Holder .
514+ :param owner (optional): Instance of the domain sending unlock request .
490515 :param force(optional, default=False): When true, lock is forcefully released.
491516
492517 :return: True if the lock was successfully released,
@@ -499,9 +524,10 @@ def test_lock(index: str, **kwargs):
499524 """
500525 Test whether Config is locked.
501526 :param index(required): param index: Identifier of the config.
502- :param lock_key(optional): Lock related key ex: conf>service>lock.
527+ :param domain(optional): Identity of the Lock Holder.
528+ :param owner(optional): Instance of domain who needs to test lock.
503529
504- :return: True if lock is acquired by somone else False
530+ :return: True if lock can be acquired by someone else False
505531 """
506532 return Conf ._conf .test_lock (index , ** kwargs )
507533
0 commit comments