浏览代码

info: use a pre12-meta cache to accelerate stats for borg < 1.2 archives

first time borg info is invoked on a borg 1.1 repo, it can take
a rather long time computing and caching some stats values for
1.1 archives, which borg 1.2 archives have in their archive
metadata structure. be patient, esp. if you have lots of old
archives.

following invocations are much faster.
Thomas Waldmann 3 年之前
父节点
当前提交
25e27a1539
共有 2 个文件被更改,包括 45 次插入0 次删除
  1. 32 0
      src/borg/archive.py
  2. 13 0
      src/borg/cache.py

+ 32 - 0
src/borg/archive.py

@@ -105,6 +105,27 @@ class Statistics:
             'nfiles': self.nfiles,
         }
 
+    def as_raw_dict(self):
+        return {
+            'size': self.osize,
+            'csize': self.csize,
+            'nfiles': self.nfiles,
+            'size_parts': self.osize_parts,
+            'csize_parts': self.csize_parts,
+            'nfiles_parts': self.nfiles_parts,
+        }
+
+    @classmethod
+    def from_raw_dict(cls, **kw):
+        self = cls()
+        self.osize = kw['size']
+        self.csize = kw['csize']
+        self.nfiles = kw['nfiles']
+        self.osize_parts = kw['size_parts']
+        self.csize_parts = kw['csize_parts']
+        self.nfiles_parts = kw['nfiles_parts']
+        return self
+
     @property
     def osize_fmt(self):
         return format_file_size(self.osize, iec=self.iec)
@@ -627,6 +648,17 @@ Utilization of max. archive size: {csize_max:.0%}
         self.cache.commit()
 
     def calc_stats(self, cache, want_unique=True):
+        # caching wrapper around _calc_stats which is rather slow for archives made with borg < 1.2
+        have_borg12_meta = self.metadata.get('nfiles') is not None
+        try:
+            stats = Statistics.from_raw_dict(**cache.pre12_meta[self.fpr])
+        except KeyError:  # not in pre12_meta cache
+            stats = self._calc_stats(cache, want_unique=want_unique)
+            if not have_borg12_meta:
+                cache.pre12_meta[self.fpr] = stats.as_raw_dict()
+        return stats
+
+    def _calc_stats(self, cache, want_unique=True):
         have_borg12_meta = self.metadata.get('nfiles') is not None
 
         if have_borg12_meta and not want_unique:

+ 13 - 0
src/borg/cache.py

@@ -1,4 +1,5 @@
 import configparser
+import json
 import os
 import shutil
 import stat
@@ -407,6 +408,7 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}"""
 
     def __init__(self, iec=False):
         self.iec = iec
+        self.pre12_meta = {}  # here we cache archive metadata for borg < 1.2
 
     def __str__(self):
         return self.str_format.format(self.format_tuple())
@@ -511,6 +513,8 @@ class LocalCache(CacheStatsMixin):
         os.makedirs(os.path.join(self.path, 'chunks.archive.d'))
         with SaveFile(os.path.join(self.path, files_cache_name()), binary=True):
             pass  # empty file
+        with SaveFile(os.path.join(self.path, 'pre12-meta'), binary=False) as fd:
+            json.dump(self.pre12_meta, fd, indent=4)
 
     def _do_open(self):
         self.cache_config.load()
@@ -521,6 +525,11 @@ class LocalCache(CacheStatsMixin):
             self.files = None
         else:
             self._read_files()
+        try:
+            with open(os.path.join(self.path, 'pre12-meta')) as fd:
+                self.pre12_meta = json.load(fd)
+        except (FileNotFoundError, json.JSONDecodeError):
+            pass
 
     def open(self):
         if not os.path.isdir(self.path):
@@ -529,6 +538,9 @@ class LocalCache(CacheStatsMixin):
         self.rollback()
 
     def close(self):
+        # save the pre12_meta cache in any case
+        with open(os.path.join(self.path, 'pre12-meta'), 'w') as fd:
+            json.dump(self.pre12_meta, fd, indent=4)
         if self.cache_config is not None:
             self.cache_config.close()
             self.cache_config = None
@@ -1066,6 +1078,7 @@ Chunk index:    {0.total_unique_chunks:20d}             unknown"""
         self.security_manager = SecurityManager(repository)
         self.security_manager.assert_secure(manifest, key, lock_wait=lock_wait)
 
+        self.pre12_meta = {}
         logger.warning('Note: --no-cache-sync is an experimental feature.')
 
     # Public API