mirror of
https://github.com/garethgeorge/backrest.git
synced 2025-12-16 18:45:36 +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.
|
// 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 {
|
func (o *Orchestrator) runVersion(mainCtx context.Context, config *v1.Config) bool {
|
||||||
lock := sync.Mutex{}
|
var lock sync.Mutex
|
||||||
ctx, cancel := context.WithCancel(mainCtx)
|
ctx, cancel := context.WithCancel(mainCtx)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -140,7 +140,9 @@ func (o *Orchestrator) runVersion(mainCtx context.Context, config *v1.Config) bo
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
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()))
|
zap.L().Debug("cancelled scheduled (but not running) task, orchestrator context is cancelled.", zap.String("task", t.Name()))
|
||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
|
|||||||
@@ -19,12 +19,6 @@ type RepoOrchestrator struct {
|
|||||||
|
|
||||||
repoConfig *v1.Repo
|
repoConfig *v1.Repo
|
||||||
repo *restic.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 {
|
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 {
|
func (r *RepoOrchestrator) Snapshots(ctx context.Context) ([]*restic.Snapshot, 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()
|
|
||||||
|
|
||||||
snapshots, err := r.repo.Snapshots(ctx, restic.WithPropagatedEnvVars(restic.EnvToPropagate...), restic.WithFlags("--latest", "1000"))
|
snapshots, err := r.repo.Snapshots(ctx, restic.WithPropagatedEnvVars(restic.EnvToPropagate...), restic.WithFlags("--latest", "1000"))
|
||||||
if err != nil {
|
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 {
|
sort.SliceStable(snapshots, func(i, j int) bool {
|
||||||
return snapshots[i].UnixTimeMs() < snapshots[j].UnixTimeMs()
|
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 snapshots, nil
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RepoOrchestrator) SnapshotsForPlan(ctx context.Context, plan *v1.Plan) ([]*restic.Snapshot, error) {
|
func (r *RepoOrchestrator) SnapshotsForPlan(ctx context.Context, plan *v1.Plan) ([]*restic.Snapshot, error) {
|
||||||
r.snapshotsMu.Lock()
|
snapshots, err := r.Snapshots(ctx)
|
||||||
defer r.snapshotsMu.Unlock()
|
if err != nil {
|
||||||
|
|
||||||
if err := r.updateSnapshotsIfNeeded(ctx, false); err != nil {
|
|
||||||
return nil, err
|
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) {
|
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)
|
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)))
|
zap.L().Debug("Backup completed", zap.String("repo", r.repoConfig.Id), zap.Duration("duration", time.Since(startTime)))
|
||||||
|
|
||||||
|
|
||||||
return summary, nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ export const OperationList = ({
|
|||||||
}: React.PropsWithoutRef<{ operations: EOperation[] }>) => {
|
}: React.PropsWithoutRef<{ operations: EOperation[] }>) => {
|
||||||
operations.sort((a, b) => b.parsedTime - a.parsedTime);
|
operations.sort((a, b) => b.parsedTime - a.parsedTime);
|
||||||
|
|
||||||
const elems = operations.map((operation) => (
|
|
||||||
<OperationRow operation={operation} />
|
|
||||||
));
|
|
||||||
|
|
||||||
if (operations.length === 0) {
|
if (operations.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Empty
|
<Empty
|
||||||
@@ -38,6 +34,11 @@ export const OperationList = ({
|
|||||||
renderItem={(item, index) => (
|
renderItem={(item, index) => (
|
||||||
<OperationRow key={item.parsedId} operation={item} />
|
<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;
|
const backupOp = operation.operationBackup;
|
||||||
let desc = `Backup at ${formatTime(operation.unixTimeStartMs!)}`;
|
let desc = `Backup at ${formatTime(operation.unixTimeStartMs!)}`;
|
||||||
if (operation.status !== OperationStatus.STATUS_INPROGRESS) {
|
if (operation.status !== OperationStatus.STATUS_INPROGRESS) {
|
||||||
desc += ` and finished at ${formatTime(operation.unixTimeEndMs!)}`;
|
desc += ` completed in ${formatDuration(
|
||||||
|
parseInt(operation.unixTimeEndMs!) -
|
||||||
|
parseInt(operation.unixTimeStartMs!)
|
||||||
|
)}`;
|
||||||
} else {
|
} else {
|
||||||
desc += " and is still running.";
|
desc += " and is still running.";
|
||||||
}
|
}
|
||||||
@@ -273,5 +277,17 @@ const formatTime = (time: number | string) => {
|
|||||||
}
|
}
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
d.setTime(time);
|
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