Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Add cluster stats tab to explorer #11325

Merged
merged 1 commit into from
Aug 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 262 additions & 14 deletions explorer/package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"@types/socket.io-client": "^1.4.33",
"bootstrap": "^4.5.0",
"bs58": "^4.0.1",
"humanize-duration-ts": "^2.1.1",
"node-sass": "^4.14.1",
"prettier": "^2.0.5",
"react": "^16.13.1",
"react-app-rewired": "^2.1.6",
"react-countup": "^4.3.3",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"socket.io-client": "^2.3.0",
"solana-sdk-wasm": "file:wasm/pkg",
"superstruct": "^0.10.12",
"typescript": "^3.9.7",
"wasm-loader": "^1.3.0"
},
Expand Down
11 changes: 6 additions & 5 deletions explorer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ACCOUNT_ALIASES, ACCOUNT_ALIASES_PLURAL } from "./providers/accounts";
import TabbedPage from "components/TabbedPage";
import TopAccountsCard from "components/TopAccountsCard";
import SupplyCard from "components/SupplyCard";
import StatsCard from "components/StatsCard";
import { pickCluster } from "utils/url";
import Banner from "components/Banner";

Expand Down Expand Up @@ -84,11 +85,11 @@ function App() {
<AccountsCard />
</TabbedPage>
</Route>
<Route
render={({ location }) => (
<Redirect to={{ ...location, pathname: "/transactions" }} />
)}
></Route>
<Route>
<TabbedPage tab="Stats">
<StatsCard />
</TabbedPage>
</Route>
</Switch>
</div>
</>
Expand Down
131 changes: 131 additions & 0 deletions explorer/src/components/StatsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from "react";
import CountUp from "react-countup";

import TableCardBody from "./common/TableCardBody";
import {
useDashboardInfo,
usePerformanceInfo,
useRootSlot,
PERF_UPDATE_SEC,
useSetActive,
} from "providers/stats/solanaBeach";
import { slotsToHumanString } from "utils";
import { useCluster, Cluster } from "providers/cluster";

export default function StatsCard() {
return (
<div className="card">
<div className="card-header">
<div className="row align-items-center">
<div className="col">
<h4 className="card-header-title">Live Cluster Info</h4>
</div>
</div>
</div>
<StatsCardBody />
</div>
);
}

function StatsCardBody() {
const rootSlot = useRootSlot();
const dashboardInfo = useDashboardInfo();
const performanceInfo = usePerformanceInfo();
const txTrackerRef = React.useRef({ old: 0, new: 0 });
const txTracker = txTrackerRef.current;
const setSocketActive = useSetActive();
const { cluster } = useCluster();

React.useEffect(() => {
setSocketActive(true);
return () => setSocketActive(false);
}, [setSocketActive, cluster]);

const statsAvailable =
cluster === Cluster.MainnetBeta || cluster === Cluster.Testnet;
if (!statsAvailable) {
return (
<div className="card-body text-center">
<div className="text-muted">
Stats are not available for this cluster
</div>
</div>
);
}

if (performanceInfo) {
const { totalTransactionCount: txCount, avgTPS } = performanceInfo;

// Track last tx count to initialize count up
if (txCount !== txTracker.new) {
// If this is the first tx count value, estimate the previous one
// in order to have a starting point for our animation
txTracker.old = txTracker.new || txCount - PERF_UPDATE_SEC * avgTPS;
txTracker.new = txCount;
}
} else {
txTrackerRef.current = { old: 0, new: 0 };
}

if (rootSlot === undefined || !dashboardInfo || !performanceInfo) {
return (
<div className="card-body text-center">
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</div>
);
}

const currentBlock = rootSlot.toLocaleString("en-US");
const { avgBlockTime_1min, epochInfo } = dashboardInfo;
const averageBlockTime = Math.round(1000 * avgBlockTime_1min) + "ms";
const { slotIndex, slotsInEpoch } = epochInfo;
const currentEpoch = epochInfo.epoch.toString();
const epochProgress = ((100 * slotIndex) / slotsInEpoch).toFixed(1) + "%";
const epochTimeRemaining = slotsToHumanString(slotsInEpoch - slotIndex);
const transactionCount = (
<CountUp
start={txTracker.old}
end={txTracker.new}
duration={PERF_UPDATE_SEC + 2}
delay={0}
useEasing={false}
preserveValue={true}
separator=","
/>
);
const averageTps = Math.round(performanceInfo.avgTPS);

return (
<TableCardBody>
<tr>
<td className="w-100">Block</td>
<td className="text-right text-monospace">{currentBlock}</td>
</tr>
<tr>
<td className="w-100">Block time</td>
<td className="text-right text-monospace">{averageBlockTime}</td>
</tr>
<tr>
<td className="w-100">Epoch</td>
<td className="text-right text-monospace">{currentEpoch} </td>
</tr>
<tr>
<td className="w-100">Epoch progress</td>
<td className="text-right text-monospace">{epochProgress} </td>
</tr>
<tr>
<td className="w-100">Epoch time remaining</td>
<td className="text-right text-monospace">{epochTimeRemaining} </td>
</tr>
<tr>
<td className="w-100">Transaction count</td>
<td className="text-right text-monospace">{transactionCount} </td>
</tr>
<tr>
<td className="w-100">Transactions per second</td>
<td className="text-right text-monospace">{averageTps} </td>
</tr>
</TableCardBody>
);
}
5 changes: 4 additions & 1 deletion explorer/src/components/TabbedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useClusterModal } from "providers/cluster";
import ClusterStatusButton from "components/ClusterStatusButton";
import { pickCluster } from "utils/url";

export type Tab = "Transactions" | "Accounts" | "Supply";
export type Tab = "Transactions" | "Accounts" | "Supply" | "Stats";

type Props = { children: React.ReactNode; tab: Tab };
export default function TabbedPage({ children, tab }: Props) {
Expand All @@ -22,6 +22,9 @@ export default function TabbedPage({ children, tab }: Props) {
<div className="row align-items-center">
<div className="col">
<ul className="nav nav-tabs nav-overflow header-tabs">
<li className="nav-item">
<NavLink href="/" tab="Stats" current={tab} />
</li>
<li className="nav-item">
<NavLink
href="/transactions"
Expand Down
21 changes: 12 additions & 9 deletions explorer/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ import { RichListProvider } from "./providers/richList";
import { SupplyProvider } from "./providers/supply";
import { TransactionsProvider } from "./providers/transactions";
import { AccountsProvider } from "./providers/accounts";
import { StatsProvider } from "providers/stats";

ReactDOM.render(
<Router>
<ClusterProvider>
<SupplyProvider>
<RichListProvider>
<AccountsProvider>
<TransactionsProvider>
<App />
</TransactionsProvider>
</AccountsProvider>
</RichListProvider>
</SupplyProvider>
<StatsProvider>
<SupplyProvider>
<RichListProvider>
<AccountsProvider>
<TransactionsProvider>
<App />
</TransactionsProvider>
</AccountsProvider>
</RichListProvider>
</SupplyProvider>
</StatsProvider>
</ClusterProvider>
</Router>,
document.getElementById("root")
Expand Down
7 changes: 7 additions & 0 deletions explorer/src/providers/stats/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";
import { SolanaBeachProvider } from "./solanaBeach";

type Props = { children: React.ReactNode };
export function StatsProvider({ children }: Props) {
return <SolanaBeachProvider>{children}</SolanaBeachProvider>;
}
Loading