Skip to content
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
19 changes: 10 additions & 9 deletions apps/dashboard/src/@/api/usage/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,20 @@ export const fetchRPCUsage = unstable_cache(
type Last24HoursRPCUsageApiResponse = {
peakRate: {
date: string;
peakRPS: number;
peakRPS: string;
};
averageRate: {
date: string;
averageRate: number;
includedCount: number;
rateLimitedCount: number;
overageCount: number;
averageRate: string;
peakRPS: string;
includedCount: string;
rateLimitedCount: string;
overageCount: string;
}[];
totalCounts: {
includedCount: number;
rateLimitedCount: number;
overageCount: number;
includedCount: string;
rateLimitedCount: string;
overageCount: string;
};
};

Expand Down Expand Up @@ -106,7 +107,7 @@ export const getLast24HoursRPCUsage = unstable_cache(
data: resData.data as Last24HoursRPCUsageApiResponse,
};
},
["rpc-usage-last-24-hours"],
["rpc-usage-last-24-hours:v2"],
{
revalidate: 60, // 1 minute
},
Expand Down
63 changes: 26 additions & 37 deletions apps/dashboard/src/@/components/blocks/charts/area-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
Expand All @@ -31,18 +30,19 @@ type ThirdwebAreaChartProps<TConfig extends ChartConfig> = {
description?: string;
titleClassName?: string;
};
footer?: React.ReactNode;
customHeader?: React.ReactNode;
// chart config
config: TConfig;
data: Array<Record<keyof TConfig, number> & { time: number | string | Date }>;
showLegend?: boolean;
maxLimit?: number;

yAxis?: boolean;
xAxis?: {
sameDay?: boolean;
};

variant?: "stacked" | "individual";

// chart className
chartClassName?: string;
isPending: boolean;
Expand Down Expand Up @@ -77,17 +77,7 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
) : props.data.length === 0 ? (
<EmptyChartState />
) : (
<AreaChart
accessibilityLayer
data={
props.maxLimit
? props.data.map((d) => ({
...d,
maxLimit: props.maxLimit,
}))
: props.data
}
>
<AreaChart accessibilityLayer data={props.data}>
<CartesianGrid vertical={false} />
{props.yAxis && <YAxis tickLine={false} axisLine={false} />}
<XAxis
Expand Down Expand Up @@ -136,26 +126,28 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
</linearGradient>
))}
</defs>
{configKeys.map((key) => (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId="a"
/>
))}
{props.maxLimit && (
<Area
type="monotone"
dataKey="maxLimit"
stroke="#ef4444"
strokeWidth={2}
strokeDasharray="5 5"
fill="none"
/>
{configKeys.map((key) =>
key === "maxLine" ? (
<Area
key={key}
type="monotone"
dataKey="maxLine"
stroke="#ef4444"
strokeWidth={2}
strokeDasharray="5 5"
fill="none"
/>
) : (
<Area
key={key}
dataKey={key}
type="natural"
fill={`url(#fill_${key})`}
fillOpacity={0.4}
stroke={`var(--color-${key})`}
stackId={props.variant !== "stacked" ? undefined : "a"}
/>
),
)}

{props.showLegend && (
Expand All @@ -167,9 +159,6 @@ export function ThirdwebAreaChart<TConfig extends ChartConfig>(
)}
</ChartContainer>
</CardContent>
{props.footer && (
<CardFooter className="w-full">{props.footer}</CardFooter>
)}
</Card>
);
}
4 changes: 1 addition & 3 deletions apps/dashboard/src/@/components/ui/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,7 @@ const ChartTooltipContent = React.forwardRef<
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{item.name === "maxLimit"
? "Upper Limit"
: itemConfig?.label || item.name}
{itemConfig?.label || item.name}
</span>
</div>
{item.value !== undefined && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ export function CountGraph(props: {
currentRateLimit: number;
data: {
date: string;
includedCount: number;
overageCount: number;
rateLimitedCount: number;
includedCount: string;
overageCount: string;
rateLimitedCount: string;
}[];
}) {
const hasAnyRateLimited = props.data.some((v) => v.rateLimitedCount > 0);
const hasAnyRateLimited = props.data.some(
(v) => Number(v.rateLimitedCount) > 0,
);
return (
<ThirdwebAreaChart
chartClassName="aspect-[1.5] lg:aspect-[4]"
header={{
title: "Requests Over Time",
description: "Requests over the last 24 hours. All times in UTC.",
description: "Requests over the last 24 hours.",
}}
config={
hasAnyRateLimited
Expand Down Expand Up @@ -47,13 +49,13 @@ export function CountGraph(props: {
}}
hideLabel={false}
toolTipLabelFormatter={(label) => {
return formatDate(new Date(label), "MMM dd, HH:mm");
return formatDate(label, "MMM dd, HH:mm");
}}
// @ts-expect-error - sending MORE data than expected is ok
data={props.data
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
.map((v) => ({
time: v.date,
time: `${v.date}Z`,
includedCount: Number(v.includedCount) + Number(v.overageCount),
rateLimitedCount: Number(v.rateLimitedCount),
}))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,63 @@

import { ThirdwebAreaChart } from "@/components/blocks/charts/area-chart";
import { formatDate } from "date-fns";
import { InfoIcon } from "lucide-react";

export function RateGraph(props: {
peakPercentage: number;
currentRateLimit: number;
data: { date: string; averageRate: number }[];
data: { date: string; averageRate: string; peakRPS: string }[];
}) {
return (
<ThirdwebAreaChart
chartClassName="aspect-[1.5] lg:aspect-[4]"
header={{
title: "Request Rate Over Time",
description: "Request rate over the last 24 hours. All times in UTC.",
description: "Request rate over the last 24 hours.",
}}
// only show the footer if the peak usage is greater than 80%
footer={
props.peakPercentage > 80 ? (
<div className="flex items-center justify-center gap-2">
<InfoIcon className="h-4 w-4 text-muted-foreground" />
<p className="text-muted-foreground text-xs">
The red dashed line represents your current plan rate limit (
{props.currentRateLimit} RPS)
</p>
</div>
) : undefined
config={
props.peakPercentage > 80
? {
averageRate: {
label: "Average RPS",
color: "hsl(var(--chart-1))",
},
peakRPS: {
label: "Peak RPS",
color: "hsl(var(--chart-2))",
},
maxLine: {
label: "Plan Rate Limit",
},
}
: {
averageRate: {
label: "Average RPS",
color: "hsl(var(--chart-1))",
},
peakRPS: {
label: "Peak RPS",
color: "hsl(var(--chart-2))",
},
}
}
config={{
averageRate: {
label: "Average RPS",
color: "hsl(var(--chart-1))",
},
}}
// @ts-expect-error - maxLine is always sent but not always rendered, this is OK
data={props.data
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
.map((v) => ({
time: v.date,
averageRate: Number(v.averageRate.toFixed(2)),
time: `${v.date}Z`,
averageRate: Number(Number(v.averageRate).toFixed(2)),
peakRPS: Number(v.peakRPS),
maxLine: props.currentRateLimit,
}))}
yAxis
xAxis={{
sameDay: true,
}}
showLegend
hideLabel={false}
toolTipLabelFormatter={(label) => {
return formatDate(new Date(label), "MMM dd, HH:mm");
return formatDate(label, "MMM dd, HH:mm");
}}
// only show the upper limit if the peak usage is greater than 80%
maxLimit={props.peakPercentage > 80 ? props.currentRateLimit : undefined}
isPending={false}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default async function RPCUsage(props: {
const { peakRate, totalCounts, averageRate } = apiData.data;

// Calculate percentage of limit for the peak
const peakPercentage = (peakRate.peakRPS / currentRateLimit) * 100;
const peakPercentage = (Number(peakRate.peakRPS) / currentRateLimit) * 100;

// Determine status based on peak percentage
const getStatusColor = (percentage: number) => {
Expand Down Expand Up @@ -98,7 +98,7 @@ export default async function RPCUsage(props: {
<CardContent>
<div className="flex items-center justify-between">
<div className="font-bold text-2xl">
{peakRate.peakRPS.toFixed(1)} RPS
{Number(peakRate.peakRPS)} RPS
</div>
<div className="flex items-center gap-2">
<div
Expand All @@ -114,7 +114,7 @@ export default async function RPCUsage(props: {
<p className="mt-1 text-muted-foreground text-xs">
<ClockIcon className="mr-1 inline h-3 w-3" />
{peakRate.date
? format(new Date(peakRate.date), "MMM d, HH:mm")
? format(new Date(`${peakRate.date}Z`), "MMM d, HH:mm")
: "No Requests in last 24 hours"}
</p>
</CardContent>
Expand Down Expand Up @@ -177,9 +177,9 @@ export default async function RPCUsage(props: {
<AlertTriangleIcon className="h-4 w-4" />
<AlertTitle>Rate Limit Exceeded</AlertTitle>
<AlertDescription>
Your peak usage of {peakRate.peakRPS.toFixed(1)} RPS has exceeded
your plan limit of {currentRateLimit} RPS. Consider upgrading your
plan to avoid rate limiting.
Your peak usage of {Number(peakRate.peakRPS)} RPS has exceeded your
plan limit of {currentRateLimit} RPS. Consider upgrading your plan
to avoid rate limiting.
</AlertDescription>
</Alert>
)}
Expand Down
Loading