Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit 933fb0d

Browse files
author
Tanuja Shinde
authored
CORTX-33673: Improvement for lock-unlock interface with lock end time… (#869)
* CORTX-33673: Improvement for lock-unlock interface with lock end time implementation Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: updated setattr Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: updated lock unlock interfaces Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: updated lock unlock interfaces Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: push multi thread testing program in test folder Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: push multi thread testing program in test folder Signed-off-by: Tanuja Shinde <[email protected]> * CORTX-33673: push multi thread testing program in test folder Signed-off-by: Tanuja Shinde <[email protected]> Signed-off-by: Tanuja Shinde <[email protected]>
1 parent 8ca8276 commit 933fb0d

File tree

3 files changed

+285
-72
lines changed

3 files changed

+285
-72
lines changed

py-utils/src/utils/conf_store/conf_store.py

Lines changed: 94 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
1717

1818
import errno
19-
from datetime import datetime
20-
from time import sleep
19+
import time
2120

2221
from cortx.utils.conf_store.error import ConfError
2322
from 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

363388
class 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

py-utils/src/utils/const.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@
9090
VERSION_UPGRADE = "UPGRADE"
9191

9292
# Confstore lock keys
93-
LOCK_KEY = "cortx>gconf>lock"
94-
LOCK_TIME_KEY = "%s>time"
95-
LOCK_OWNER_KEY = "%s>owner"
96-
DEFAULT_LOCK_TIMEOUT = 0
93+
LOCK_END_TIME_KEY_PREFIX = "lock>%s>end_time"
94+
LOCK_OWNER_KEY_PREFIX = "lock>%s>owner"
95+
DEFAULT_LOCK_DURATION = 10
9796
DEFAULT_RETRY_DELAY = 1
97+
DEFAULT_LOCK_DOMAIN = "default"

0 commit comments

Comments
 (0)