Add messages sent metrics to kathy (#749)

* re-enable eth for kathy

* Better gitignore

* add prometheus dns config

* add prometheus metrics to kathy

* fix newline

* cleanup gitignore

* fix job name

* cleanup gitignore

* cleanup gitignore

* make metrics submission a lib
pull/772/head
Mattie Conover 2 years ago committed by GitHub
parent 2769c4b357
commit 6fb29b64eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .gitignore
  2. 4
      typescript/infra/config/environments/mainnet/helloworld.ts
  3. 2
      typescript/infra/config/environments/testnet2/helloworld.ts
  4. 3
      typescript/infra/helm/helloworld-kathy/templates/cron-job.yaml
  5. 86
      typescript/infra/scripts/funding/fund-relayers-from-deployer.ts
  6. 35
      typescript/infra/scripts/helloworld/kathy.ts
  7. 1
      typescript/infra/src/config/helloworld.ts
  8. 3
      typescript/infra/src/helloworld/kathy.ts
  9. 27
      typescript/infra/src/utils/metrics.ts

4
.gitignore vendored

@ -14,10 +14,8 @@ rust/tmp.env
tmp.env tmp.env
.DS_STORE .DS_STORE
typescript/*/.env
typescript/*/node_modules typescript/*/node_modules
typescript/**/tsconfig.tsbuildinfo solidity/*/artifacts
**/**/tsconfig.tsbuildinfo
.yarn/install-state.gz .yarn/install-state.gz
.yarn/cache .yarn/cache

@ -11,8 +11,10 @@ export const helloWorld: HelloWorldConfig<MainnetChains> = {
tag: 'sha-0f9c0f9', tag: 'sha-0f9c0f9',
}, },
cronSchedule: '0 15 * * *', // Every day at 3:00 PM UTC cronSchedule: '0 15 * * *', // Every day at 3:00 PM UTC
chainsToSkip: ['ethereum'], chainsToSkip: [],
runEnv: environment, runEnv: environment,
namespace: environment, namespace: environment,
prometheusPushGateway:
'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091',
}, },
}; };

@ -14,5 +14,7 @@ export const helloWorld: HelloWorldConfig<TestnetChains> = {
chainsToSkip: [], chainsToSkip: [],
runEnv: environment, runEnv: environment,
namespace: environment, namespace: environment,
prometheusPushGateway:
'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091',
}, },
}; };

@ -28,4 +28,5 @@ spec:
env: env:
- name: CHAINS_TO_SKIP - name: CHAINS_TO_SKIP
value: {{ join "," .Values.chainsToSkip }} value: {{ join "," .Values.chainsToSkip }}
- name: PROMETHEUS_PUSH_GATEWAY
value: {{ .Values.infra.prometheusPushGateway }}

@ -1,16 +1,13 @@
import { Console } from 'console'; import { Console } from 'console';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { Gauge, Pushgateway, Registry } from 'prom-client'; import { Gauge, Registry } from 'prom-client';
import { import { ChainConnection, CompleteChainMap } from '@abacus-network/sdk';
ChainConnection,
ChainName,
CompleteChainMap,
} from '@abacus-network/sdk';
import { AgentKey, ReadOnlyAgentKey } from '../../src/agents/agent'; import { AgentKey, ReadOnlyAgentKey } from '../../src/agents/agent';
import { getRelayerKeys } from '../../src/agents/key-utils'; import { getRelayerKeys } from '../../src/agents/key-utils';
import { KEY_ROLE_ENUM } from '../../src/agents/roles'; import { KEY_ROLE_ENUM } from '../../src/agents/roles';
import { submitMetrics } from '../../src/utils/metrics';
import { readJSONAtPath } from '../../src/utils/utils'; import { readJSONAtPath } from '../../src/utils/utils';
import { import {
assertEnvironment, assertEnvironment,
@ -43,12 +40,6 @@ const walletBalanceGauge = new Gauge({
}); });
metricsRegister.registerMetric(walletBalanceGauge); metricsRegister.registerMetric(walletBalanceGauge);
interface FunderBalance {
chain: ChainName;
address?: string;
balance: number;
}
// Min delta is 1/10 of the desired balance // Min delta is 1/10 of the desired balance
const MIN_DELTA_NUMERATOR = ethers.BigNumber.from(1); const MIN_DELTA_NUMERATOR = ethers.BigNumber.from(1);
const MIN_DELTA_DENOMINATOR = ethers.BigNumber.from(10); const MIN_DELTA_DENOMINATOR = ethers.BigNumber.from(10);
@ -148,8 +139,6 @@ async function main() {
: getRelayerKeys(agentConfig); : getRelayerKeys(agentConfig);
const chains = relayerKeys.map((key) => key.chainName!); const chains = relayerKeys.map((key) => key.chainName!);
const balances: FunderBalance[] = [];
let failureOccurred = false; let failureOccurred = false;
for (const chain of chains) { for (const chain of chains) {
@ -183,16 +172,23 @@ async function main() {
failureOccurred = true; failureOccurred = true;
} }
} }
balances.push({ walletBalanceGauge
chain, .labels({
address: funderAddress, chain,
balance: parseFloat( wallet_address: funderAddress ?? 'unknown',
ethers.utils.formatEther(await chainConnection.signer!.getBalance()), wallet_name: 'relayer-funder',
), token_symbol: 'Native',
}); token_name: 'Native',
...constMetricLabels,
})
.set(
parseFloat(
ethers.utils.formatEther(await chainConnection.signer!.getBalance()),
),
);
} }
await submitFunderBalanceMetrics(balances); await submitMetrics(metricsRegister);
if (failureOccurred) { if (failureOccurred) {
error('At least one failure occurred when funding relayers'); error('At least one failure occurred when funding relayers');
@ -217,53 +213,13 @@ function getRelayerKeysFromSerializedAddressFile(path: string): AgentKey[] {
.filter((key: AgentKey) => key.role === KEY_ROLE_ENUM.Relayer); .filter((key: AgentKey) => key.role === KEY_ROLE_ENUM.Relayer);
} }
function getPushGateway(): Pushgateway | null {
const gatewayAddr = process.env['PROMETHEUS_PUSH_GATEWAY'];
if (gatewayAddr) {
return new Pushgateway(gatewayAddr, [], metricsRegister);
} else {
warn(
'Prometheus push gateway address was not defined; not publishing metrics.',
);
return null;
}
}
async function submitFunderBalanceMetrics(balances: FunderBalance[]) {
const gateway = getPushGateway();
if (!gateway) return;
for (const { chain, address, balance } of balances) {
walletBalanceGauge
.labels({
chain,
wallet_address: address ?? 'unknown',
wallet_name: 'relayer-funder',
token_symbol: 'Native',
token_name: 'Native',
...constMetricLabels,
})
.set(balance);
}
const { resp, body } = await gateway.push({ jobName: 'relayer_funder' });
const statusCode =
typeof resp == 'object' && resp != null && 'statusCode' in resp
? (resp as any).statusCode
: 'unknown';
log(`Prometheus metrics pushed to PushGateway`, {
statusCode,
body,
});
}
function log(message: string, data?: any) { function log(message: string, data?: any) {
logWithFunction(console.log, message, data); logWithFunction(console.log, message, data);
} }
function warn(message: string, data?: any) { // function warn(message: string, data?: any) {
logWithFunction(console.warn, message, data); // logWithFunction(console.warn, message, data);
} // }
function error(message: string, data?: any) { function error(message: string, data?: any) {
logWithFunction(console.error, message, data); logWithFunction(console.error, message, data);

@ -1,13 +1,37 @@
import { Counter, Registry } from 'prom-client';
import { HelloWorldApp } from '@abacus-network/helloworld'; import { HelloWorldApp } from '@abacus-network/helloworld';
import { ChainName, Chains } from '@abacus-network/sdk'; import { ChainName, Chains } from '@abacus-network/sdk';
import { submitMetrics } from '../../src/utils/metrics';
import { sleep } from '../../src/utils/utils'; import { sleep } from '../../src/utils/utils';
import { getCoreEnvironmentConfig, getEnvironment } from '../utils'; import { getCoreEnvironmentConfig, getEnvironment } from '../utils';
import { getApp } from './utils'; import { getApp } from './utils';
const constMetricLabels = {
// this needs to get set in main because of async reasons
abacus_deployment: '',
abacus_context: 'abacus',
};
const metricsRegister = new Registry();
const messagesCount = new Counter({
name: 'abacus_kathy_messages',
help: 'Test messages which have been sent with',
registers: [metricsRegister],
labelNames: [
'origin',
'remote',
'status',
...(Object.keys(constMetricLabels) as (keyof typeof constMetricLabels)[]),
],
});
metricsRegister.registerMetric(messagesCount);
async function main() { async function main() {
const environment = await getEnvironment(); const environment = await getEnvironment();
constMetricLabels.abacus_deployment = environment;
const coreConfig = getCoreEnvironmentConfig(environment); const coreConfig = getCoreEnvironmentConfig(environment);
const app = await getApp(coreConfig); const app = await getApp(coreConfig);
const chains = app.chains() as Chains[]; const chains = app.chains() as Chains[];
@ -26,21 +50,30 @@ async function main() {
const sources = chains.filter((chain) => !skip || !skip.includes(chain)); const sources = chains.filter((chain) => !skip || !skip.includes(chain));
for (const source of sources) { for (const source of sources) {
for (const destination of sources.slice().filter((d) => d !== source)) { for (const destination of sources.filter((d) => d !== source)) {
const labels = {
origin: source,
remote: destination,
...constMetricLabels,
};
try { try {
await sendMessage(app, source, destination); await sendMessage(app, source, destination);
messagesCount.labels({ ...labels, status: 'success' }).inc();
} catch (err) { } catch (err) {
console.error( console.error(
`Error sending message from ${source} to ${destination}, continuing...`, `Error sending message from ${source} to ${destination}, continuing...`,
`${err}`.replaceAll('\n', ' ## '), `${err}`.replaceAll('\n', ' ## '),
); );
failureOccurred = true; failureOccurred = true;
messagesCount.labels({ ...labels, status: 'failure' }).inc();
} }
// Sleep 500ms to avoid race conditions where nonces are reused // Sleep 500ms to avoid race conditions where nonces are reused
await sleep(500); await sleep(500);
} }
} }
await submitMetrics(metricsRegister);
if (failureOccurred) { if (failureOccurred) {
console.error('Failure occurred at least once'); console.error('Failure occurred at least once');
process.exit(1); process.exit(1);

@ -8,6 +8,7 @@ export interface HelloWorldKathyConfig<Chain extends ChainName> {
runEnv: string; runEnv: string;
namespace: string; namespace: string;
chainsToSkip: Chain[]; chainsToSkip: Chain[];
prometheusPushGateway: string;
} }
export interface HelloWorldConfig<Chain extends ChainName> { export interface HelloWorldConfig<Chain extends ChainName> {

@ -40,6 +40,9 @@ function getHelloworldKathyHelmValues<Chain extends ChainName>(
repository: kathyConfig.docker.repo, repository: kathyConfig.docker.repo,
tag: kathyConfig.docker.tag, tag: kathyConfig.docker.tag,
}, },
infra: {
prometheusPushGateway: kathyConfig.prometheusPushGateway,
},
}; };
return helmifyValues(values); return helmifyValues(values);

@ -0,0 +1,27 @@
import { Pushgateway, Registry } from 'prom-client';
function getPushGateway(register: Registry): Pushgateway | null {
const gatewayAddr = process.env['PROMETHEUS_PUSH_GATEWAY'];
if (gatewayAddr) {
return new Pushgateway(gatewayAddr, [], register);
} else {
console.warn(
'Prometheus push gateway address was not defined; not publishing metrics.',
);
return null;
}
}
export async function submitMetrics(register: Registry) {
const gateway = getPushGateway(register);
if (!gateway) return;
const { resp } = await gateway.push({ jobName: 'kathy' });
const statusCode =
typeof resp == 'object' && resp != null && 'statusCode' in resp
? (resp as any).statusCode
: 'unknown';
console.log(
`Prometheus metrics pushed to PushGateway with status ${statusCode}`,
);
}
Loading…
Cancel
Save