README.adoc
@@ -14,7 +14,7 @@ ~/.borgbutler.json ---- { "cache_max_disc_size_mb" : 1000, "cache_archive_content_max_disc_size_mb" : 1000, "repo-configs" : [ { "name" : "My-Laptop-Local-Backup", "repo" : "/Volumes/backup/my-laptop-backup", borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ArchiveFilelistCache.java
@@ -12,13 +12,21 @@ import org.slf4j.LoggerFactory; import java.io.*; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; class ArchiveFilelistCache { private static Logger log = LoggerFactory.getLogger(ArchiveFilelistCache.class); private static final String CACHE_ARCHIVE_LISTS_BASENAME = "archive-content-"; private static final String CACHE_FILE_GZIP_EXTENSION = "gz"; private File cacheDir; private int cacheArchiveContentMaxDiscSizeMB; private long FILES_EXPIRE_TIME = 5 * 24 * 3660 * 1000; // Expires after 5 days. @Getter private Archive archive; @@ -80,10 +88,70 @@ */ public void cleanUp() { File[] files = cacheDir.listFiles(); long currentMillis = System.currentTimeMillis(); for (File file : files) { try { if (!file.exists() || !isCacheFile(file)) continue; // Get last access time of file: FileTime time = Files.readAttributes(file.toPath(), BasicFileAttributes.class).lastAccessTime(); if (currentMillis - FILES_EXPIRE_TIME > time.toMillis()) { log.info("Delete old cache file (last access " + time + " older than 5 days): " + file.getAbsolutePath()); file.delete(); } } catch (IOException ex) { log.error("Can't get last accesstime from cache files (ignore file '" + file.getAbsolutePath() + "'): " + ex.getMessage(), ex); } } int sizeInMB = getCacheDiskSizeInMB(files); if (sizeInMB > cacheArchiveContentMaxDiscSizeMB) { log.info("Maximum size of cache files exceeded (" + sizeInMB + "MB > " + cacheArchiveContentMaxDiscSizeMB + "MB). Deleting the old ones (with the oldest access)..."); } SortedMap<FileTime, File> sortedFiles = new TreeMap<>(); for (File file : files) { if (!file.exists() || !isCacheFile(file)) continue; try { // Get last access time of file: FileTime time = Files.readAttributes(file.toPath(), BasicFileAttributes.class).lastAccessTime(); sortedFiles.put(time, file); } catch (IOException ex) { log.error("Can't get last accesstime from cache files (ignore file '" + file.getAbsolutePath() + "'): " + ex.getMessage(), ex); } } for (Map.Entry<FileTime, File> entry : sortedFiles.entrySet()) { FileTime time = entry.getKey(); File file = entry.getValue(); if (!file.exists() || !isCacheFile(file)) continue; log.info("Deleting cache file (last access " + time + "): " + file.getAbsolutePath()); file.delete(); int newSizeInMB = getCacheDiskSizeInMB(files); if (newSizeInMB < cacheArchiveContentMaxDiscSizeMB) { log.info("New cache size is " + newSizeInMB + "MB. (" + (sizeInMB - newSizeInMB) + "MB deleted.)"); break; } } } public int getCacheDiskSizeInMB() { return getCacheDiskSizeInMB(cacheDir.listFiles()); } private int getCacheDiskSizeInMB(File[] files) { int sizeInMB = 0; for (File file : files) { if (!file.exists()) continue; if (!isCacheFile(file)) continue; sizeInMB += (int) (file.length() / 1048576); // In MB } return sizeInMB; } public void removeAllCacheFiles() { File[] files = cacheDir.listFiles(); for (File file : files) { if (isCacheFile(file)) { log.info("Processing cache file: " + file.getAbsolutePath()); //file.delete(); log.info("Deleting cache file: " + file.getAbsolutePath()); file.delete(); } } } @@ -93,8 +161,9 @@ + "-" + repoConfig.getRepo() + "-" + archive.getArchive() + ".gz", true)); } ArchiveFilelistCache(File cacheDir) { ArchiveFilelistCache(File cacheDir, int cacheArchiveContentMaxDiscSizeMB) { this.cacheDir = cacheDir; this.cacheArchiveContentMaxDiscSizeMB = cacheArchiveContentMaxDiscSizeMB; } private boolean isCacheFile(File file) { borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -2,6 +2,7 @@ import de.micromata.borgbutler.BorgCommands; import de.micromata.borgbutler.config.BorgRepoConfig; import de.micromata.borgbutler.config.Configuration; import de.micromata.borgbutler.config.ConfigurationHandler; import de.micromata.borgbutler.json.borg.*; import org.apache.commons.collections4.CollectionUtils; @@ -103,9 +104,10 @@ } private ButlerCache() { Configuration configuration = ConfigurationHandler.getConfiguration(); this.jcsCache = JCSCache.getInstance(); this.repoInfoCacheAccess = jcsCache.getJCSCache("repoInfo"); this.repoListCacheAccess = jcsCache.getJCSCache("repoList"); this.archiveFilelistCache = new ArchiveFilelistCache(getCacheDir()); this.archiveFilelistCache = new ArchiveFilelistCache(getCacheDir(), configuration.getCacheArchiveContentMaxDiscSizeMB()); } } borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
@@ -10,11 +10,11 @@ @Getter private String borgCommand = "borg"; /** * Default is 200 MB (approximately). * Default is 100 MB (approximately). */ @Getter @JsonProperty("cache_max_disc_size_mb") private int cacheMaxDiscSizeMB = 200; @JsonProperty("cache_archive_content_max_disc_size_mb") private int cacheArchiveContentMaxDiscSizeMB = 100; @Getter @JsonProperty("repo-configs") borgbutler-core/src/test/java/de/micromata/borgbutler/cache/ArchiveFilelistCacheTest.java
@@ -8,7 +8,11 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.List; @@ -21,15 +25,12 @@ @Test void readWriteTest() throws Exception { List<FilesystemItem> list = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { list.add(create(i)); } ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out")); List<FilesystemItem> list = createList(1000000); ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 100); cache.removeAllCacheFiles(); BorgRepoConfig repoConfig = new BorgRepoConfig(); repoConfig.setRepo("repo"); Archive archive = new Archive(); set(archive, "archive", "archive-2018-12-10"); Archive archive = createArchive("2018-12-10"); log.info("Saving " + list.size() + " items to out dir."); cache.save(repoConfig, archive, list); log.info("Saving done."); @@ -40,23 +41,53 @@ for (int i = 0; i < filesystemItems.length; i++) { assertEquals(list.get(i).getPath(), filesystemItems[i].getPath()); } cache.removeAllCacheFiles(); } @Test void readWriteEmptyTest() throws Exception { List<FilesystemItem> list = new ArrayList<>(); ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out")); ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 100); cache.removeAllCacheFiles(); BorgRepoConfig repoConfig = new BorgRepoConfig(); repoConfig.setRepo("repo"); Archive archive = new Archive(); set(archive, "archive", "archive-2018-12-09"); if (cache.getFile(repoConfig, archive).exists()) { cache.getFile(repoConfig, archive).delete(); } Archive archive = createArchive("2018-12-09"); assertNull(cache.load(repoConfig, archive)); cache.save(repoConfig, archive, list); FilesystemItem[] filesystemItems = cache.load(repoConfig, archive); assertNull(cache.load(repoConfig, archive)); cache.removeAllCacheFiles(); } @Test void cleanUpTest() throws Exception { List<FilesystemItem> list = createList(1000000); ArchiveFilelistCache cache = new ArchiveFilelistCache(new File("out"), 5); cache.removeAllCacheFiles(); BorgRepoConfig repoConfig = new BorgRepoConfig(); repoConfig.setRepo("repo"); long millis = System.currentTimeMillis(); Archive archive = createArchive("2018-11-20"); cache.save(repoConfig, archive, list); setLastAccessTime(cache.getFile(repoConfig, archive), millis - 10 * 3600000); // Fake lastAccessTime - 10 h archive = createArchive("2018-11-21"); cache.save(repoConfig, archive, list); setLastAccessTime(cache.getFile(repoConfig, archive), millis - 60000); // Fake lastAccessTime - 1min cache.save(repoConfig, archive, list); cache.cleanUp(); cache.removeAllCacheFiles(); } private List<FilesystemItem> createList(int number) throws Exception { List<FilesystemItem> list = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { list.add(create(i)); } return list; } private FilesystemItem create(int i) throws Exception { @@ -73,4 +104,17 @@ f1.set(obj, value); return this; } private Archive createArchive(String time) throws Exception { Archive archive = new Archive(); set(archive, "archive", "archive-" + time); set(archive, "time", time); return archive; } private void setLastAccessTime(File file, long accessTime) throws IOException { Path path = file.toPath(); FileTime fileTime = FileTime.fromMillis(accessTime); Files.setAttribute(path, "lastAccessTime", fileTime); } }