Improve backup functionality (#3258)

Waits for an ongoing backup task to complete in onDisable (and yells at users for `/reload`ing), and adds a `backup.always-run` option to enable always running backups even when no users have logged in since the last backup.

Fixes #3257 and closes #2646.
This commit is contained in:
Josh Roy 2020-05-11 11:55:31 -04:00 committed by GitHub
parent 61d0ed3f01
commit 8b71437264
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 4 deletions

View File

@ -7,6 +7,8 @@ import org.bukkit.command.CommandSender;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -20,11 +22,13 @@ public class Backup implements Runnable {
private transient boolean running = false;
private transient int taskId = -1;
private transient boolean active = false;
private final AtomicBoolean pendingShutdown = new AtomicBoolean(false);
private transient CompletableFuture<Object> taskLock = null;
public Backup(final IEssentials ess) {
this.ess = ess;
server = ess.getServer();
if (!ess.getOnlinePlayers().isEmpty()) {
if (!ess.getOnlinePlayers().isEmpty() || ess.getSettings().isAlwaysRunBackup()) {
ess.runTaskAsynchronously(this::startTask);
}
}
@ -52,12 +56,21 @@ public class Backup implements Runnable {
}
}
public CompletableFuture<Object> getTaskLock() {
return taskLock;
}
public void setPendingShutdown(boolean shutdown) {
pendingShutdown.set(shutdown);
}
@Override
public void run() {
if (active) {
return;
}
active = true;
taskLock = new CompletableFuture<>();
final String command = ess.getSettings().getBackupCommand();
if (command == null || "".equals(command)) {
return;
@ -66,6 +79,7 @@ public class Backup implements Runnable {
final CommandSender cs = server.getConsoleSender();
server.dispatchCommand(cs, "save-all");
active = false;
taskLock.complete(new Object());
return;
}
LOGGER.log(Level.INFO, tl("backupStarted"));
@ -102,14 +116,17 @@ public class Backup implements Runnable {
@Override
public void run() {
server.dispatchCommand(cs, "save-on");
if (ess.getOnlinePlayers().isEmpty()) {
if (!ess.getSettings().isAlwaysRunBackup() && ess.getOnlinePlayers().isEmpty()) {
stopTask();
}
active = false;
taskLock.complete(new Object());
LOGGER.log(Level.INFO, tl("backupFinished"));
}
}
ess.scheduleSyncDelayedTask(new BackupEnableSaveTask());
if (!pendingShutdown.get()) {
ess.scheduleSyncDelayedTask(new BackupEnableSaveTask());
}
}
});
}

View File

@ -314,6 +314,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
handleCrash(ex);
throw ex;
}
getBackup().setPendingShutdown(false);
}
@Override
@ -363,12 +364,17 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
@Override
public void onDisable() {
boolean stopping = ServerState.isStopping();
if (!stopping) {
LOGGER.log(Level.SEVERE, tl("serverReloading"));
}
getBackup().setPendingShutdown(true);
for (User user : getOnlineUsers()) {
if (user.isVanished()) {
user.setVanished(false);
user.sendMessage(tl("unvanishedReload"));
}
if (ServerState.isStopping()) {
if (stopping) {
user.setLastLocation();
if (!user.isHidden()) {
user.setLastLogout(System.currentTimeMillis());
@ -379,6 +385,10 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
}
cleanupOpenInventories();
if (getBackup().getTaskLock() != null && !getBackup().getTaskLock().isDone()) {
LOGGER.log(Level.SEVERE, tl("backupInProgress"));
getBackup().getTaskLock().join();
}
if (i18n != null) {
i18n.onDisable();
}

View File

@ -32,6 +32,8 @@ public interface ISettings extends IConf {
long getBackupInterval();
boolean isAlwaysRunBackup();
String getChatFormat(String group);
int getChatRadius();

View File

@ -425,6 +425,11 @@ public class Settings implements net.ess3.api.ISettings {
return config.getString("backup.command", null);
}
@Override
public boolean isAlwaysRunBackup() {
return config.getBoolean("backup.always-run", false);
}
private final Map<String, String> chatFormats = Collections.synchronizedMap(new HashMap<>());
@Override

View File

@ -371,6 +371,8 @@ unprotected-sign-names:
backup:
# Interval in minutes.
interval: 30
# If true, the backup task will run even if there are no players online.
always-run: false
# Unless you add a valid backup command or script here, this feature will be useless.
# Use 'save-all' to simply force regular world saving without backup.
#command: 'rdiff-backup World1 backups/World1'

View File

@ -27,6 +27,7 @@ backOther=\u00a76Returned\u00a7c {0}\u00a76 to previous location.
backupDisabled=\u00a74An external backup script has not been configured.
backupFinished=\u00a76Backup finished.
backupStarted=\u00a76Backup started.
backupInProgress=\u00a76An external backup script is currently in progress! Halting plugin disable until finished.
backUsageMsg=\u00a76Returning to previous location.
balance=\u00a7aBalance\:\u00a7c {0}
balanceOther=\u00a7aBalance of {0}\u00a7a\:\u00a7c {1}
@ -484,6 +485,7 @@ seenOnline=\u00a76Player\u00a7c {0} \u00a76has been \u00a7aonline\u00a76 since \
sellBulkPermission=\u00a76You do not have permission to bulk sell.
sellHandPermission=\u00a76You do not have permission to hand sell.
serverFull=Server is full\!
serverReloading=There's a good chance you're reloading your server right now. If that's the case, why do you hate yourself? Expect no support from the EssentialsX team when using /reload.
serverTotal=\u00a76Server Total\:\u00a7c {0}
serverUnsupported=You are running an unsupported server version!
setBal=\u00a7aYour balance was set to {0}.