fix: disk loader not fiiling all requested cached data from the disk

This commit is contained in:
Vinicius Moreira
2019-09-17 13:11:22 -03:00
parent bfd947043c
commit 16e55c9613
7 changed files with 56 additions and 33 deletions

View File

@@ -15,11 +15,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- "Application settings" button located in right lower corner - "Application settings" button located in right lower corner
- Package "Name" filter field (above the packages table) - Package "Name" filter field (above the packages table)
- Showing the number of packages being shown by the total found - Showing the number of packages being shown by the total found
- "Show" button for large fields of the "Info" window
### Improvements: ### Improvements:
- Reading installed Snaps now takes around 95% less time - Reading installed Snaps now takes around 95% less time
- Reading installed Flatpaks now takes around 45% less time - Reading installed Flatpaks now takes around 45% less time
- Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...) - Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...)
- When a package is installed, it will be the first element of the table after refreshing
- Progress bar status can now be controlled by the software manager while an operation is being executed - Progress bar status can now be controlled by the software manager while an operation is being executed
- Flatpak: showing runtime branches as versions when they are not available - Flatpak: showing runtime branches as versions when they are not available
@@ -32,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixes: ### Fixes:
- cached Flatpak app current version - cached Flatpak app current version
- disk loader not filling all requested cached data from the disk
### Code ### Code
- Code was internally modularized as: "api" (conceptual classes used to create custom software managers), "gems" (software managers), "commons" (common classes shared between the UI and "gems") - Code was internally modularized as: "api" (conceptual classes used to create custom software managers), "gems" (software managers), "commons" (common classes shared between the UI and "gems")

View File

@@ -30,7 +30,7 @@ context = ApplicationContext(i18n=i18n,
download_icons=args.download_icons, download_icons=args.download_icons,
app_root_dir=ROOT_DIR, app_root_dir=ROOT_DIR,
cache_factory=cache_factory, cache_factory=cache_factory,
disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache), disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache, logger=logger),
logger=logger) logger=logger)
user_config = config.read() user_config = config.read()

View File

@@ -1,3 +1,4 @@
import time
from argparse import Namespace from argparse import Namespace
from threading import Thread from threading import Thread
from typing import List, Set, Type from typing import List, Set, Type
@@ -20,6 +21,8 @@ class GenericSoftwareManager(SoftwareManager):
self.thread_prepare = None self.thread_prepare = None
self.i18n = context.i18n self.i18n = context.i18n
self.disk_loader_factory = context.disk_loader_factory self.disk_loader_factory = context.disk_loader_factory
self.logger = context.logger
self._already_prepared = []
def reset_cache(self): def reset_cache(self):
if self._available_map is not None: if self._available_map is not None:
@@ -66,6 +69,7 @@ class GenericSoftwareManager(SoftwareManager):
res.new.extend(apps_found.new) res.new.extend(apps_found.new)
def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult: def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult:
ti = time.time()
self._wait_to_be_ready() self._wait_to_be_ready()
res = SearchResult([], [], 0) res = SearchResult([], [], 0)
@@ -85,13 +89,15 @@ class GenericSoftwareManager(SoftwareManager):
t.join() t.join()
if disk_loader: if disk_loader:
disk_loader.stop = True disk_loader.stop_working()
disk_loader.join() disk_loader.join()
res.installed = self._sort(res.installed, norm_word) res.installed = self._sort(res.installed, norm_word)
res.new = self._sort(res.new, norm_word) res.new = self._sort(res.new, norm_word)
res.total = len(res.installed) + len(res.new) res.total = len(res.installed) + len(res.new)
tf = time.time()
self.logger.info('Took {0:.2f} seconds'.format(tf - ti))
return res return res
def _wait_to_be_ready(self): def _wait_to_be_ready(self):
@@ -106,6 +112,7 @@ class GenericSoftwareManager(SoftwareManager):
return True return True
def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult: def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult:
ti = time.time()
self._wait_to_be_ready() self._wait_to_be_ready()
res = SearchResult([], None, 0) res = SearchResult([], None, 0)
@@ -138,9 +145,11 @@ class GenericSoftwareManager(SoftwareManager):
res.total += man_res.total res.total += man_res.total
if disk_loader: if disk_loader:
disk_loader.stop = True disk_loader.stop_working()
disk_loader.join() disk_loader.join()
tf = time.time()
self.logger.info('Took {0:.2f} seconds'.format(tf - ti))
return res return res
def downgrade(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool: def downgrade(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
@@ -221,8 +230,9 @@ class GenericSoftwareManager(SoftwareManager):
def _prepare(self): def _prepare(self):
if self.managers: if self.managers:
for man in self.managers: for man in self.managers:
if self._can_work(man): if man not in self._already_prepared and self._can_work(man):
man.prepare() man.prepare()
self._already_prepared.append(man)
def prepare(self): def prepare(self):
self.thread_prepare = Thread(target=self._prepare) self.thread_prepare = Thread(target=self._prepare)

View File

@@ -100,13 +100,13 @@ class ArchManager(SoftwareManager):
if pkgsinfo: if pkgsinfo:
for pkgdata in pkgsinfo: for pkgdata in pkgsinfo:
app = self.mapper.map_api_data(pkgdata, not_signed) pkg = self.mapper.map_api_data(pkgdata, not_signed)
app.downgrade_enabled = downgrade_enabled pkg.downgrade_enabled = downgrade_enabled
if disk_loader: if disk_loader:
disk_loader.fill(app) disk_loader.fill(pkg)
app.status = PackageStatus.READY pkg.status = PackageStatus.READY
apps.append(app) apps.append(pkg)
def _fill_mirror_pkgs(self, mirrors: dict, apps: list): def _fill_mirror_pkgs(self, mirrors: dict, apps: list):
# TODO # TODO
@@ -203,8 +203,8 @@ class ArchManager(SoftwareManager):
watcher.change_progress(50) watcher.change_progress(50)
if info.get('required by'): if info.get('required by'):
pkname = '"{}"'.format(pkg.name) pkname = bold(pkg.name)
msg = '{}:\n\n{}\n\n{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), info['required by'], self.i18n['arch.uninstall.required_by.advice'].format(pkname)) msg = '{}:<br/><br/>{}<br/><br/>{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), bold(info['required by']), self.i18n['arch.uninstall.required_by.advice'].format(pkname))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING) watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING)
return False return False
@@ -537,7 +537,6 @@ class ArchManager(SoftwareManager):
def prepare(self): def prepare(self):
self.dcache_updater.start() self.dcache_updater.start()
self.aur_index_updater.start() self.aur_index_updater.start()
def list_updates(self) -> List[PackageUpdate]: def list_updates(self) -> List[PackageUpdate]:

View File

@@ -22,7 +22,6 @@ class AURIndexUpdater(Thread):
self.man = man self.man = man
def run(self): def run(self):
while True: while True:
self.logger.info('Pre-indexing AUR packages in memory') self.logger.info('Pre-indexing AUR packages in memory')
res = self.http_client.get(URL_INDEX) res = self.http_client.get(URL_INDEX)

View File

@@ -1,5 +1,7 @@
import json import json
import logging
import os import os
import time
from threading import Thread, Lock from threading import Thread, Lock
from typing import Type, Dict from typing import Type, Dict
@@ -10,13 +12,15 @@ from bauh.api.abstract.model import SoftwarePackage
class AsyncDiskCacheLoader(Thread, DiskCacheLoader): class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache]): def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache], logger: logging.Logger):
super(AsyncDiskCacheLoader, self).__init__(daemon=True) super(AsyncDiskCacheLoader, self).__init__(daemon=True)
self.apps = [] self.pkgs = []
self.stop = False self._work = True
self.lock = Lock() self.lock = Lock()
self.cache_map = cache_map self.cache_map = cache_map
self.enabled = enabled self.enabled = enabled
self.logger = logger
self.processed = 0
def fill(self, pkg: SoftwarePackage): def fill(self, pkg: SoftwarePackage):
""" """
@@ -24,25 +28,28 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
:param pkg: :param pkg:
:return: :return:
""" """
if self.enabled: if self.enabled and pkg and pkg.supports_disk_cache():
if pkg and pkg.supports_disk_cache(): self.pkgs.append(pkg)
self.lock.acquire()
self.apps.append(pkg) def stop_working(self):
self.lock.release() self._work = False
def run(self): def run(self):
if self.enabled: if self.enabled:
last = 0
while True: while True:
if self.apps: time.sleep(0.00001)
self.lock.acquire() if len(self.pkgs) > self.processed:
app = self.apps[0] pkg = self.pkgs[last]
del self.apps[0]
self.lock.release() self._fill_cached_data(pkg)
self._fill_cached_data(app) self.processed += 1
elif self.stop: last += 1
elif not self._work:
break break
def _fill_cached_data(self, pkg: SoftwarePackage): def _fill_cached_data(self, pkg: SoftwarePackage) -> bool:
if self.enabled: if self.enabled:
if os.path.exists(pkg.get_disk_data_path()): if os.path.exists(pkg.get_disk_data_path()):
with open(pkg.get_disk_data_path()) as f: with open(pkg.get_disk_data_path()) as f:
@@ -54,12 +61,16 @@ class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
if cache: if cache:
cache.add_non_existing(pkg.id, cached_data) cache.add_non_existing(pkg.id, cached_data)
return True
return False
class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory): class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
def __init__(self, disk_cache_enabled: bool): def __init__(self, disk_cache_enabled: bool, logger: logging.Logger):
super(DefaultDiskCacheLoaderFactory, self).__init__() super(DefaultDiskCacheLoaderFactory, self).__init__()
self.disk_cache_enabled = disk_cache_enabled self.disk_cache_enabled = disk_cache_enabled
self.logger = logger
self.cache_map = {} self.cache_map = {}
def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache): def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache):
@@ -68,4 +79,4 @@ class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
self.cache_map[pkg_type] = cache self.cache_map[pkg_type] = cache
def new(self) -> AsyncDiskCacheLoader: def new(self) -> AsyncDiskCacheLoader:
return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map) return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map, logger=self.logger)

View File

@@ -5,7 +5,7 @@ from typing import List, Type, Set
from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal from PyQt5.QtCore import QEvent, Qt, QSize, pyqtSignal
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap, QCursor
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHeaderView, QToolBar, \
QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QPushButton, QComboBox, QMenu, QAction, QApplication
from bauh.api.abstract.cache import MemoryCache from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.context import ApplicationContext
@@ -231,7 +231,8 @@ class ManageWindow(QWidget):
self.toolbar_bottom.addWidget(new_spacer()) self.toolbar_bottom.addWidget(new_spacer())
self.progress_bar = QProgressBar() self.progress_bar = QProgressBar()
self.progress_bar.setMaximumHeight(7) self.progress_bar.setMaximumHeight(10 if QApplication.instance().style().objectName().lower() == 'windows' else 4)
self.progress_bar.setTextVisible(False) self.progress_bar.setTextVisible(False)
self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar) self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)