Skip to content

Commit

Permalink
fix: UI and code quality improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
garethgeorge committed Dec 26, 2023
1 parent 6bfe769 commit c5e435d
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 48 deletions.
10 changes: 5 additions & 5 deletions internal/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func (s *Server) Backup(ctx context.Context, req *connect.Request[types.StringVa
if err != nil {
return nil, fmt.Errorf("failed to get plan %q: %w", req.Msg.Value, err)
}
s.orchestrator.ScheduleTask(orchestrator.NewOneofBackupTask(s.orchestrator, plan, time.Now()), orchestrator.TaskPriorityInteractive)
s.orchestrator.ScheduleTask(orchestrator.NewOneoffBackupTask(s.orchestrator, plan, time.Now()), orchestrator.TaskPriorityInteractive)
return connect.NewResponse(&emptypb.Empty{}), nil
}

Expand All @@ -266,8 +266,8 @@ func (s *Server) Forget(ctx context.Context, req *connect.Request[types.StringVa

at := time.Now()

s.orchestrator.ScheduleTask(orchestrator.NewOneofForgetTask(s.orchestrator, plan, "", at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityForget)
s.orchestrator.ScheduleTask(orchestrator.NewOneofIndexSnapshotsTask(s.orchestrator, plan, at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityIndexSnapshots)
s.orchestrator.ScheduleTask(orchestrator.NewOneoffForgetTask(s.orchestrator, plan, "", at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityForget)
s.orchestrator.ScheduleTask(orchestrator.NewOneoffIndexSnapshotsTask(s.orchestrator, plan, at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityIndexSnapshots)

return connect.NewResponse(&emptypb.Empty{}), nil
}
Expand All @@ -279,7 +279,7 @@ func (s *Server) Prune(ctx context.Context, req *connect.Request[types.StringVal
}

at := time.Now()
s.orchestrator.ScheduleTask(orchestrator.NewOneofPruneTask(s.orchestrator, plan, "", at, true), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityPrune)
s.orchestrator.ScheduleTask(orchestrator.NewOneoffPruneTask(s.orchestrator, plan, "", at, true), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityPrune)

return connect.NewResponse(&emptypb.Empty{}), nil
}
Expand All @@ -302,7 +302,7 @@ func (s *Server) Restore(ctx context.Context, req *connect.Request[v1.RestoreSna

at := time.Now()

s.orchestrator.ScheduleTask(orchestrator.NewOneofRestoreTask(s.orchestrator, orchestrator.RestoreTaskOpts{
s.orchestrator.ScheduleTask(orchestrator.NewOneoffRestoreTask(s.orchestrator, orchestrator.RestoreTaskOpts{
Plan: plan,
SnapshotId: req.Msg.SnapshotId,
Path: req.Msg.Path,
Expand Down
8 changes: 4 additions & 4 deletions internal/orchestrator/taskbackup.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewScheduledBackupTask(orchestrator *Orchestrator, plan *v1.Plan) (*BackupT
}, nil
}

func NewOneofBackupTask(orchestrator *Orchestrator, plan *v1.Plan, at time.Time) *BackupTask {
func NewOneoffBackupTask(orchestrator *Orchestrator, plan *v1.Plan, at time.Time) *BackupTask {
didOnce := false
return &BackupTask{
name: fmt.Sprintf("onetime backup for plan %q", plan.Id),
Expand Down Expand Up @@ -142,11 +142,11 @@ func backupHelper(ctx context.Context, orchestrator *Orchestrator, plan *v1.Plan
// schedule followup tasks
at := time.Now()
if plan.Retention != nil {
orchestrator.ScheduleTask(NewOneofForgetTask(orchestrator, plan, op.SnapshotId, at), TaskPriorityForget)
orchestrator.ScheduleTask(NewOneoffForgetTask(orchestrator, plan, op.SnapshotId, at), TaskPriorityForget)
}

orchestrator.ScheduleTask(NewOneofIndexSnapshotsTask(orchestrator, plan, at), TaskPriorityIndexSnapshots)
orchestrator.ScheduleTask(NewOneofStatsTask(orchestrator, plan, op.SnapshotId, at), TaskPriorityStats)
orchestrator.ScheduleTask(NewOneoffIndexSnapshotsTask(orchestrator, plan, at), TaskPriorityIndexSnapshots)
orchestrator.ScheduleTask(NewOneoffStatsTask(orchestrator, plan, op.SnapshotId, at), TaskPriorityStats)

return nil
}
4 changes: 2 additions & 2 deletions internal/orchestrator/taskforget.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type ForgetTask struct {

var _ Task = &ForgetTask{}

func NewOneofForgetTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *ForgetTask {
func NewOneoffForgetTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *ForgetTask {
return &ForgetTask{
TaskWithOperation: TaskWithOperation{
orch: orchestrator,
Expand Down Expand Up @@ -102,7 +102,7 @@ func (t *ForgetTask) Run(ctx context.Context) error {
}

if len(forgot) > 0 {
t.orch.ScheduleTask(NewOneofPruneTask(t.orch, t.plan, op.SnapshotId, time.Now(), false), TaskPriorityPrune)
t.orch.ScheduleTask(NewOneoffPruneTask(t.orch, t.plan, op.SnapshotId, time.Now(), false), TaskPriorityPrune)
}

return err
Expand Down
2 changes: 1 addition & 1 deletion internal/orchestrator/taskindexsnapshots.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type IndexSnapshotsTask struct {

var _ Task = &IndexSnapshotsTask{}

func NewOneofIndexSnapshotsTask(orchestrator *Orchestrator, plan *v1.Plan, at time.Time) *IndexSnapshotsTask {
func NewOneoffIndexSnapshotsTask(orchestrator *Orchestrator, plan *v1.Plan, at time.Time) *IndexSnapshotsTask {
return &IndexSnapshotsTask{
orchestrator: orchestrator,
plan: plan,
Expand Down
6 changes: 4 additions & 2 deletions internal/orchestrator/taskprune.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type PruneTask struct {

var _ Task = &PruneTask{}

func NewOneofPruneTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time, force bool) *PruneTask {
func NewOneoffPruneTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time, force bool) *PruneTask {
return &PruneTask{
TaskWithOperation: TaskWithOperation{
orch: orchestrator,
Expand Down Expand Up @@ -60,7 +60,7 @@ func (t *PruneTask) Next(now time.Time) *time.Time {

func (t *PruneTask) getNextPruneTime(repo *RepoOrchestrator, policy *v1.PrunePolicy) (time.Time, error) {
var lastPruneTime time.Time
t.orch.OpLog.ForEachByRepo(t.plan.Repo, indexutil.CollectLastN(1000), func(op *v1.Operation) error {
t.orch.OpLog.ForEachByRepo(t.plan.Repo, indexutil.CollectLastN(100), func(op *v1.Operation) error {
if _, ok := op.Op.(*v1.Operation_OperationPrune); ok {
lastPruneTime = time.Unix(0, op.UnixTimeStartMs*int64(time.Millisecond))
}
Expand Down Expand Up @@ -145,6 +145,8 @@ func (t *PruneTask) Run(ctx context.Context) error {
},
}

// Schedule a task to update persisted stats for the repo

return nil
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/orchestrator/taskrestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type RestoreTask struct {

var _ Task = &RestoreTask{}

func NewOneofRestoreTask(orchestrator *Orchestrator, opts RestoreTaskOpts, at time.Time) *RestoreTask {
func NewOneoffRestoreTask(orchestrator *Orchestrator, opts RestoreTaskOpts, at time.Time) *RestoreTask {
return &RestoreTask{
TaskWithOperation: TaskWithOperation{
orch: orchestrator,
Expand Down
2 changes: 1 addition & 1 deletion internal/orchestrator/taskstats.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type StatsTask struct {

var _ Task = &ForgetTask{}

func NewOneofStatsTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *StatsTask {
func NewOneoffStatsTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *StatsTask {
return &StatsTask{
TaskWithOperation: TaskWithOperation{
orch: orchestrator,
Expand Down
16 changes: 10 additions & 6 deletions webui/src/components/OperationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,7 @@ export const OperationRow = ({

let body: React.ReactNode | undefined;

if (
operation.displayMessage &&
operation.status === OperationStatus.STATUS_ERROR
) {
body = <pre>{operation.displayMessage}</pre>;
} else if (operation.op.case === "operationBackup") {
if (operation.op.case === "operationBackup") {
const backupOp = operation.op.value;
body = (
<>
Expand Down Expand Up @@ -274,6 +269,15 @@ export const OperationRow = ({
);
}

if (operation.displayMessage) {
body = (
<>
<pre>{details.state}: {operation.displayMessage}</pre>
{body}
</>
);
}

return (
<List.Item>
<List.Item.Meta title={title} avatar={avatar} description={body} />
Expand Down
1 change: 0 additions & 1 deletion webui/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export const API_PREFIX = "/api";
export const MAX_OPERATION_HISTORY = 10000;
7 changes: 4 additions & 3 deletions webui/src/lib/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ export const formatTime = (time: number | string | Date) => {
const hours = d.getUTCHours() % 12 == 0 ? 12 : d.getUTCHours() % 12;
const minutes =
d.getUTCMinutes() < 10 ? "0" + d.getUTCMinutes() : d.getUTCMinutes();
return `${isoStr.substring(0, 10)} at ${hours}:${minutes} ${
d.getUTCHours() > 12 ? "PM" : "AM"
}`;
const seconds =
d.getUTCSeconds() < 10 ? "0" + d.getUTCSeconds() : d.getUTCSeconds();
return `${isoStr.substring(0, 10)} at ${hours}:${minutes}:${seconds} ${d.getUTCHours() > 12 ? "PM" : "AM"
}`;
};

export const localISOTime = (time: number | string | Date) => {
Expand Down
12 changes: 10 additions & 2 deletions webui/src/state/oplog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,16 @@ export class BackupInfoCollector {
// use the latest status that is not cancelled.
let statusIdx = operations.length - 1;
let status = OperationStatus.STATUS_SYSTEM_CANCELLED;
while (statusIdx !== -1 && (shouldHideStatus(status) || status === OperationStatus.STATUS_PENDING)) {
status = operations[statusIdx].status!;
while (statusIdx !== -1) {
const curStatus = operations[statusIdx].status;
if (
shouldHideStatus(status) ||
status === OperationStatus.STATUS_PENDING ||
curStatus === OperationStatus.STATUS_ERROR ||
curStatus === OperationStatus.STATUS_WARNING
) {
status = operations[statusIdx].status;
}
statusIdx--;
}

Expand Down
45 changes: 25 additions & 20 deletions webui/src/views/RepoView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import { Repo } from "../../gen/ts/v1/config_pb";
import { Col, Empty, Flex, Row, Tabs, Typography } from "antd";
import { Col, Empty, Flex, Row, Spin, Tabs, Typography } from "antd";
import { useRecoilValue } from "recoil";
import { configState } from "../state/config";
import { useAlertApi } from "../components/Alerts";
Expand All @@ -14,9 +14,11 @@ import { formatBytes } from "../lib/formatting";

export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => {
const alertsApi = useAlertApi()!;
const [loading, setLoading] = useState(true);
const [stats, setStats] = useState<RepoStats | null>(null);

useEffect(() => {
setLoading(true);
setStats(null);
getOperations(new GetOperationsRequest({ repoId: repo.id!, lastN: BigInt(10) })).then((operations) => {
for (const op of operations) {
Expand All @@ -29,10 +31,11 @@ export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => {
}
}).catch((e) => {
console.error(e);
}).finally(() => {
setLoading(false);
});
}, [repo.id]);


// Gracefully handle deletions by checking if the plan is still in the config.
const config = useRecoilValue(configState);
let repoInConfig = config.repos?.find((p) => p.id === repo.id);
Expand All @@ -41,8 +44,8 @@ export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => {
}
repo = repoInConfig;

if (!stats) {
return <Empty description="No stats available. Run a backup." />;
if (loading) {
return <Spin />;
}

const items = [
Expand All @@ -52,22 +55,24 @@ export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => {
children: (
<>
<h3>Repo Stats</h3>
<Row>
<Col style={{ paddingRight: "20px" }}>
<p><strong>Total Size: </strong></p>
<p><strong>Total Size Uncompressed: </strong></p>
<p><strong>Blob Count: </strong></p>
<p><strong>Snapshot Count: </strong></p>
<p><strong>Compression Ratio: </strong></p>
</Col>
<Col>
<p>{formatBytes(Number(stats.totalSize))}</p>
<p>{formatBytes(Number(stats.totalUncompressedSize))}</p>
<p>{Number(stats.totalBlobCount)} blobs</p>
<p>{Number(stats.snapshotCount)} snapshots</p>
<p>{Math.round(stats.compressionRatio * 1000) / 1000}</p>
</Col>
</Row>
{stats === null ? <Empty description="No data. Have you run a backup yet?" /> :
<Row>
<Col style={{ paddingRight: "20px" }}>
<p><strong>Total Size: </strong></p>
<p><strong>Total Size Uncompressed: </strong></p>
<p><strong>Blob Count: </strong></p>
<p><strong>Snapshot Count: </strong></p>
<p><strong>Compression Ratio: </strong></p>
</Col>
<Col>
<p>{formatBytes(Number(stats.totalSize))}</p>
<p>{formatBytes(Number(stats.totalUncompressedSize))}</p>
<p>{Number(stats.totalBlobCount)} blobs</p>
<p>{Number(stats.snapshotCount)} snapshots</p>
<p>{Math.round(stats.compressionRatio * 1000) / 1000}</p>
</Col>
</Row>
}
</>
),
},
Expand Down

0 comments on commit c5e435d

Please sign in to comment.