mirror of
https://github.com/garethgeorge/backrest.git
synced 2025-12-13 09:15:39 +00:00
fix: improve UI performance
This commit is contained in:
@@ -117,7 +117,7 @@ func (o *Orchestrator) Run(mainCtx context.Context) error {
|
||||
|
||||
// runImmutable is a helper function for Run() that runs the orchestration loop with a single version of the config.
|
||||
func (o *Orchestrator) runVersion(mainCtx context.Context, config *v1.Config) bool {
|
||||
lock := sync.Mutex{}
|
||||
var lock sync.Mutex
|
||||
ctx, cancel := context.WithCancel(mainCtx)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
@@ -140,7 +140,9 @@ func (o *Orchestrator) runVersion(mainCtx context.Context, config *v1.Config) bo
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
zap.L().Debug("cancelled scheduled (but not running) task, orchestrator context is cancelled.", zap.String("task", t.Name()))
|
||||
return
|
||||
case <-timer.C:
|
||||
|
||||
@@ -19,12 +19,6 @@ type RepoOrchestrator struct {
|
||||
|
||||
repoConfig *v1.Repo
|
||||
repo *restic.Repo
|
||||
|
||||
// TODO: decide if snapshot caching is a good idea. We gain performance but
|
||||
// increase background memory use by a small amount at all times (probably on the order of 1MB).
|
||||
snapshotsMu sync.Mutex // enable very fast snapshot access IF no update is required.
|
||||
snapshotsResetTimer *time.Timer
|
||||
snapshots []*restic.Snapshot
|
||||
}
|
||||
|
||||
func newRepoOrchestrator(repoConfig *v1.Repo, repo *restic.Repo) *RepoOrchestrator {
|
||||
@@ -34,64 +28,26 @@ func newRepoOrchestrator(repoConfig *v1.Repo, repo *restic.Repo) *RepoOrchestrat
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) updateSnapshotsIfNeeded(ctx context.Context, force bool) error {
|
||||
if r.snapshots != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.snapshotsResetTimer != nil {
|
||||
if !r.snapshotsResetTimer.Stop() {
|
||||
<-r.snapshotsResetTimer.C
|
||||
}
|
||||
}
|
||||
|
||||
r.snapshotsResetTimer = time.AfterFunc(10 * time.Minute, func() {
|
||||
r.snapshotsMu.Lock()
|
||||
defer r.snapshotsMu.Unlock()
|
||||
r.snapshots = nil
|
||||
})
|
||||
|
||||
if r.snapshots != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
func (r *RepoOrchestrator) Snapshots(ctx context.Context) ([]*restic.Snapshot, error) {
|
||||
snapshots, err := r.repo.Snapshots(ctx, restic.WithPropagatedEnvVars(restic.EnvToPropagate...), restic.WithFlags("--latest", "1000"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update snapshots: %w", err)
|
||||
return nil, fmt.Errorf("restic.Snapshots: %w", err)
|
||||
}
|
||||
|
||||
sort.SliceStable(snapshots, func(i, j int) bool {
|
||||
return snapshots[i].UnixTimeMs() < snapshots[j].UnixTimeMs()
|
||||
})
|
||||
r.snapshots = snapshots
|
||||
|
||||
zap.L().Debug("updated snapshots", zap.String("repo", r.repoConfig.Id), zap.Duration("duration", time.Since(startTime)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) Snapshots(ctx context.Context) ([]*restic.Snapshot, error) {
|
||||
r.snapshotsMu.Lock()
|
||||
defer r.snapshotsMu.Unlock()
|
||||
if err := r.updateSnapshotsIfNeeded(ctx, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.snapshots, nil
|
||||
return snapshots, nil
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) SnapshotsForPlan(ctx context.Context, plan *v1.Plan) ([]*restic.Snapshot, error) {
|
||||
r.snapshotsMu.Lock()
|
||||
defer r.snapshotsMu.Unlock()
|
||||
|
||||
if err := r.updateSnapshotsIfNeeded(ctx, false); err != nil {
|
||||
snapshots, err := r.Snapshots(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return filterSnapshotsForPlan(r.snapshots, plan), nil
|
||||
return filterSnapshotsForPlan(snapshots, plan), nil
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) Backup(ctx context.Context, plan *v1.Plan, progressCallback func(event *restic.BackupProgressEntry)) (*restic.BackupProgressEntry, error) {
|
||||
@@ -123,14 +79,7 @@ func (r *RepoOrchestrator) Backup(ctx context.Context, plan *v1.Plan, progressCa
|
||||
return nil, fmt.Errorf("failed to backup: %w", err)
|
||||
}
|
||||
|
||||
// Reset snapshots since a new backup has been added.
|
||||
r.snapshotsMu.Lock()
|
||||
r.snapshots = nil
|
||||
r.snapshotsMu.Unlock()
|
||||
|
||||
zap.L().Debug("Backup completed", zap.String("repo", r.repoConfig.Id), zap.Duration("duration", time.Since(startTime)))
|
||||
|
||||
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,6 @@ export const OperationList = ({
|
||||
}: React.PropsWithoutRef<{ operations: EOperation[] }>) => {
|
||||
operations.sort((a, b) => b.parsedTime - a.parsedTime);
|
||||
|
||||
const elems = operations.map((operation) => (
|
||||
<OperationRow operation={operation} />
|
||||
));
|
||||
|
||||
if (operations.length === 0) {
|
||||
return (
|
||||
<Empty
|
||||
@@ -38,6 +34,11 @@ export const OperationList = ({
|
||||
renderItem={(item, index) => (
|
||||
<OperationRow key={item.parsedId} operation={item} />
|
||||
)}
|
||||
pagination={
|
||||
operations.length > 50
|
||||
? { position: "both", align: "center", defaultPageSize: 50 }
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -68,7 +69,10 @@ export const OperationRow = ({
|
||||
const backupOp = operation.operationBackup;
|
||||
let desc = `Backup at ${formatTime(operation.unixTimeStartMs!)}`;
|
||||
if (operation.status !== OperationStatus.STATUS_INPROGRESS) {
|
||||
desc += ` and finished at ${formatTime(operation.unixTimeEndMs!)}`;
|
||||
desc += ` completed in ${formatDuration(
|
||||
parseInt(operation.unixTimeEndMs!) -
|
||||
parseInt(operation.unixTimeStartMs!)
|
||||
)}`;
|
||||
} else {
|
||||
desc += " and is still running.";
|
||||
}
|
||||
@@ -273,5 +277,17 @@ const formatTime = (time: number | string) => {
|
||||
}
|
||||
const d = new Date();
|
||||
d.setTime(time);
|
||||
return d.toLocaleString();
|
||||
return d.toISOString();
|
||||
};
|
||||
|
||||
const formatDuration = (ms: number) => {
|
||||
const seconds = Math.floor(ms / 100) / 10;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours === 0 && minutes === 0) {
|
||||
return `${seconds % 60}s`;
|
||||
} else if (hours === 0) {
|
||||
return `${minutes}m${seconds % 60}s`;
|
||||
}
|
||||
return `${hours}h${minutes % 60}m${seconds % 60}s`;
|
||||
};
|
||||
Reference in New Issue
Block a user