A backup is restored at boot, and never undoes a protective act
PrivaPub admin restore <id> (and soon the administrator's page) checks the backup (same host, a format and newest migration this build reads, every hash) and writes restore.json; the running service sees it within seconds and stops, and the next start restores it in MaintenanceGate, before migrations, indexes and hosted services: a pre-restore backup taken once, every collection dropped and imported raw with its indexes, the media the live directory lacks brought back, then the protective merge from the pre-restore backup. Followers and follows are the live ones; blocks, mutes, domain blocks, reserved names, tombstones, reports, filters and OAuth applications are the union; deletions win; accounts made since become tombstones and local posts made since answer 410; every session ends. Each attempt redoes everything; one refused before any change is abandoned and recorded, one failed midway exits 1 for systemd to retry, and after three it exits 75, which the unit no longer restarts. Commands wait (exit 75) while a restore is pending. RestoreRecord tells what happened (admin restore --status). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
12bb75809f
commit
fba57318fa
13 files changed
+1050
-11
No files matched your search
@@ -0,0 +1,59 @@
|
||||
namespace PrivaPub.Infrastructure.Backup
|
||||
{
|
||||
// What Program asks before migrations: whether a restore waits, and what this process does about it. The service carries
|
||||
// it out; a command waits for it (exit 75, EX_TEMPFAIL), except asking how it went.
|
||||
public static class MaintenanceGate
|
||||
{
|
||||
/// <summary>The exit code that stops systemd restarting the service (RestartPreventExitStatus=75): a restore gave up.</summary>
|
||||
public const int TryLater = 75;
|
||||
|
||||
/// <summary>Null to go on booting; else the code to exit with.</summary>
|
||||
public static async Task<int?> Enter(IServiceProvider services, string[] command, CancellationToken token)
|
||||
{
|
||||
var backups = services.GetRequiredService<Backups>();
|
||||
var logger = services.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(MaintenanceGate));
|
||||
if (command != default)
|
||||
{
|
||||
var waiting = RestoreMarker.Read(backups.Root);
|
||||
if (waiting == default || command is ["restore", ..])
|
||||
return default;
|
||||
Console.Error.WriteLine($"the restore of {waiting.Backup} is {waiting.State}{(waiting.Error == default ? string.Empty : $" ({waiting.Error})")}: try again once the service has carried it out (PrivaPub admin restore --status)");
|
||||
return TryLater;
|
||||
}
|
||||
return await ServerRestore.ApplyPending(backups.Context(), logger, token) switch
|
||||
{
|
||||
RestoreOutcome.Failed => 1,//systemd starts it again, and the next attempt redoes everything
|
||||
RestoreOutcome.GaveUp => TryLater,
|
||||
_ => (int?)null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// A restore asked for while the service runs (from the administrator's page or the CLI): the service stops within
|
||||
// seconds, systemd starts it again, and the restore runs before anything else.
|
||||
public class RestoreWatcher(Backups backups, IHostApplicationLifetime lifetime, ILogger<RestoreWatcher> logger) : BackgroundService
|
||||
{
|
||||
static readonly TimeSpan Interval = TimeSpan.FromSeconds(3);
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(Interval, stoppingToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (RestoreMarker.Read(backups.Root) is { State: "pending" } marker)
|
||||
{
|
||||
logger.LogWarning("A restore of {Backup} was asked for by {RequestedBy}: stopping, to restore it at the next start", marker.Backup, marker.RequestedBy);
|
||||
lifetime.StopApplication();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user