mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 06:16:12 +08:00
feat: show rate limit warning in services page when total RPS > 100 (#4266)
* feat: show rate limit warning in services page when total rps > 100 * feat: update message * feat: rate limit message should be shown only to cloud users on trail --------- Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
585d6e2a21
commit
2e0fdbb498
3
frontend/public/locales/en-GB/services.json
Normal file
3
frontend/public/locales/en-GB/services.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support."
|
||||||
|
}
|
3
frontend/public/locales/en/services.json
Normal file
3
frontend/public/locales/en/services.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rps_over_100": "You are sending data at more than 100 RPS, your ingestion may be rate limited. Please reach out to us via Intercom support."
|
||||||
|
}
|
2
frontend/src/constants/global.ts
Normal file
2
frontend/src/constants/global.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
const MAX_RPS_LIMIT = 100;
|
||||||
|
export { MAX_RPS_LIMIT };
|
@ -1,12 +1,20 @@
|
|||||||
|
import { WarningFilled } from '@ant-design/icons';
|
||||||
|
import { Flex, Typography } from 'antd';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import { MAX_RPS_LIMIT } from 'constants/global';
|
||||||
|
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||||
import { useGetQueriesRange } from 'hooks/queryBuilder/useGetQueriesRange';
|
import { useGetQueriesRange } from 'hooks/queryBuilder/useGetQueriesRange';
|
||||||
|
import useLicense from 'hooks/useLicense';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { useMemo } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { ServicesList } from 'types/api/metrics/getService';
|
import { ServicesList } from 'types/api/metrics/getService';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { isCloudUser } from 'utils/app';
|
||||||
|
import { getTotalRPS } from 'utils/services';
|
||||||
|
|
||||||
import { getColumns } from '../Columns/ServiceColumn';
|
import { getColumns } from '../Columns/ServiceColumn';
|
||||||
import { ServiceMetricsTableProps } from '../types';
|
import { ServiceMetricsTableProps } from '../types';
|
||||||
@ -22,6 +30,10 @@ function ServiceMetricTable({
|
|||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
const { t: getText } = useTranslation(['services']);
|
||||||
|
|
||||||
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
const isCloudUserVal = isCloudUser();
|
||||||
|
|
||||||
const queries = useGetQueriesRange(queryRangeRequestData, {
|
const queries = useGetQueriesRange(queryRangeRequestData, {
|
||||||
queryKey: [
|
queryKey: [
|
||||||
@ -53,14 +65,38 @@ function ServiceMetricTable({
|
|||||||
|
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const tableColumns = useMemo(() => getColumns(search, true), [search]);
|
const tableColumns = useMemo(() => getColumns(search, true), [search]);
|
||||||
|
const [RPS, setRPS] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFetching && licenseData?.payload?.onTrial && isCloudUserVal) {
|
||||||
|
if (services.length > 0) {
|
||||||
|
const rps = getTotalRPS(services);
|
||||||
|
setRPS(rps);
|
||||||
|
} else {
|
||||||
|
setRPS(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [services, licenseData, isFetching, isCloudUserVal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<>
|
||||||
columns={tableColumns}
|
{RPS > MAX_RPS_LIMIT && (
|
||||||
loading={isLoading}
|
<Flex justify="center">
|
||||||
dataSource={services}
|
<Typography.Title level={5} type="warning" style={{ marginTop: 0 }}>
|
||||||
rowKey="serviceName"
|
<WarningFilled /> {getText('rps_over_100')}
|
||||||
/>
|
</Typography.Title>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ResourceAttributesFilter />
|
||||||
|
|
||||||
|
<ResizeTable
|
||||||
|
columns={tableColumns}
|
||||||
|
loading={isLoading}
|
||||||
|
dataSource={services}
|
||||||
|
rowKey="serviceName"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
|
import { WarningFilled } from '@ant-design/icons';
|
||||||
|
import { Flex, Typography } from 'antd';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
import { useMemo } from 'react';
|
import { MAX_RPS_LIMIT } from 'constants/global';
|
||||||
|
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||||
|
import useLicense from 'hooks/useLicense';
|
||||||
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { isCloudUser } from 'utils/app';
|
||||||
|
import { getTotalRPS } from 'utils/services';
|
||||||
|
|
||||||
import { getColumns } from '../Columns/ServiceColumn';
|
import { getColumns } from '../Columns/ServiceColumn';
|
||||||
import ServiceTableProps from '../types';
|
import ServiceTableProps from '../types';
|
||||||
@ -10,16 +18,43 @@ function ServiceTraceTable({
|
|||||||
loading,
|
loading,
|
||||||
}: ServiceTableProps): JSX.Element {
|
}: ServiceTableProps): JSX.Element {
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
const [RPS, setRPS] = useState(0);
|
||||||
|
const { t: getText } = useTranslation(['services']);
|
||||||
|
|
||||||
|
const { data: licenseData, isFetching } = useLicense();
|
||||||
|
const isCloudUserVal = isCloudUser();
|
||||||
const tableColumns = useMemo(() => getColumns(search, false), [search]);
|
const tableColumns = useMemo(() => getColumns(search, false), [search]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFetching && licenseData?.payload?.onTrial && isCloudUserVal) {
|
||||||
|
if (services.length > 0) {
|
||||||
|
const rps = getTotalRPS(services);
|
||||||
|
setRPS(rps);
|
||||||
|
} else {
|
||||||
|
setRPS(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [services, licenseData, isFetching, isCloudUserVal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<>
|
||||||
columns={tableColumns}
|
{RPS > MAX_RPS_LIMIT && (
|
||||||
loading={loading}
|
<Flex justify="flex-end">
|
||||||
dataSource={services}
|
<Typography.Text type="warning" style={{ marginTop: 0 }}>
|
||||||
rowKey="serviceName"
|
<WarningFilled /> {getText('rps_over_100')}
|
||||||
/>
|
</Typography.Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ResourceAttributesFilter />
|
||||||
|
|
||||||
|
<ResizeTable
|
||||||
|
columns={tableColumns}
|
||||||
|
loading={loading}
|
||||||
|
dataSource={services}
|
||||||
|
rowKey="serviceName"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ function Services(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||||
<Container>
|
<Container style={{ marginTop: 0 }}>
|
||||||
{isSpanMetricEnabled ? <ServiceMetrics /> : <ServiceTraces />}
|
{isSpanMetricEnabled ? <ServiceMetrics /> : <ServiceTraces />}
|
||||||
</Container>
|
</Container>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
@ -6,9 +6,12 @@ import { extractDomain } from 'utils/app';
|
|||||||
const useAnalytics = (): any => {
|
const useAnalytics = (): any => {
|
||||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
|
// Segment Page View - analytics.page([category], [name], [properties], [options], [callback]);
|
||||||
const trackPageView = (pageName: string): void => {
|
const trackPageView = (pageName: string): void => {
|
||||||
if (user && user.email) {
|
if (user && user.email) {
|
||||||
window.analytics.page(pageName);
|
window.analytics.page(null, pageName, {
|
||||||
|
userId: user.email,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,6 +25,9 @@ const useAnalytics = (): any => {
|
|||||||
groupId: extractDomain(user?.email),
|
groupId: extractDomain(user?.email),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updatedPropertes = { ...properties };
|
||||||
|
updatedPropertes.userId = user.email;
|
||||||
window.analytics.track(eventName, properties, context);
|
window.analytics.track(eventName, properties, context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import ReleaseNote from 'components/ReleaseNote';
|
import ReleaseNote from 'components/ReleaseNote';
|
||||||
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
|
||||||
import ServicesApplication from 'container/ServiceApplication';
|
import ServicesApplication from 'container/ServiceApplication';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
@ -11,7 +10,6 @@ function Metrics(): JSX.Element {
|
|||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
<ReleaseNote path={location.pathname} />
|
<ReleaseNote path={location.pathname} />
|
||||||
|
|
||||||
<ResourceAttributesFilter />
|
|
||||||
<ServicesApplication />
|
<ServicesApplication />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
4
frontend/src/utils/services.ts
Normal file
4
frontend/src/utils/services.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { ServicesList } from 'types/api/metrics/getService';
|
||||||
|
|
||||||
|
export const getTotalRPS = (services: ServicesList[]): number =>
|
||||||
|
services.reduce((accumulator, service) => accumulator + service.callRate, 0);
|
Loading…
x
Reference in New Issue
Block a user