Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ export function ActorsProvider({

const metrics = atom({
metrics: { cpu: null, memory: null } as Metrics,
updatedAt: Date.now(),
status: "pending",
});
metrics.onMount = (set) => {
Expand Down Expand Up @@ -293,6 +294,7 @@ export function ActorsProvider({
...prev,
...data,
status: query.status,
updatedAt: Date.now(),
}));
}

Expand Down
8 changes: 6 additions & 2 deletions frontend/apps/hub/src/queries/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { ls } from "@/lib/ls";
import { isRivetError } from "@/lib/utils";
import { RivetClient } from "@rivet-gg/api-full";
import { RivetClient as RivetEeClient } from "@rivet-gg/api-ee";
import { type APIResponse, type Fetcher, fetcher } from "@rivet-gg/api/core";
import {
type APIResponse,
type Fetcher,
fetcher,
} from "@rivet-gg/api-full/core";
import { getConfig, timing, toast } from "@rivet-gg/components";
import { broadcastQueryClient } from "@tanstack/query-broadcast-client-experimental";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
Expand Down Expand Up @@ -133,7 +137,7 @@ const clientOptions: RivetClient.Options = {
...args,
withCredentials: true,
maxRetries: 0,
timeoutMs: 30_000 // 30 seconds
timeoutMs: 30_000, // 30 seconds
});

return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function Actor() {
ActorFeature.Config,
ActorFeature.Logs,
ActorFeature.State,
ActorFeature.Metrics,
ActorFeature.Connections,
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ function Actor() {
if (!actor) {
return (
<ActorNotFound
features={[ActorFeature.Config, ActorFeature.Logs]}
features={[
ActorFeature.Config,
ActorFeature.Logs,
ActorFeature.Metrics,
]}
/>
);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/hub/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ export default defineConfig({
// Listen on a different port since we don't proxy WebSockets on /ui
hmr: {
port: 5080,
host: "127.0.0.1"
}
host: "127.0.0.1",
},
},
preview: {
port: 5080,
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/components/src/actors/actor-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export type LogsAtom = Atom<{
}>;
export type MetricsAtom = Atom<{
metrics: Metrics;
updatedAt: number;
// query status
status: string;
}>;
Expand Down
106 changes: 106 additions & 0 deletions frontend/packages/components/src/actors/actor-cpu-stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { format } from "date-fns";
import { useId } from "react";
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "../ui/chart";
import { timing } from "../lib/timing";

interface ActorCpuStatsProps {
interval?: number;
cpu: number[];
metricsAt: number;
syncId?: string;
}

const chartConfig = {
value: {
color: "hsl(var(--chart-1))",
label: "CPU Usage",
},
} satisfies ChartConfig;

export function ActorCpuStats({
interval = 15,
cpu,
metricsAt,
syncId,
}: ActorCpuStatsProps) {
const data = cpu.map((value, i) => ({
x: `${(cpu.length - i) * -interval}`,
value: value / 100,
config: {
label: new Date(
metricsAt - (cpu.length - i) * timing.seconds(interval),
),
},
}));

const id = useId();

const fillId = `fill-${id}`;
return (
<ChartContainer config={chartConfig} className="-ml-6">
<AreaChart accessibilityLayer data={data} syncId={syncId}>
<CartesianGrid vertical={true} />
<XAxis
interval="preserveStartEnd"
dataKey="x"
hide
axisLine={false}
domain={[0, 60]}
tickCount={60}
/>
<YAxis
dataKey="value"
axisLine={false}
domain={[0, 1]}
tickFormatter={(value) => `${value * 100}%`}
/>
<ChartTooltip
content={
<ChartTooltipContent
hideIndicator
labelKey="label"
labelFormatter={(label) => {
return format(label, "HH:mm:ss");
}}
valueFormatter={(value) => {
if (typeof value !== "number") {
return "n/a";
}
return `${(value * 100).toFixed(2)}%`;
}}
/>
}
/>
<defs>
<linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor="var(--color-value)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--color-value)"
stopOpacity={0.1}
/>
</linearGradient>
</defs>
<Area
isAnimationActive={false}
dataKey="value"
type="linear"
fill={`url(#${fillId})`}
fillOpacity={0.4}
stroke="var(--color-value)"
stackId="a"
/>
</AreaChart>
</ChartContainer>
);
}
114 changes: 114 additions & 0 deletions frontend/packages/components/src/actors/actor-memory-stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { format } from "date-fns";
import { filesize } from "filesize";
import { useId } from "react";
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "../ui/chart";
import { timing } from "../lib/timing";

interface ActorMemoryStatsProps {
metricsAt: number;
memory: number[];
allocatedMemory?: number;
syncId?: string;
interval?: number;
}

const chartConfig = {
value: {
color: "hsl(var(--chart-1))",
label: "Memory Usage",
},
} satisfies ChartConfig;

export function ActorMemoryStats({
interval = 15,
memory,
allocatedMemory,
metricsAt,
syncId,
}: ActorMemoryStatsProps) {
const data = memory.map((value, i) => ({
x: `${(memory.length - i) * -interval}`,
value,
config: {
label: new Date(
metricsAt - (memory.length - i) * timing.seconds(interval),
),
},
}));

const max = allocatedMemory || Math.max(...memory);

const id = useId();

const fillId = `fill-${id}`;
return (
<ChartContainer config={chartConfig} className="-ml-6">
<AreaChart accessibilityLayer data={data} syncId={syncId}>
<CartesianGrid vertical={true} />
<XAxis
interval="preserveStartEnd"
dataKey="x"
hide
axisLine={false}
domain={[0, 60]}
tickCount={60}
includeHidden
/>
<YAxis
dataKey="value"
axisLine={false}
domain={[0, max]}
tickFormatter={(value) =>
`${Math.ceil((value / max) * 100)}%`
}
/>
<ChartTooltip
content={
<ChartTooltipContent
hideIndicator
labelKey="label"
labelFormatter={(label) => {
return format(label, "HH:mm:ss");
}}
valueFormatter={(value) => {
if (typeof value !== "number") {
return "n/a";
}
return `${filesize(value)} (${Math.round((value / max) * 100).toFixed(2)}%)`;
}}
/>
}
/>
<defs>
<linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor="var(--color-value)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--color-value)"
stopOpacity={0.1}
/>
</linearGradient>
</defs>
<Area
isAnimationActive={false}
dataKey="value"
type="linear"
fill={`url(#${fillId})`}
fillOpacity={0.4}
stroke="var(--color-value)"
stackId="a"
/>
</AreaChart>
</ChartContainer>
);
}
Loading
Loading