Skip to content

Commit

Permalink
Refactor notifications (#381)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablomendezroyo authored Oct 2, 2024
1 parent 681085c commit cd47394
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConsensusClient, ExecutionClient } from "@stakingbrain/common";
import { DappmanagerApi, PostgresClient } from "../../apiClients/index.js";
import { PostgresClient } from "../../apiClients/index.js";
import {
BlockProposalStatus,
ValidatorPerformance,
Expand All @@ -9,18 +9,15 @@ import {
import { IdealRewards, TotalRewards } from "../../apiClients/types.js";
import logger from "../../logger/index.js";
import { logPrefix } from "./logPrefix.js";
import { sendValidatorsPerformanceNotifications } from "./sendValidatorsPerformanceNotifications.js";

/**
* Insert the performance data for the validators in the Postgres DB. On any error
* inserting the performance of a validator, the error will be logged and the process will continue
* with the next validator.
*/
export async function insertPerformanceDataAndSendNotification({
export async function insertPerformanceData({
executionClient,
consensusClient,
sendNotification,
dappmanagerApi,
postgresClient,
currentEpoch,
activeValidatorsIndexes,
Expand All @@ -30,8 +27,6 @@ export async function insertPerformanceDataAndSendNotification({
}: {
executionClient: ExecutionClient;
consensusClient: ConsensusClient;
sendNotification: boolean;
dappmanagerApi: DappmanagerApi;
postgresClient: PostgresClient;
currentEpoch: number;
activeValidatorsIndexes: string[];
Expand Down Expand Up @@ -103,15 +98,6 @@ export async function insertPerformanceDataAndSendNotification({
attestationsTotalRewards
}
});

await sendValidatorsPerformanceNotifications({
sendNotification,
dappmanagerApi,
currentEpoch: currentEpoch.toString(),
validatorBlockStatusMap,
validatorAttestationsRewards,
error
});
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,42 @@
import { DappmanagerApi } from "../../apiClients/index.js";
import { NotificationType } from "../../apiClients/dappmanager/types.js";
import { BlockProposalStatus, ValidatorPerformanceError } from "../../apiClients/postgres/types.js";
import { BlockProposalStatus } from "../../apiClients/postgres/types.js";
import logger from "../../logger/index.js";
import { logPrefix } from "./logPrefix.js";
import { IdealRewards, TotalRewards } from "../../apiClients/types.js";

/**
* Sends validator performance notification to the dappmanager. The notification will have the following format:
* ```
* **Validator(s) performance notification for epoch **
* - Blocks:
* - Proposed: Validator(s) proposed a block
* - Missed: Validator(s) missed a block
* - Attestations
* - Error
* ```
* Sends validator performance notification to the dappmanager. The notifications available are:
* - success: validator(s) proposed a block
* - warning: validator(s) missed attestations
* - danger: validator(s) missed a block
*/
export async function sendValidatorsPerformanceNotifications({
sendNotification,
dappmanagerApi,
currentEpoch,
validatorBlockStatusMap,
validatorAttestationsRewards,
error
validatorAttestationsRewards
}: {
sendNotification: boolean;
dappmanagerApi: DappmanagerApi;
currentEpoch: string;
validatorBlockStatusMap?: Map<string, BlockProposalStatus>;
validatorAttestationsRewards?: { totalRewards: TotalRewards[]; idealRewards: IdealRewards };
error?: ValidatorPerformanceError;
}): Promise<void> {
if (!sendNotification) return;
if (error)
await dappmanagerApi.sendDappmanagerNotification({
title: "Failed to fetch performance data",
notificationType: NotificationType.Danger,
body: `Failed to fetch performance data for epoch ${currentEpoch}: ${error}`
});
else if (validatorBlockStatusMap && validatorAttestationsRewards)
await Promise.all([
sendSuccessNotificationNotThrow({ dappmanagerApi, validatorBlockStatusMap, currentEpoch }),
sendWarningNotificationNotThrow({
dappmanagerApi,
validatorBlockStatusMap,
validatorAttestationsRewards,
currentEpoch
})
}),
sendDangerNotificationNotThrow({ dappmanagerApi, validatorBlockStatusMap, currentEpoch })
]);
}

/**
* Triggers sending success notification in the dappmanager if any:
* - blocks proposed
*/
async function sendSuccessNotificationNotThrow({
dappmanagerApi,
currentEpoch,
Expand All @@ -77,38 +60,48 @@ async function sendSuccessNotificationNotThrow({
.catch((error) => logger.error(`${logPrefix}Failed to send success notification to dappmanager`, error));
}

/**
* Triggers sending warning notification in the dappmanager if any:
* - blocks missed
* - attestations missed
*/
async function sendWarningNotificationNotThrow({
dappmanagerApi,
validatorBlockStatusMap,
validatorAttestationsRewards,
currentEpoch
}: {
dappmanagerApi: DappmanagerApi;
validatorBlockStatusMap: Map<string, BlockProposalStatus>;
validatorAttestationsRewards: { totalRewards: TotalRewards[]; idealRewards: IdealRewards };
currentEpoch: string;
}): Promise<void> {
// Send the warning notification together: block missed and att missed
const validatorsMissedBlocks = Array.from(validatorBlockStatusMap).filter(
([_, blockStatus]) => blockStatus === "Missed"
);
const validatorsMissedAttestations = validatorAttestationsRewards.totalRewards
.filter((validator) => parseInt(validator.source) <= 0)
.map((validator) => validator.validator_index);

if (validatorsMissedBlocks.length === 0 && validatorsMissedAttestations.length === 0) return;
if (validatorsMissedAttestations.length === 0) return;
await dappmanagerApi
.sendDappmanagerNotification({
title: `Validator(s) missed a block or attestation in epoch ${currentEpoch}`,
title: `Validator(s) missed attestations in epoch ${currentEpoch}`,
notificationType: NotificationType.Warning,
body: `Validator(s) ${validatorsMissedBlocks.join(", ")} missed a block. Validator(s) ${validatorsMissedAttestations.join(
", "
)} missed an attestation`
body: `Validator(s) ${validatorsMissedAttestations.join(", ")} missed attestations`
})
.catch((error) => logger.error(`${logPrefix}Failed to send warning notification to dappmanager`, error));
}

async function sendDangerNotificationNotThrow({
dappmanagerApi,
currentEpoch,
validatorBlockStatusMap
}: {
dappmanagerApi: DappmanagerApi;
validatorBlockStatusMap: Map<string, BlockProposalStatus>;
currentEpoch: string;
}): Promise<void> {
const validatorsMissedBlocks = Array.from(validatorBlockStatusMap).filter(
([_, blockStatus]) => blockStatus === "Missed"
);

if (validatorsMissedBlocks.length === 0) return;
await dappmanagerApi
.sendDappmanagerNotification({
title: `Validator(s) missed a block in epoch ${currentEpoch}`,
notificationType: NotificationType.Danger,
body: `Validator(s) ${validatorsMissedBlocks.join(", ")} missed a block`
})
.catch((error) => logger.error(`${logPrefix}Failed to send danger notification to dappmanager`, error));
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BeaconchainApi } from "../../apiClients/beaconchain/index.js";
import { PostgresClient } from "../../apiClients/postgres/index.js";
import logger from "../../logger/index.js";
import { BrainDataBase } from "../../db/index.js";
import { insertPerformanceDataAndSendNotification } from "./insertPerformanceDataAndSendNotification.js";
import { insertPerformanceData } from "./insertPerformanceData.js";
import { getValidatorAttestationsRewards } from "./getValidatorAttestationsRewards.js";
import { getBlockProposalStatusMap } from "./getBlockProposalStatusMap.js";
import { getActiveValidatorsLoadedInBrain } from "./getActiveValidatorsLoadedInBrain.js";
Expand All @@ -18,6 +18,7 @@ import { BeaconchainApiError } from "../../apiClients/beaconchain/error.js";
import { BrainDbError } from "../../db/error.js";
import { ExecutionOfflineError, NodeSyncingError } from "./error.js";
import { DappmanagerApi } from "../../apiClients/index.js";
import { sendValidatorsPerformanceNotifications } from "./sendValidatorsPerformanceNotifications.js";

let lastProcessedEpoch: number | undefined = undefined;
let lastEpochProcessedWithError = false;
Expand Down Expand Up @@ -127,9 +128,7 @@ export async function fetchAndInsertPerformanceCron({
lastEpochProcessedWithError = true;
} finally {
// Always call storeData in the finally block, regardless of success or failure in try block
await insertPerformanceDataAndSendNotification({
sendNotification,
dappmanagerApi,
await insertPerformanceData({
postgresClient,
activeValidatorsIndexes,
currentEpoch,
Expand All @@ -139,6 +138,16 @@ export async function fetchAndInsertPerformanceCron({
executionClient,
consensusClient
});

// Send notifications if the last epoch was processed without an error
if (!lastEpochProcessedWithError)
await sendValidatorsPerformanceNotifications({
sendNotification,
dappmanagerApi,
currentEpoch: currentEpoch.toString(),
validatorBlockStatusMap,
validatorAttestationsRewards
});
}
}

Expand Down

0 comments on commit cd47394

Please sign in to comment.