mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-05-29 16:00:57 +00:00
improve peer state handling
This commit is contained in:
+21
-15
@@ -1,10 +1,6 @@
|
||||
import { Operation, OperationEvent, OperationEventType, OperationStatus } from "../../gen/ts/v1/operations_pb";
|
||||
import { GetOperationsRequest, GetOperationsRequestSchema, OpSelector } from "../../gen/ts/v1/service_pb";
|
||||
import { getOperations, subscribeToOperations, unsubscribeFromOperations } from "./oplog";
|
||||
import {
|
||||
STATS_OPERATION_HISTORY,
|
||||
STATUS_OPERATION_HISTORY,
|
||||
} from "../constants";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
|
||||
type Subscriber = (ids: bigint[], flowIDs: bigint[], event: OperationEventType) => void;
|
||||
@@ -90,7 +86,6 @@ export const getStatusForSelector = async (sel: OpSelector) => {
|
||||
return await getStatus(req);
|
||||
};
|
||||
|
||||
|
||||
export class OplogState {
|
||||
private byID: Map<bigint, Operation> = new Map();
|
||||
private byFlowID: Map<bigint, Operation[]> = new Map();
|
||||
@@ -220,15 +215,26 @@ export class OplogState {
|
||||
}
|
||||
|
||||
|
||||
export const matchSelector = (selector: OpSelector, op: Operation) => {
|
||||
if (selector.planId && selector.planId !== op.planId) {
|
||||
return false;
|
||||
}
|
||||
if (selector.repoGuid && selector.repoGuid !== op.repoGuid) {
|
||||
return false;
|
||||
}
|
||||
if (selector.flowId && selector.flowId !== op.flowId) {
|
||||
return false;
|
||||
|
||||
// Defining matchers for each field in OpSelector to determine if an operation matches the selector.
|
||||
// Type system asserts that a check must exist for each field in OpSelector.
|
||||
const selectorFieldMatchers: { [K in keyof OpSelector]: (op: Operation, sel: OpSelector) => boolean } = {
|
||||
planId: (op, sel) => op.planId === sel.planId,
|
||||
repoGuid: (op, sel) => op.repoGuid === sel.repoGuid,
|
||||
flowId: (op, sel) => op.flowId === sel.flowId,
|
||||
instanceId: (op, sel) => op.instanceId === sel.instanceId,
|
||||
snapshotId: (op, sel) => op.snapshotId === sel.snapshotId,
|
||||
originalInstanceKeyid: (op, sel) => op.originalInstanceKeyid === sel.originalInstanceKeyid,
|
||||
ids: (op: Operation, sel: OpSelector) => sel.ids.includes(op.id),
|
||||
["$typeName"]: (op: Operation, sel: OpSelector): boolean => true, // $typeName is a proto property that isn't used for matching
|
||||
};
|
||||
|
||||
export const matchSelector = (selector: OpSelector, op: Operation): boolean => {
|
||||
for (const key in selector) {
|
||||
const matcher = selectorFieldMatchers[key as keyof OpSelector];
|
||||
if (matcher && !matcher(op, selector)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import { EmptySchema } from "../../gen/ts/types/value_pb";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import _ from "lodash";
|
||||
import { backrestService } from "../api";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const subscribers: ((event?: OperationEvent, err?: Error) => void)[] = [];
|
||||
|
||||
@@ -57,7 +58,6 @@ export const unsubscribeFromOperations = (
|
||||
console.log("unsubscribed from operations, subscriber count: ", subscribers.length);
|
||||
};
|
||||
|
||||
|
||||
export const shouldHideOperation = (operation: Operation) => {
|
||||
return (
|
||||
operation.op.case === "operationStats" ||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { PeerState } from "../../gen/ts/v1/syncservice_pb";
|
||||
import { syncStateService } from "../api";
|
||||
|
||||
@@ -43,19 +44,20 @@ const subscribeToSyncStates = async (
|
||||
let peerStates: Map<string, PeerState> = new Map();
|
||||
const subscribers: Set<(peerStates: PeerState[]) => void> = new Set();
|
||||
|
||||
export const subscribeToPeerStates = (
|
||||
const subscribeToPeerStates = (
|
||||
callback: (peerStates: PeerState[]) => void,
|
||||
): void => {
|
||||
subscribers.add(callback);
|
||||
callback(Array.from(peerStates.values()));
|
||||
};
|
||||
|
||||
export const unsubscribeFromPeerStates = (
|
||||
const unsubscribeFromPeerStates = (
|
||||
callback: (peerStates: PeerState[]) => void,
|
||||
): void => {
|
||||
subscribers.delete(callback);
|
||||
};
|
||||
|
||||
|
||||
(async () => {
|
||||
const abortController = new AbortController(); // never aborts at the moment.
|
||||
subscribeToSyncStates(() => {
|
||||
@@ -73,3 +75,23 @@ export const unsubscribeFromPeerStates = (
|
||||
}
|
||||
}, abortController);
|
||||
})();
|
||||
|
||||
export const useSyncStates = (): PeerState[] => {
|
||||
const [syncStates, setSyncStates] = useState<PeerState[]>(() =>
|
||||
Array.from(peerStates.values())
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStateUpdate = (states: PeerState[]) => {
|
||||
setSyncStates(states);
|
||||
};
|
||||
|
||||
subscribeToPeerStates(handleStateUpdate);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromPeerStates(handleStateUpdate);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return syncStates;
|
||||
};
|
||||
|
||||
+3
-28
@@ -35,10 +35,7 @@ import { Route, Routes, useNavigate, useParams } from "react-router-dom";
|
||||
import { MainContentAreaTemplate } from "./MainContentArea";
|
||||
import { create } from "@bufbuild/protobuf";
|
||||
import { PeerState } from "../../gen/ts/v1/syncservice_pb";
|
||||
import {
|
||||
subscribeToPeerStates,
|
||||
unsubscribeFromPeerStates,
|
||||
} from "../state/peerstates";
|
||||
import { useSyncStates } from "../state/peerstates";
|
||||
const { Header, Sider } = Layout;
|
||||
|
||||
const SummaryDashboard = React.lazy(() =>
|
||||
@@ -97,18 +94,7 @@ const RepoViewContainer = () => {
|
||||
|
||||
const RemoteRepoViewContainer = () => {
|
||||
const { peerInstanceId, repoId } = useParams();
|
||||
const [peerStates, setPeerStates] = useState<PeerState[]>([]);
|
||||
|
||||
// subscribe to peer states
|
||||
useEffect(() => {
|
||||
const cb = (states: PeerState[]) => {
|
||||
setPeerStates(states);
|
||||
};
|
||||
subscribeToPeerStates(cb);
|
||||
return () => {
|
||||
unsubscribeFromPeerStates(cb);
|
||||
};
|
||||
}, []);
|
||||
const peerStates = useSyncStates();
|
||||
|
||||
// Peer state is used to find the right repo
|
||||
const peerState = peerStates.find(
|
||||
@@ -171,18 +157,7 @@ export const App: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [config, setConfig] = useConfig();
|
||||
|
||||
const [peerStates, setPeerStates] = useState<PeerState[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!config || !config.multihost) return;
|
||||
const cb = (states: PeerState[]) => {
|
||||
setPeerStates(states);
|
||||
};
|
||||
subscribeToPeerStates(cb);
|
||||
return () => {
|
||||
unsubscribeFromPeerStates(cb);
|
||||
};
|
||||
}, [config]);
|
||||
const peerStates = useSyncStates();
|
||||
|
||||
const items = getSidenavItems(config, peerStates);
|
||||
|
||||
|
||||
@@ -30,10 +30,7 @@ import {
|
||||
Multihost_Permission_Type,
|
||||
} from "../../gen/ts/v1/config_pb";
|
||||
import { PeerState } from "../../gen/ts/v1/syncservice_pb";
|
||||
import {
|
||||
subscribeToPeerStates,
|
||||
unsubscribeFromPeerStates,
|
||||
} from "../state/peerstates";
|
||||
import { useSyncStates } from "../state/peerstates";
|
||||
import { PeerStateConnectionStatusIcon } from "../components/SyncStateIcon";
|
||||
|
||||
interface FormData {
|
||||
@@ -76,7 +73,7 @@ export const SettingsModal = () => {
|
||||
const showModal = useShowModal();
|
||||
const alertsApi = useAlertApi()!;
|
||||
const [form] = Form.useForm<FormData>();
|
||||
const [peerStates, setPeerStates] = useState<PeerState[]>([]);
|
||||
const peerStates = useSyncStates();
|
||||
const [reloadOnCancel, setReloadOnCancel] = useState(false);
|
||||
const [formEdited, setFormEdited] = useState(false);
|
||||
|
||||
@@ -84,16 +81,6 @@ export const SettingsModal = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const cb = (syncStates: PeerState[]) => {
|
||||
setPeerStates(syncStates);
|
||||
};
|
||||
subscribeToPeerStates(cb);
|
||||
return () => {
|
||||
unsubscribeFromPeerStates(cb);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
// Validate form
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
Spin,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import { useConfig } from "../components/ConfigProvider";
|
||||
import {
|
||||
SummaryDashboardResponse,
|
||||
@@ -42,10 +42,7 @@ import { isMobile } from "../lib/browserutil";
|
||||
import { useNavigate } from "react-router";
|
||||
import { toJsonString } from "@bufbuild/protobuf";
|
||||
import { ConfigSchema, Multihost } from "../../gen/ts/v1/config_pb";
|
||||
import {
|
||||
subscribeToPeerStates,
|
||||
unsubscribeFromPeerStates,
|
||||
} from "../state/peerstates";
|
||||
import { useSyncStates } from "../state/peerstates";
|
||||
import { PeerState } from "../../gen/ts/v1/syncservice_pb";
|
||||
import { PeerStateConnectionStatusIcon } from "../components/SyncStateIcon";
|
||||
import { last } from "lodash";
|
||||
@@ -322,25 +319,14 @@ const MultihostSummary = ({
|
||||
}: {
|
||||
multihostConfig: Multihost | null;
|
||||
}) => {
|
||||
const [peerStates, setPeerStates] = useState<Map<string, PeerState>>(
|
||||
new Map()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const cb = (syncStates: PeerState[]) => {
|
||||
setPeerStates((prev) => {
|
||||
const updated = new Map(prev);
|
||||
for (const state of syncStates) {
|
||||
updated.set(state.peerKeyid, state);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
subscribeToPeerStates(cb);
|
||||
return () => {
|
||||
unsubscribeFromPeerStates(cb);
|
||||
};
|
||||
}, []);
|
||||
const allPeerStates = useSyncStates();
|
||||
const peerStates = useMemo(() => {
|
||||
const map = new Map<string, PeerState>();
|
||||
for (const state of allPeerStates) {
|
||||
map.set(state.peerKeyid, state);
|
||||
}
|
||||
return map;
|
||||
}, [allPeerStates]);
|
||||
|
||||
const knownHostTiles: JSX.Element[] = [];
|
||||
for (const cfgPeer of multihostConfig?.knownHosts || []) {
|
||||
|
||||
Reference in New Issue
Block a user