Merge pull request #6185 from mempool/mononaut/ssr-monitoring

This commit is contained in:
wiz 2025-12-26 23:21:24 -10:00 committed by GitHub
commit 389b91eeec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 71 additions and 6 deletions

View file

@ -3,9 +3,12 @@ import path from 'path';
import os from 'os';
import { IBackendInfo } from '../mempool.interfaces';
import config from '../config';
import bitcoinClient from './bitcoin/bitcoin-client';
import logger from '../logger';
class BackendInfo {
private backendInfo: IBackendInfo;
private timer;
constructor() {
// This file is created by ./fetch-version.ts during building
@ -26,7 +29,22 @@ class BackendInfo {
gitCommit: versionInfo.gitCommit,
lightning: config.LIGHTNING.ENABLED,
backend: config.MEMPOOL.BACKEND,
coreVersion: '?',
};
this.timer = setInterval(async () => {
await this.$updateCoreVersion();
}, 10 * 60 * 1000); // every 10 minutes
this.$updateCoreVersion(); // starting immediately
}
private async $updateCoreVersion(): Promise<void> {
try {
const networkInfo = await bitcoinClient.getNetworkInfo();
this.backendInfo.coreVersion = networkInfo.subversion;
} catch (e) {
logger.err(`Exception in $updateCoreVersion. Reason: ${(e instanceof Error ? e.message : e)}`);
}
}
public getBackendInfo(): IBackendInfo {

View file

@ -28,6 +28,8 @@ interface FailoverHost {
hybrid?: string,
backend?: string,
electrs?: string,
ssr?: string,
core?: string,
lastUpdated: number,
}
}
@ -35,7 +37,7 @@ interface FailoverHost {
class FailoverRouter {
activeHost: FailoverHost;
fallbackHost: FailoverHost;
maxSlippage: number = config.ESPLORA.MAX_BEHIND_TIP ?? 2;
maxSlippage: number = config.ESPLORA.MAX_BEHIND_TIP ?? (Common.isLiquid() ? 8 : 2);
maxHeight: number = 0;
hosts: FailoverHost[];
multihost: boolean;
@ -145,7 +147,8 @@ class FailoverRouter {
if (Date.now() - host.hashes.lastUpdated > this.gitHashInterval) {
await Promise.all([
this.$updateFrontendGitHash(host),
this.$updateBackendGitHash(host),
this.$updateBackendVersions(host),
this.$updateSSRGitHash(host),
config.MEMPOOL.OFFICIAL ? this.$updateHybridGitHash(host) : Promise.resolve(),
]);
host.hashes.lastUpdated = Date.now();
@ -301,18 +304,33 @@ class FailoverRouter {
}
}
private async $updateBackendGitHash(host: FailoverHost): Promise<void> {
private async $updateBackendVersions(host: FailoverHost): Promise<void> {
try {
const url = `${host.publicDomain}/api/v1/backend-info`;
const response = await this.pollConnection.get<any>(url, { timeout: config.ESPLORA.FALLBACK_TIMEOUT });
if (response.data?.gitCommit) {
host.hashes.backend = response.data.gitCommit;
}
if (response.data?.coreVersion) {
host.hashes.core = response.data.coreVersion;
}
} catch (e) {
// failed to get backend build hash - do nothing
}
}
private async $updateSSRGitHash(host: FailoverHost): Promise<void> {
try {
const url = `${host.publicDomain}/ssr/api/status`;
const response = await this.pollConnection.get<any>(url, { timeout: config.ESPLORA.FALLBACK_TIMEOUT });
if (response.data?.gitHash) {
host.hashes.ssr = response.data.gitHash;
}
} catch (e) {
// failed to get ssr build hash - do nothing
}
}
// returns the public mempool domain corresponding to an esplora server url
// (a bit of a hack to avoid manually specifying frontend & backend URLs for each esplora server)
private extractPublicDomain(url: string): string {

View file

@ -504,9 +504,35 @@ export interface IBackendInfo {
gitCommit: string;
version: string;
lightning: boolean;
coreVersion: string;
backend: 'esplora' | 'electrum' | 'none';
}
export interface INetworkInfo {
version: number;
subversion: string;
protocolversion: number;
localservices: string;
localrelay: boolean;
timeoffset: number;
networkactive: boolean;
networks: {
name: string;
limited: boolean;
reachable: boolean;
proxy: string;
proxy_randomize_credentials: boolean;
}[];
relayfee: number;
incrementalfee: number;
localaddresses: {
address: string;
port: number;
score: number;
}[];
warnings: string;
}
export interface IDifficultyAdjustment {
progressPercent: number;
difficultyChange: number;

View file

@ -23,6 +23,8 @@
<th class="frontend only-large">Front</th>
<th class="backend only-large">Back</th>
<th class="electrs only-large">Electrs</th>
<th class="ssr only-large">SSR</th>
<th class="core only-large">Core</th>
</tr>
<tr *ngFor="let host of hosts; let i = index; trackBy: trackByFn">
<td class="rank">{{ i + 1 }}</td>
@ -32,9 +34,9 @@
<td class="rtt only-small">{{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="rtt only-large">{{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="height">{{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '')) }}</td>
<ng-container *ngFor="let type of ['hybrid', 'frontend', 'backend', 'electrs']">
<td class="{{type}} only-large" [style.background-color]="host.hashes?.[type] ? '#' + host.hashes[type].slice(0, 6) : ''">
@if (host.hashes?.[type]) {
<ng-container *ngFor="let type of ['hybrid', 'frontend', 'backend', 'electrs', 'ssr', 'core']">
<td class="{{type}} only-large" [style.background-color]="type !== 'core' && host.hashes?.[type] ? '#' + host.hashes[type].slice(0, 6) : ''">
@if (type !== 'core' && host.hashes?.[type]) {
<a [style.color]="'white'" href="https://github.com/mempool/{{repoMap[type]}}/commit/{{ host.hashes[type] }}" target="_blank">{{ host.hashes[type].slice(0, 8) || '?' }}</a>
} @else {
<span>?</span>

View file

@ -23,6 +23,7 @@ export class ServerHealthComponent implements OnInit {
hybrid: 'mempool.space',
backend: 'mempool',
electrs: 'electrs',
ssr: 'mempool.space',
};
constructor(