From bfb00dfa01b91b2bdc56e82b8dd6427d3da75e4b Mon Sep 17 00:00:00 2001 From: Marian Beermann Date: Sat, 23 Apr 2016 18:03:05 +0200 Subject: [PATCH] borg mount: cache partially read data chunks Fixes #966 --- borg/archiver.py | 5 +++++ borg/fuse.py | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/borg/archiver.py b/borg/archiver.py index c56da85f8..fc2919719 100644 --- a/borg/archiver.py +++ b/borg/archiver.py @@ -1533,6 +1533,11 @@ class Archiver: To allow a regular user to use fstab entries, add the ``user`` option: ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0`` + + The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users + to tweak the performance. It sets the number of cached data chunks; additional + memory usage can be up to ~8 MiB times this number. The default is the number + of CPU cores. """) subparser = subparsers.add_parser('mount', parents=[common_parser], add_help=False, description=self.do_mount.__doc__, diff --git a/borg/fuse.py b/borg/fuse.py index e1de56281..d92c24ed4 100644 --- a/borg/fuse.py +++ b/borg/fuse.py @@ -8,9 +8,13 @@ import tempfile import time from .archive import Archive from .helpers import daemonize, bigint_to_int +from .logger import create_logger +from .lrucache import LRUCache from distutils.version import LooseVersion import msgpack +logger = create_logger() + # Does this version of llfuse support ns precision? have_fuse_xtime_ns = hasattr(llfuse.EntryAttributes, 'st_mtime_ns') @@ -54,6 +58,9 @@ class FuseOperations(llfuse.Operations): self.pending_archives = {} self.accounted_chunks = {} self.cache = ItemCache() + data_cache_capacity = int(os.environ.get('BORG_MOUNT_DATA_CACHE_ENTRIES', os.cpu_count() or 1)) + logger.debug('mount data cache capacity: %d chunks', data_cache_capacity) + self.data_cache = LRUCache(capacity=data_cache_capacity, dispose=lambda _: None) if archive: self.process_archive(archive) else: @@ -229,7 +236,16 @@ class FuseOperations(llfuse.Operations): offset -= s continue n = min(size, s - offset) - _, data = self.key.decrypt(id, self.repository.get(id)) + if id in self.data_cache: + data = self.data_cache[id] + if offset + n == len(data): + # evict fully read chunk from cache + del self.data_cache[id] + else: + _, data = self.key.decrypt(id, self.repository.get(id)) + if offset + n < len(data): + # chunk was only partially read, cache it + self.data_cache[id] = data parts.append(data[offset:offset + n]) offset = 0 size -= n