mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
<img width="1575" height="1134" alt="image" src="https://github.com/user-attachments/assets/994b1113-717f-44a2-89a4-13bc0182db20" /> Attempts to improve our edge function overview pages to provide stronger insights into the health of a function, including reliability (error rates), performance (execution times) and usage (cpu and memory). As part of this work it refactors existing charts to use our new chart components. main consideration is the collective performance of error queries https://github.com/supabase/supabase/pull/44009/changes#diff-2a79cf61c5397a8ef363c333229fa7729a2efc90a4d8e0806e49c212d5aa97e7 ## To test: 1. Create an edge function that errors out randomly across requests. You can use cron to poll this function every second. 2. View the edge function and on the overview page confirm that errors are showing and grouped correctly in recent failed invocations sections. --------- Co-authored-by: Ali Waseem <waseema393@gmail.com>
157 lines
5.4 KiB
TypeScript
157 lines
5.4 KiB
TypeScript
import AlertError from 'components/ui/AlertError'
|
|
import type { ComponentProps } from 'react'
|
|
import { useMemo } from 'react'
|
|
import type { ChartIntervals } from 'types'
|
|
import { Button } from 'ui'
|
|
import { Chart, ChartActions, ChartLoadingState, ChartMetric } from 'ui-patterns/Chart'
|
|
import { PageContainer } from 'ui-patterns/PageContainer'
|
|
import {
|
|
PageSection,
|
|
PageSectionAside,
|
|
PageSectionContent,
|
|
PageSectionMeta,
|
|
PageSectionSummary,
|
|
} from 'ui-patterns/PageSection'
|
|
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
|
|
|
import { EdgeFunctionChartEmptyState } from './EdgeFunctionChartEmptyState'
|
|
import { EdgeFunctionInvocationsChart } from './EdgeFunctionInvocationsChart'
|
|
import {
|
|
EDGE_FUNCTION_CHART_INTERVALS,
|
|
formatRate,
|
|
getChartEmptyStateCopy,
|
|
getSegmentedButtonClassName,
|
|
} from './EdgeFunctionOverview.utils'
|
|
import { toAlertError } from './EdgeFunctionRecentErrors.utils'
|
|
import type { InvocationChartDatum, InvocationUpdateAnnotation } from './EdgeFunctionOverview.utils'
|
|
|
|
interface EdgeFunctionInvocationsSectionProps {
|
|
interval: string
|
|
onIntervalChange: (interval: string) => void
|
|
selectedInterval: ChartIntervals
|
|
actions?: ComponentProps<typeof ChartActions>['actions']
|
|
totalInvocationCount: number
|
|
totalErrorCount: number
|
|
totalWarningCount: number
|
|
isLoadingFunction: boolean
|
|
isErrorFunction: boolean
|
|
functionError?: unknown
|
|
isLoadingChart: boolean
|
|
isErrorChart: boolean
|
|
chartErrorMessage?: string
|
|
chartData: InvocationChartDatum[]
|
|
onChartClick: () => void
|
|
updateAnnotation?: InvocationUpdateAnnotation
|
|
}
|
|
|
|
export const EdgeFunctionInvocationsSection = ({
|
|
interval,
|
|
onIntervalChange,
|
|
selectedInterval,
|
|
actions,
|
|
totalInvocationCount,
|
|
totalErrorCount,
|
|
totalWarningCount,
|
|
isLoadingFunction,
|
|
isErrorFunction,
|
|
functionError,
|
|
isLoadingChart,
|
|
isErrorChart,
|
|
chartErrorMessage,
|
|
chartData,
|
|
onChartClick,
|
|
updateAnnotation,
|
|
}: EdgeFunctionInvocationsSectionProps) => {
|
|
const dateTimeFormat = selectedInterval.format ?? 'MMM D, h:mma'
|
|
const emptyStateCopy = useMemo(
|
|
() => getChartEmptyStateCopy('invocations', isErrorChart, chartErrorMessage),
|
|
[chartErrorMessage, isErrorChart]
|
|
)
|
|
|
|
return (
|
|
<PageSection className="bg-surface-100/50 border-b pb-10 pt-8">
|
|
<PageSectionContent>
|
|
<PageContainer size="full">
|
|
<div className="flex flex-col gap-5">
|
|
<PageSectionMeta className="!items-center">
|
|
<PageSectionSummary>
|
|
<div className="flex flex-wrap items-start gap-x-8 gap-y-4">
|
|
<ChartMetric
|
|
label="Total Invocations"
|
|
value={totalInvocationCount}
|
|
status="default"
|
|
tooltip="Total number of invocations"
|
|
/>
|
|
<ChartMetric
|
|
label="5xx Rate"
|
|
value={formatRate(totalErrorCount, totalInvocationCount)}
|
|
status="negative"
|
|
tooltip="Share of invocations that returned a 5xx status code"
|
|
/>
|
|
<ChartMetric
|
|
label="4xx Rate"
|
|
value={formatRate(totalWarningCount, totalInvocationCount)}
|
|
status="warning"
|
|
tooltip="Share of invocations that returned a 4xx status code"
|
|
/>
|
|
</div>
|
|
</PageSectionSummary>
|
|
<PageSectionAside className="flex-wrap @xl:self-center">
|
|
<div className="flex items-center">
|
|
{EDGE_FUNCTION_CHART_INTERVALS.map((item, index) => {
|
|
return (
|
|
<Button
|
|
key={`function-filter-${item.key}`}
|
|
type={interval === item.key ? 'secondary' : 'default'}
|
|
onClick={() => onIntervalChange(item.key)}
|
|
className={getSegmentedButtonClassName(
|
|
index,
|
|
EDGE_FUNCTION_CHART_INTERVALS.length
|
|
)}
|
|
>
|
|
{item.label}
|
|
</Button>
|
|
)
|
|
})}
|
|
</div>
|
|
<ChartActions actions={actions} />
|
|
</PageSectionAside>
|
|
</PageSectionMeta>
|
|
|
|
<div>
|
|
{isLoadingFunction && <GenericSkeletonLoader />}
|
|
{isErrorFunction && (
|
|
<AlertError
|
|
error={toAlertError(functionError)}
|
|
subject="Failed to retrieve edge function details"
|
|
layout="vertical"
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<Chart isLoading={isLoadingChart}>
|
|
{isLoadingChart ? (
|
|
<ChartLoadingState />
|
|
) : isErrorChart || chartData.length === 0 ? (
|
|
<EdgeFunctionChartEmptyState
|
|
title={emptyStateCopy.title}
|
|
description={emptyStateCopy.description}
|
|
/>
|
|
) : (
|
|
<EdgeFunctionInvocationsChart
|
|
chartData={chartData}
|
|
dateTimeFormat={dateTimeFormat}
|
|
onChartClick={onChartClick}
|
|
updateAnnotation={updateAnnotation}
|
|
/>
|
|
)}
|
|
</Chart>
|
|
</div>
|
|
</div>
|
|
</PageContainer>
|
|
</PageSectionContent>
|
|
</PageSection>
|
|
)
|
|
}
|