feat: monitor add max retries to avoid network fluctuation

This commit is contained in:
moonrailgun 2024-02-09 21:16:39 +08:00
parent 461e23be2f
commit 97d55da454
8 changed files with 66 additions and 46 deletions

View File

@ -19,6 +19,7 @@ const defaultValues: Omit<MonitorInfoEditorValues, 'payload'> = {
type: monitorProviders[0].name, type: monitorProviders[0].name,
active: true, active: true,
interval: 60, interval: 60,
maxRetries: 0,
}; };
interface MonitorInfoEditorProps { interface MonitorInfoEditorProps {
@ -90,6 +91,14 @@ export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
/> />
</Form.Item> </Form.Item>
<Form.Item
label="Max Retries"
name="maxRetries"
tooltip="Maximum retries before the service is marked as down and a notification is sent"
>
<InputNumber min={0} max={10} defaultValue={0} />
</Form.Item>
{formEl} {formEl}
<Form.Item label="Notification" name="notificationIds"> <Form.Item label="Notification" name="notificationIds">

View File

@ -1,4 +1,5 @@
import { z } from 'zod'; import { z } from 'zod';
import { MonitorModelSchema } from '../../prisma/zod';
// Match prisma `JsonValue` // Match prisma `JsonValue`
export const jsonFieldSchema = z.union([ export const jsonFieldSchema = z.union([
@ -53,18 +54,7 @@ export const websiteInfoSchema = z.object({
deletedAt: z.date().nullable(), deletedAt: z.date().nullable(),
}); });
export const monitorInfoSchema = z.object({ export const monitorInfoWithNotificationIdSchema = MonitorModelSchema.and(
id: z.string(),
workspaceId: z.string(),
name: z.string(),
type: z.string(),
active: z.boolean(),
interval: z.number(),
payload: jsonFieldSchema,
createdAt: z.date(),
});
export const monitorInfoWithNotificationIdSchema = monitorInfoSchema.and(
z.object({ z.object({
notifications: z.array(z.object({ id: z.string() })), notifications: z.array(z.object({ id: z.string() })),
}) })

View File

@ -5,7 +5,7 @@ import { logger } from '../../utils/logger';
export type MonitorUpsertData = Pick< export type MonitorUpsertData = Pick<
Monitor, Monitor,
'workspaceId' | 'name' | 'type' | 'interval' 'workspaceId' | 'name' | 'type' | 'interval' | 'maxRetries'
> & { > & {
id?: string; id?: string;
active?: boolean; active?: boolean;

View File

@ -15,6 +15,7 @@ import { createAuditLog } from '../auditLog';
export class MonitorRunner { export class MonitorRunner {
isStopped = false; isStopped = false;
timer: NodeJS.Timeout | null = null; timer: NodeJS.Timeout | null = null;
retriedNum = 0;
constructor(public monitor: Monitor & { notifications: Notification[] }) {} constructor(public monitor: Monitor & { notifications: Notification[] }) {}
@ -23,7 +24,7 @@ export class MonitorRunner {
*/ */
async startMonitor() { async startMonitor() {
const monitor = this.monitor; const monitor = this.monitor;
const { type, interval, workspaceId } = monitor; const { type, interval, workspaceId, maxRetries } = monitor;
const provider = monitorProviders[type]; const provider = monitorProviders[type];
if (!provider) { if (!provider) {
@ -58,8 +59,13 @@ export class MonitorRunner {
value = -1; value = -1;
} }
if (this.retriedNum < maxRetries) {
// can be retry
this.retriedNum++;
} else {
// check event update // check event update
if (value < 0 && currentStatus === 'UP') { if (value < 0 && currentStatus === 'UP') {
// UP -> DOWN
await this.createEvent( await this.createEvent(
'DOWN', 'DOWN',
`Monitor [${monitor.name}] has been down` `Monitor [${monitor.name}] has been down`
@ -73,7 +79,12 @@ export class MonitorRunner {
]); ]);
currentStatus = 'DOWN'; currentStatus = 'DOWN';
} else if (value > 0 && currentStatus === 'DOWN') { } else if (value > 0 && currentStatus === 'DOWN') {
await this.createEvent('UP', `Monitor [${monitor.name}] has been up`); // DOWN -> UP
this.retriedNum = 0;
await this.createEvent(
'UP',
`Monitor [${monitor.name}] has been up`
);
await this.notify(`[${monitor.name}] ✅ Up`, [ await this.notify(`[${monitor.name}] ✅ Up`, [
token.text( token.text(
`[${monitor.name}] ✅ Up\nTime: ${dayjs().format( `[${monitor.name}] ✅ Up\nTime: ${dayjs().format(
@ -83,6 +94,7 @@ export class MonitorRunner {
]); ]);
currentStatus = 'UP'; currentStatus = 'UP';
} }
}
// insert into data // insert into data
const data = await prisma.monitorData.create({ const data = await prisma.monitorData.create({
@ -93,9 +105,6 @@ export class MonitorRunner {
}); });
subscribeEventBus.emit('onMonitorReceiveNewData', workspaceId, data); subscribeEventBus.emit('onMonitorReceiveNewData', workspaceId, data);
// Run next loop
nextAction();
} catch (err) { } catch (err) {
logger.error('[Monitor] Run monitor error,', monitor.id, String(err)); logger.error('[Monitor] Run monitor error,', monitor.id, String(err));
createAuditLog({ createAuditLog({
@ -104,6 +113,9 @@ export class MonitorRunner {
relatedType: 'Monitor', relatedType: 'Monitor',
content: `Run monitor(id: ${monitor.id}) error: ${String(err)}`, content: `Run monitor(id: ${monitor.id}) error: ${String(err)}`,
}); });
} finally {
// Run next loop
nextAction();
} }
}; };

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Monitor" ADD COLUMN "maxRetries" INTEGER NOT NULL DEFAULT 0;

View File

@ -265,6 +265,7 @@ model Monitor {
type String @db.VarChar(100) type String @db.VarChar(100)
active Boolean @default(true) @db.Boolean active Boolean @default(true) @db.Boolean
interval Int @default(20) @db.Integer interval Int @default(20) @db.Integer
maxRetries Int @default(0)
// TODO // TODO
// maxRetry Int @default(0) @db.Integer // maxRetry Int @default(0) @db.Integer
// retryInterval Int @default(0) @db.Integer // retryInterval Int @default(0) @db.Integer

View File

@ -15,6 +15,7 @@ export const MonitorModelSchema = z.object({
type: z.string(), type: z.string(),
active: z.boolean(), active: z.boolean(),
interval: z.number().int(), interval: z.number().int(),
maxRetries: z.number().int(),
/** /**
* [CommonPayload] * [CommonPayload]
*/ */

View File

@ -16,13 +16,15 @@ import {
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { import {
monitorEventSchema, monitorEventSchema,
monitorInfoSchema,
monitorInfoWithNotificationIdSchema, monitorInfoWithNotificationIdSchema,
monitorStatusSchema, monitorStatusSchema,
} from '../../model/_schema'; } from '../../model/_schema';
import { OPENAPI_TAG } from '../../utils/const'; import { OPENAPI_TAG } from '../../utils/const';
import { OpenApiMeta } from 'trpc-openapi'; import { OpenApiMeta } from 'trpc-openapi';
import { MonitorStatusPageModelSchema } from '../../prisma/zod'; import {
MonitorModelSchema,
MonitorStatusPageModelSchema,
} from '../../prisma/zod';
import { runCodeInVM } from '../../model/monitor/provider/custom'; import { runCodeInVM } from '../../model/monitor/provider/custom';
import { createAuditLog } from '../../model/auditLog'; import { createAuditLog } from '../../model/auditLog';
import { import {
@ -124,11 +126,12 @@ export const monitorRouter = router({
type: z.string(), type: z.string(),
active: z.boolean().default(true), active: z.boolean().default(true),
interval: z.number().int().min(5).max(10000).default(20), interval: z.number().int().min(5).max(10000).default(20),
maxRetries: z.number().int().min(0).max(10).default(0),
notificationIds: z.array(z.string()).default([]), notificationIds: z.array(z.string()).default([]),
payload: z.object({}).passthrough(), payload: z.object({}).passthrough(),
}) })
) )
.output(monitorInfoSchema) .output(MonitorModelSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const { const {
id, id,
@ -137,6 +140,7 @@ export const monitorRouter = router({
type, type,
active, active,
interval, interval,
maxRetries,
notificationIds, notificationIds,
payload, payload,
} = input; } = input;
@ -148,6 +152,7 @@ export const monitorRouter = router({
type, type,
active, active,
interval, interval,
maxRetries,
notificationIds, notificationIds,
payload, payload,
}); });
@ -166,7 +171,7 @@ export const monitorRouter = router({
monitorId: z.string().cuid2(), monitorId: z.string().cuid2(),
}) })
) )
.output(monitorInfoSchema) .output(MonitorModelSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const { workspaceId, monitorId } = input; const { workspaceId, monitorId } = input;
@ -239,7 +244,7 @@ export const monitorRouter = router({
active: z.boolean(), active: z.boolean(),
}) })
) )
.output(monitorInfoSchema) .output(MonitorModelSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { workspaceId, monitorId, active } = input; const { workspaceId, monitorId, active } = input;
const user = ctx.user; const user = ctx.user;