Skip to content

Commit

Permalink
Speedup backups slightly + max backup folder size config (#100)
Browse files Browse the repository at this point in the history
* switch backups to common compress lib & add max folder size config

* Change uncompressed backup to a STORED zip instead of folder

* disabled by default

* stringbuilder be gone

* custom-named backups should be deleted by default

* iterator isn't necessary anymore

* update configs

---------

Co-authored-by: Martin Robertz <dream-master@gmx.net>
  • Loading branch information
Lyfts and Dream-Master authored Sep 13, 2024
1 parent 6aa640b commit 9b013ce
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 126 deletions.
10 changes: 10 additions & 0 deletions src/main/java/serverutils/ServerUtilitiesConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,16 @@ public static class Backups {
@Config.Comment("Silence backup notifications.")
@Config.DefaultBoolean(false)
public boolean silent_backup;

@Config.Comment("""
Max size of backup folder in GB. If total folder size exceeds this value it will delete old backups until the size is under.
0 = Disabled and backups_to_keep will be used instead.""")
@Config.DefaultInt(0)
public int max_folder_size;

@Config.Comment("Delete backups that have a custom name set through /backup start <name>")
@Config.DefaultBoolean(true)
public boolean delete_custom_name_backups;
}

public static class Login {
Expand Down
72 changes: 51 additions & 21 deletions src/main/java/serverutils/lib/util/FileUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package serverutils.lib.util;

import static serverutils.lib.util.FileUtils.SizeUnit.GB;
import static serverutils.lib.util.FileUtils.SizeUnit.KB;
import static serverutils.lib.util.FileUtils.SizeUnit.MB;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
Expand All @@ -14,13 +18,23 @@

public class FileUtils {

public static final int KB = 1024;
public static final int MB = KB * 1024;
public static final int GB = MB * 1024;
public enum SizeUnit {

B(1),
KB(B.size * 1024),
MB(KB.size * 1024),
GB(MB.size * 1024);

private final long size;

SizeUnit(long size) {
this.size = size;
}

public static final double KB_D = 1024D;
public static final double MB_D = KB_D * 1024D;
public static final double GB_D = MB_D * 1024D;
public long getSize() {
return size;
}
}

public static File newFile(File file) {
if (!file.exists()) {
Expand Down Expand Up @@ -104,33 +118,49 @@ public static void listTree0(List<File> list, File file) {
}

public static long getSize(File file) {
if (!file.exists()) {
return 0L;
} else if (file.isFile()) {
return file.length();
return getSize(file, SizeUnit.B);
}

public static long getSize(File file, SizeUnit sizeUnit) {
long size = getSize0(file);
if (size == 0L) return 0L;

return switch (sizeUnit) {
case KB -> size / KB.getSize();
case MB -> size / MB.getSize();
case GB -> size / GB.getSize();
default -> size;
};
}

private static long getSize0(File file) {
if (!file.exists()) return 0L;
long size = 0;

if (file.isFile()) {
size += file.length();
} else if (file.isDirectory()) {
long length = 0L;
File[] f1 = file.listFiles();
if (f1 != null && f1.length > 0) {
for (File aF1 : f1) {
length += getSize(aF1);
size += getSize0(aF1);
}
}
return length;
}
return 0L;

return size;
}

public static String getSizeString(double b) {
if (b >= GB_D) {
return String.format("%.1fGB", b / GB_D);
} else if (b >= MB_D) {
return String.format("%.1fMB", b / MB_D);
} else if (b >= KB_D) {
return String.format("%.1fKB", b / KB_D);
if (b >= GB.getSize()) {
return String.format("%.1fGB", b / (double) GB.getSize());
} else if (b >= MB.getSize()) {
return String.format("%.1fMB", b / (double) MB.getSize());
} else if (b >= KB.getSize()) {
return String.format("%.1fKB", b / (double) KB.getSize());
}

return ((long) b) + "B";
return b + "B";
}

public static String getSizeString(File file) {
Expand Down
62 changes: 47 additions & 15 deletions src/main/java/serverutils/task/backup/BackupTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

import static serverutils.ServerUtilitiesConfig.backups;
import static serverutils.ServerUtilitiesNotifications.BACKUP_START;
import static serverutils.lib.util.FileUtils.SizeUnit;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;

import serverutils.ServerUtilities;
import serverutils.ServerUtilitiesConfig;
Expand All @@ -23,6 +29,7 @@

public class BackupTask extends Task {

public static final Pattern BACKUP_NAME_PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2}(.*)");
public static File backupsFolder;
public static ThreadBackup thread;
public static boolean hadPlayer = false;
Expand Down Expand Up @@ -84,34 +91,58 @@ public void execute(Universe universe) {
}
}
} catch (Exception ex) {
ServerUtilities.LOGGER.info("Error while saving world: {}", ex.getMessage());
ServerUtilities.LOGGER.info("An error occurred while turning off auto-save.", ex);
}

File wd = server.getEntityWorld().getSaveHandler().getWorldDirectory();
File worldDir = DimensionManager.getCurrentSaveRootDirectory();

if (backups.use_separate_thread) {
thread = new ThreadBackup(wd, customName);
thread = new ThreadBackup(worldDir, customName);
thread.start();
} else {
ThreadBackup.doBackup(wd, customName);
ThreadBackup.doBackup(worldDir, customName);
}
universe.scheduleTask(new BackupTask(true));
}

public static void clearOldBackups() {
String[] backups = backupsFolder.list();
File[] files = backupsFolder.listFiles();
if (files == null || files.length == 0) return;

if (backups != null && backups.length > ServerUtilitiesConfig.backups.backups_to_keep) {
Arrays.sort(backups);
List<File> backupFiles = Arrays.stream(files).filter(
file -> backups.delete_custom_name_backups || BACKUP_NAME_PATTERN.matcher(file.getName()).matches())
.sorted(Comparator.comparingLong(File::lastModified)).collect(Collectors.toList());

int toDelete = backups.length - ServerUtilitiesConfig.backups.backups_to_keep;
ServerUtilities.LOGGER.info("Deleting {} old backups", toDelete);
int maxGb = backups.max_folder_size;
if (maxGb > 0) {
long currentSize = backupFiles.stream().mapToLong(file -> FileUtils.getSize(file, SizeUnit.GB)).sum();
if (currentSize <= maxGb) return;
deleteOldBackups(backupFiles, currentSize, maxGb);

for (int i = 0; i < toDelete; i++) {
File f = new File(backupsFolder, backups[i]);
ServerUtilities.LOGGER.info("Deleted old backup: {}", f.getPath());
FileUtils.delete(f);
}
} else if (backupFiles.size() > backups.backups_to_keep) {
deleteExcessBackups(backupFiles);
}
}

private static void deleteOldBackups(List<File> backupFiles, long currentSize, int maxGb) {
int deleted = 0;
for (File file : backupFiles) {
if (currentSize <= maxGb) break;
currentSize -= FileUtils.getSize(file, SizeUnit.GB);
ServerUtilities.LOGGER.info("Deleting old backup: {}", file.getPath());
FileUtils.delete(file);
deleted++;
}
ServerUtilities.LOGGER.info("Deleted {} old backups", deleted);
}

private static void deleteExcessBackups(List<File> backupFiles) {
int toDelete = backupFiles.size() - ServerUtilitiesConfig.backups.backups_to_keep;
ServerUtilities.LOGGER.info("Deleting {} old backups", toDelete);
for (int i = 0; i < toDelete; i++) {
File file = backupFiles.get(i);
ServerUtilities.LOGGER.info("Deleted old backup: {}", file.getPath());
FileUtils.delete(file);
}
}

Expand All @@ -126,6 +157,7 @@ private void postBackup(Universe universe) {
return;
}

clearOldBackups();
thread = null;
try {
MinecraftServer server = ServerUtils.getServer();
Expand All @@ -140,7 +172,7 @@ private void postBackup(Universe universe) {
}
}
} catch (Exception ex) {
ex.printStackTrace();
ServerUtilities.LOGGER.info("An error occurred while turning on auto-save.", ex);
}
}
}
Loading

0 comments on commit 9b013ce

Please sign in to comment.