Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/components/fleet/event-log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ function formatTime(date: Date | string): string {
export function EventLog({ nodeId, range }: EventLogProps) {
const trpc = useTRPC();

const { data: events, isLoading } = useQuery({
const { data, isLoading } = useQuery({
...trpc.fleet.getStatusTimeline.queryOptions({ nodeId, range }),
refetchInterval: 15_000,
});
const events = data?.events;

if (isLoading) {
return (
Expand Down
8 changes: 5 additions & 3 deletions src/components/fleet/status-timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ function formatDuration(ms: number): string {
export function StatusTimeline({ nodeId, range, onRangeChange }: StatusTimelineProps) {
const trpc = useTRPC();

const { data: events, isLoading, dataUpdatedAt } = useQuery({
const { data, isLoading, dataUpdatedAt } = useQuery({
...trpc.fleet.getStatusTimeline.queryOptions({ nodeId, range }),
refetchInterval: 15_000,
});
const events = data?.events;
const nodeStatus = data?.nodeStatus ?? "UNKNOWN";

type Segment = {
status: string;
Expand All @@ -82,7 +84,7 @@ export function StatusTimeline({ nodeId, range, onRangeChange }: StatusTimelineP

if (events !== undefined && now > 0) {
if (events.length === 0) {
segs.push({ status: "UNKNOWN", start: rangeStart, end: now });
segs.push({ status: nodeStatus, start: rangeStart, end: now });
} else {
// First segment: from range start to first event
const firstStatus = events[0].fromStatus ?? "UNKNOWN";
Expand All @@ -98,7 +100,7 @@ export function StatusTimeline({ nodeId, range, onRangeChange }: StatusTimelineP
}

return { segments: segs, totalMs: now - rangeStart };
}, [events, range, dataUpdatedAt]);
}, [events, nodeStatus, range, dataUpdatedAt]);

return (
<div className="space-y-2">
Expand Down
34 changes: 24 additions & 10 deletions src/server/routers/fleet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,17 @@ export const fleetRouter = router({
"30d": 30 * 24 * 60 * 60 * 1000,
};
const since = new Date(Date.now() - rangeMs[input.range]);
return prisma.nodeStatusEvent.findMany({
where: { nodeId: input.nodeId, timestamp: { gte: since } },
orderBy: { timestamp: "asc" },
});
const [events, node] = await Promise.all([
prisma.nodeStatusEvent.findMany({
where: { nodeId: input.nodeId, timestamp: { gte: since } },
orderBy: { timestamp: "asc" },
}),
prisma.vectorNode.findUnique({
where: { id: input.nodeId },
select: { status: true },
}),
]);
return { events, nodeStatus: node?.status ?? "UNKNOWN" };
}),

getUptime: protectedProcedure
Expand All @@ -100,16 +107,23 @@ export const fleetRouter = router({
orderBy: { timestamp: "asc" },
});

// Get the last event before the range to know starting status
const priorEvent = await prisma.nodeStatusEvent.findFirst({
where: { nodeId: input.nodeId, timestamp: { lt: since } },
orderBy: { timestamp: "desc" },
});
// Get the last event before the range to know starting status,
// and the node's current status as a fallback for nodes with no event history
const [priorEvent, nodeForStatus] = await Promise.all([
prisma.nodeStatusEvent.findFirst({
where: { nodeId: input.nodeId, timestamp: { lt: since } },
orderBy: { timestamp: "desc" },
}),
prisma.vectorNode.findUnique({
where: { id: input.nodeId },
select: { status: true },
}),
]);

// Walk events, tracking time in HEALTHY status
let healthySeconds = 0;
let incidents = 0;
let currentStatus = priorEvent?.toStatus ?? "UNKNOWN";
let currentStatus = priorEvent?.toStatus ?? nodeForStatus?.status ?? "UNKNOWN";
let cursor = since.getTime();

for (const event of events) {
Expand Down
Loading