Skip to content

Commit

Permalink
feat: add a check usage calculator
Browse files Browse the repository at this point in the history
* feat: add a check usage calculator

* feat: add series calculation to check editing

* chore: pr feedback

* feat: add icons to check calculations

* chore: remove unused components and bump timeout

Co-authored-by: Nathan Rodman <nathanrodman@gmail.com>
  • Loading branch information
rdubrock and nathanrodman committed Dec 3, 2020
1 parent 82fe1bc commit 1c38252
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 1 deletion.
98 changes: 98 additions & 0 deletions src/checkUsageCalc.test.ts
@@ -0,0 +1,98 @@
import { calculateUsage } from './checkUsageCalc';
import { CheckType } from './types';

it('calculates http usage', () => {
const baseUsage = calculateUsage({
probeCount: 1,
checkType: CheckType.HTTP,
frequencySeconds: 60,
});
expect(baseUsage).toStrictEqual({
checksPerMonth: 43800,
activeSeries: 128,
logsGbPerMonth: 0.04,
});

const multipleProbes = calculateUsage({
probeCount: 4,
checkType: CheckType.HTTP,
frequencySeconds: 60,
});
expect(multipleProbes).toStrictEqual({
checksPerMonth: 175200,
activeSeries: 512,
logsGbPerMonth: 0.14,
});

const differentFrequency = calculateUsage({
probeCount: 4,
checkType: CheckType.HTTP,
frequencySeconds: 10,
});
expect(differentFrequency).toStrictEqual({
checksPerMonth: 1051200,
activeSeries: 512,
logsGbPerMonth: 0.84,
});
});

it('calculates ping usage', () => {
const baseUsage = calculateUsage({
probeCount: 1,
checkType: CheckType.PING,
frequencySeconds: 60,
});
expect(baseUsage).toStrictEqual({
checksPerMonth: 43800,
activeSeries: 76,
logsGbPerMonth: 0.04,
});

const multipleProbes = calculateUsage({
probeCount: 4,
checkType: CheckType.PING,
frequencySeconds: 60,
});
expect(multipleProbes).toStrictEqual({
checksPerMonth: 175200,
activeSeries: 304,
logsGbPerMonth: 0.14,
});

const differentFrequency = calculateUsage({
probeCount: 4,
checkType: CheckType.PING,
frequencySeconds: 10,
});
expect(differentFrequency).toStrictEqual({
checksPerMonth: 1051200,
activeSeries: 304,
logsGbPerMonth: 0.84,
});
});

it('calculates TCP usage', () => {
const baseUsage = calculateUsage({
probeCount: 1,
checkType: CheckType.TCP,
frequencySeconds: 60,
});
expect(baseUsage).toStrictEqual({
checksPerMonth: 43800,
activeSeries: 59,
logsGbPerMonth: 0.04,
});
});

it('calculates DNS usage', () => {
const baseUsage = calculateUsage({
probeCount: 1,
checkType: CheckType.DNS,
frequencySeconds: 60,
});
expect(baseUsage).toStrictEqual({
checksPerMonth: 43800,
activeSeries: 79,
logsGbPerMonth: 0.04,
});
});
63 changes: 63 additions & 0 deletions src/checkUsageCalc.ts
@@ -0,0 +1,63 @@
import { CheckType } from './types';

interface ActiveSeriesParams {
probeCount: number;
checkType: CheckType;
frequencySeconds: number;
}

interface UsageValues {
checksPerMonth: number;
activeSeries: number;
logsGbPerMonth: number;
}

enum CheckSeries {
PING = 76,
HTTP = 128,
DNS = 79,
TCP = 59,
}

const getSeriesPerCheck = (checkType: CheckType) => {
switch (checkType) {
case CheckType.PING:
return CheckSeries.PING;
case CheckType.TCP:
return CheckSeries.TCP;
case CheckType.DNS:
return CheckSeries.DNS;
case CheckType.HTTP:
return CheckSeries.HTTP;
}
};

const getChecksPerMonth = (frequencySeconds: number) => {
const checksPerMinute = Math.round(60 / frequencySeconds);
const checksPerHour = checksPerMinute * 60;
const checksPerMonth = checksPerHour * 730;
return checksPerMonth;
};

const getTotalChecksPerMonth = (probeCount: number, frequencySeconds: number) => {
const checksPerMonth = getChecksPerMonth(frequencySeconds);
return checksPerMonth * probeCount;
};

const getLogsGbPerMonth = (probeCount: number, frequencySeconds: number) => {
const gbPerCheck = 0.0008;
const checksPerMonth = getChecksPerMonth(frequencySeconds);
const logsGbPerMonth = (checksPerMonth * gbPerCheck * probeCount) / 1000;
return parseFloat(logsGbPerMonth.toFixed(2));
};

export const calculateUsage = ({ probeCount, checkType, frequencySeconds }: ActiveSeriesParams): UsageValues => {
const seriesPerCheck = getSeriesPerCheck(checkType);
const activeSeries = seriesPerCheck * probeCount;

return {
checksPerMonth: getTotalChecksPerMonth(probeCount, frequencySeconds),
activeSeries,
logsGbPerMonth: getLogsGbPerMonth(probeCount, frequencySeconds),
};
};
2 changes: 1 addition & 1 deletion src/components/CheckEditor/CheckEditor.test.tsx
Expand Up @@ -15,7 +15,7 @@ import { getInstanceMock } from '../../datasource/__mocks__/DataSource';
import userEvent from '@testing-library/user-event';
import { InstanceContext } from 'components/InstanceContext';
import { AppPluginMeta } from '@grafana/data';
jest.setTimeout(30000);
jest.setTimeout(60000);

// Data mocks

Expand Down
2 changes: 2 additions & 0 deletions src/components/CheckEditor/CheckEditor.tsx
Expand Up @@ -15,6 +15,7 @@ import { ProbeOptions } from './ProbeOptions';
import { CHECK_TYPE_OPTIONS, fallbackCheck } from 'components/constants';
import { useForm, FormContext, Controller } from 'react-hook-form';
import { GrafanaTheme } from '@grafana/data';
import { CheckUsage } from '../CheckUsage';

interface Props {
check?: Check;
Expand Down Expand Up @@ -144,6 +145,7 @@ export const CheckEditor: FC<Props> = ({ check, instance, onReturn }) => {
frequency={check?.frequency ?? fallbackCheck.frequency}
probes={check?.probes ?? fallbackCheck.probes}
/>
<CheckUsage />
<CheckSettings typeOfCheck={selectedCheckType} isEditor={isEditor} />
</div>
<HorizontalGroup>
Expand Down
59 changes: 59 additions & 0 deletions src/components/CheckUsage.tsx
@@ -0,0 +1,59 @@
import React, { FC } from 'react';
import { useFormContext } from 'react-hook-form';
import { calculateUsage } from '../checkUsageCalc';
import { css } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { useStyles, Icon } from '@grafana/ui';
import { CheckFormValues } from 'types';

const getStyles = (theme: GrafanaTheme) => ({
container: css`
background-color: ${theme.colors.bg2};
padding: ${theme.spacing.lg};
margin-bottom: ${theme.spacing.md};
`,
header: css`
margin-bottom: ${theme.spacing.md};
`,
icon: css`
margin-right: ${theme.spacing.sm};
`,
section: css`
margin-bottom: ${theme.spacing.sm};
`,
value: css`
margin-left: ${theme.spacing.xs};
`,
});

export const CheckUsage: FC = () => {
const styles = useStyles(getStyles);
const { watch } = useFormContext();
const { checkType, frequency, probes }: Partial<CheckFormValues> = watch(['checkType', 'frequency', 'probes']);

if (!checkType?.value || !frequency || !probes) {
return null;
}
const { checksPerMonth, activeSeries, logsGbPerMonth } = calculateUsage({
probeCount: probes.length,
frequencySeconds: frequency,
checkType: checkType.value,
});
return (
<div className={styles.container}>
<h5 className={styles.header}>Approximate expected usage for this check</h5>
<div className={styles.section}>
<Icon className={styles.icon} name={'calendar-alt'} />
Checks per month: <strong className={styles.value}>{checksPerMonth.toLocaleString()}</strong>
</div>
<div className={styles.section}>
<Icon className={styles.icon} name={'chart-line'} />
Active series: <strong className={styles.value}>{activeSeries.toLocaleString()}</strong>
</div>
<div className={styles.section}>
<Icon className={styles.icon} name={'database'} />
Log usage per month (GB): <strong className={styles.value}>{logsGbPerMonth.toLocaleString()}</strong>
</div>
</div>
);
};

0 comments on commit 1c38252

Please sign in to comment.