[entropy.client] avoid calling close() on cached repositories and make the cache explode, improve constraints on repository objects
This commit is contained in:
@@ -757,8 +757,9 @@ class Client(Singleton, TextInterface, LoadersMixin, CacheMixin, CalculatorsMixi
|
||||
"""
|
||||
self.__instance_destroyed = True
|
||||
if hasattr(self, '_installed_repository'):
|
||||
if self._installed_repository != None:
|
||||
self._installed_repository.close()
|
||||
if self._installed_repository is not None:
|
||||
self._installed_repository.close(
|
||||
_token = etpConst['clientdbid'])
|
||||
if hasattr(self, 'logger'):
|
||||
self.logger.close()
|
||||
if hasattr(self, '_settings') and \
|
||||
|
||||
@@ -79,8 +79,38 @@ class ClientEntropyRepositoryPlugin(EntropyRepositoryPlugin):
|
||||
|
||||
return 0
|
||||
|
||||
class CachedRepository(EntropyRepository):
|
||||
"""
|
||||
This kind of repository cannot have close() called directly, without
|
||||
a valid token passed. This is because the class object is cached somewhere
|
||||
and calling close() would turn into a software bug.
|
||||
"""
|
||||
def setCloseToken(self, token):
|
||||
"""
|
||||
Set a token that can be used to validate close() calls. Calling
|
||||
close() on these repos is prohibited and considered a software bug.
|
||||
Only Entropy Client should be able to close them.
|
||||
"""
|
||||
self._close_token = token
|
||||
|
||||
class InstalledPackagesRepository(EntropyRepository):
|
||||
def close(self, _token = None):
|
||||
"""
|
||||
Reimplemented from EntropyRepository
|
||||
"""
|
||||
close_token = getattr(self, "_close_token", None)
|
||||
if close_token is not None:
|
||||
if (_token is None) or (_token != close_token):
|
||||
raise PermissionDenied(
|
||||
"cannot close this repository directly. Software bug!")
|
||||
return EntropyRepository.close(self)
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
Cannot honor the constraint in this case, sorry!
|
||||
"""
|
||||
return EntropyRepository.close(self)
|
||||
|
||||
class InstalledPackagesRepository(CachedRepository):
|
||||
"""
|
||||
This class represents the installed packages repository and is a direct
|
||||
subclass of EntropyRepository.
|
||||
@@ -2387,7 +2417,7 @@ class MaskableRepository(EntropyRepositoryBase):
|
||||
return -1, myr
|
||||
|
||||
|
||||
class AvailablePackagesRepository(EntropyRepository, MaskableRepository):
|
||||
class AvailablePackagesRepository(CachedRepository, MaskableRepository):
|
||||
"""
|
||||
This class represents the available packages repository and is a direct
|
||||
subclass of EntropyRepository. It implements the update() method in order
|
||||
@@ -2467,10 +2497,14 @@ class AvailablePackagesRepository(EntropyRepository, MaskableRepository):
|
||||
EntropyRepository.clearCache(self)
|
||||
|
||||
|
||||
class GenericRepository(EntropyRepository, MaskableRepository):
|
||||
class GenericRepository(CachedRepository, MaskableRepository):
|
||||
"""
|
||||
This class represents a generic packages repository and is a direct
|
||||
subclass of EntropyRepository.
|
||||
Even GenericRepository is a CachedRepository because its object could
|
||||
get cached by 3rd party. Actually, we require this because our installed
|
||||
packages repository could end up being a GenericRepository, when running
|
||||
in fail-safe mode.
|
||||
"""
|
||||
|
||||
def handlePackage(self, pkg_data, forcedRevision = -1,
|
||||
|
||||
@@ -62,9 +62,12 @@ class RepositoryMixin:
|
||||
def ensure_closed_repo(repoid):
|
||||
key = self.__get_repository_cache_key(repoid)
|
||||
for cache_obj in (self._repodb_cache, self._memory_db_instances):
|
||||
obj = cache_obj.pop(key, None)
|
||||
if obj is None:
|
||||
continue
|
||||
try:
|
||||
cache_obj.pop(key).close()
|
||||
except (KeyError, AttributeError, OperationalError):
|
||||
obj.close(_token = repoid)
|
||||
except OperationalError:
|
||||
pass
|
||||
|
||||
t2 = _("Please update your repositories now in order to remove this message!")
|
||||
@@ -149,6 +152,7 @@ class RepositoryMixin:
|
||||
|
||||
def close_repositories(self, mask_clear = True):
|
||||
for item in sorted(self._repodb_cache.keys()):
|
||||
repository_id, root = item
|
||||
# in-memory repositories cannot be closed
|
||||
# otherwise everything will be lost, to
|
||||
# effectively close these repos you
|
||||
@@ -156,7 +160,7 @@ class RepositoryMixin:
|
||||
if item in self._memory_db_instances:
|
||||
continue
|
||||
try:
|
||||
self._repodb_cache.pop(item).close()
|
||||
self._repodb_cache.pop(item).close(_token = repository_id)
|
||||
except OperationalError as err: # wtf!
|
||||
sys.stderr.write("!!! Cannot close Entropy repos: %s\n" % (
|
||||
err,))
|
||||
@@ -265,6 +269,7 @@ class RepositoryMixin:
|
||||
xcache = xcache,
|
||||
indexing = indexing
|
||||
)
|
||||
conn.setCloseToken(repoid)
|
||||
self._add_plugin_to_client_repository(conn)
|
||||
|
||||
if (repoid not in self._treeupdates_repos) and \
|
||||
@@ -788,6 +793,7 @@ class RepositoryMixin:
|
||||
name = etpConst['clientdbid'],
|
||||
xcache = self.xcache, indexing = self.indexing
|
||||
)
|
||||
conn.setCloseToken(etpConst['clientdbid'])
|
||||
self._add_plugin_to_client_repository(conn)
|
||||
# TODO: remove this in future, drop useless data from clientdb
|
||||
except (DatabaseError,):
|
||||
@@ -800,7 +806,7 @@ class RepositoryMixin:
|
||||
conn.validate()
|
||||
except SystemDatabaseError:
|
||||
try:
|
||||
conn.close()
|
||||
conn.close(_token = etpConst['clientdbid'])
|
||||
except (RepositoryPluginError, OSError, IOError):
|
||||
pass
|
||||
entropy.tools.print_traceback(f = self.logger)
|
||||
@@ -810,7 +816,7 @@ class RepositoryMixin:
|
||||
return conn
|
||||
|
||||
def reopen_installed_repository(self):
|
||||
self._installed_repository.close()
|
||||
self._installed_repository.close(_token = etpConst['clientdbid'])
|
||||
self._open_installed_repository()
|
||||
# make sure settings are in sync
|
||||
self._settings.clear()
|
||||
|
||||
Reference in New Issue
Block a user