Websocket subscription for fallback server health status

This commit is contained in:
Mononaut 2024-03-03 20:29:54 +00:00
parent 8405e5ff07
commit f63f1b1773
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
4 changed files with 48 additions and 5 deletions

View File

@ -29,6 +29,7 @@ export interface AbstractBitcoinApi {
$getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise<IEsploraApi.Outspend[]>; $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise<IEsploraApi.Outspend[]>;
startHealthChecks(): void; startHealthChecks(): void;
getHealthStatus(): HealthCheckHost[];
} }
export interface BitcoinRpcCredentials { export interface BitcoinRpcCredentials {
host: string; host: string;
@ -38,3 +39,14 @@ export interface BitcoinRpcCredentials {
timeout: number; timeout: number;
cookie?: string; cookie?: string;
} }
export interface HealthCheckHost {
host: string;
active: boolean;
rtt: number;
latestHeight: number;
socket: boolean;
outOfSync: boolean;
unreachable: boolean;
checked: boolean;
}

View File

@ -1,5 +1,5 @@
import * as bitcoinjs from 'bitcoinjs-lib'; import * as bitcoinjs from 'bitcoinjs-lib';
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory'; import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory';
import { IBitcoinApi } from './bitcoin-api.interface'; import { IBitcoinApi } from './bitcoin-api.interface';
import { IEsploraApi } from './esplora-api.interface'; import { IEsploraApi } from './esplora-api.interface';
import blocks from '../blocks'; import blocks from '../blocks';
@ -382,6 +382,10 @@ class BitcoinApi implements AbstractBitcoinApi {
} }
public startHealthChecks(): void {}; public startHealthChecks(): void {};
public getHealthStatus() {
return [];
}
} }
export default BitcoinApi; export default BitcoinApi;

View File

@ -1,7 +1,7 @@
import config from '../../config'; import config from '../../config';
import axios, { AxiosResponse } from 'axios'; import axios from 'axios';
import http from 'http'; import http from 'http';
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory'; import { AbstractBitcoinApi, HealthCheckHost } from './bitcoin-api-abstract-factory';
import { IEsploraApi } from './esplora-api.interface'; import { IEsploraApi } from './esplora-api.interface';
import logger from '../../logger'; import logger from '../../logger';
import { Common } from '../common'; import { Common } from '../common';
@ -157,7 +157,7 @@ class FailoverRouter {
} }
// sort hosts by connection quality, and update default fallback // sort hosts by connection quality, and update default fallback
private sortHosts(): FailoverHost[] { public sortHosts(): FailoverHost[] {
// sort by connection quality // sort by connection quality
return this.hosts.slice().sort((a, b) => { return this.hosts.slice().sort((a, b) => {
if ((a.unreachable || a.outOfSync) === (b.unreachable || b.outOfSync)) { if ((a.unreachable || a.outOfSync) === (b.unreachable || b.outOfSync)) {
@ -342,6 +342,19 @@ class ElectrsApi implements AbstractBitcoinApi {
public startHealthChecks(): void { public startHealthChecks(): void {
this.failoverRouter.startHealthChecks(); this.failoverRouter.startHealthChecks();
} }
public getHealthStatus(): HealthCheckHost[] {
return this.failoverRouter.sortHosts().map(host => ({
host: host.host,
active: host === this.failoverRouter.activeHost,
rtt: host.rtt,
latestHeight: host.latestHeight || 0,
socket: !!host.socket,
outOfSync: !!host.outOfSync,
unreachable: !!host.unreachable,
checked: !!host.checked,
}));
}
} }
export default ElectrsApi; export default ElectrsApi;

View File

@ -26,6 +26,7 @@ import mempool from './mempool';
import statistics from './statistics/statistics'; import statistics from './statistics/statistics';
import accelerationCosts from './acceleration'; import accelerationCosts from './acceleration';
import accelerationRepository from '../repositories/AccelerationRepository'; import accelerationRepository from '../repositories/AccelerationRepository';
import bitcoinApi from './bitcoin/bitcoin-api-factory';
interface AddressTransactions { interface AddressTransactions {
mempool: MempoolTransactionExtended[], mempool: MempoolTransactionExtended[],
@ -39,6 +40,7 @@ const wantable = [
'mempool-blocks', 'mempool-blocks',
'live-2h-chart', 'live-2h-chart',
'stats', 'stats',
'tomahawk',
]; ];
class WebsocketHandler { class WebsocketHandler {
@ -123,7 +125,7 @@ class WebsocketHandler {
for (const sub of wantable) { for (const sub of wantable) {
const key = `want-${sub}`; const key = `want-${sub}`;
const wants = parsedMessage.data.includes(sub); const wants = parsedMessage.data.includes(sub);
if (wants && client['wants'] && !client[key]) { if (wants && !client[key]) {
wantNow[key] = true; wantNow[key] = true;
} }
client[key] = wants; client[key] = wants;
@ -147,6 +149,10 @@ class WebsocketHandler {
response['da'] = this.socketData['da']; response['da'] = this.socketData['da'];
} }
if (wantNow['want-tomahawk'] && config.MEMPOOL.BACKEND === 'esplora' && config.ESPLORA.FALLBACK?.length) {
response['tomahawk'] = JSON.stringify(bitcoinApi.getHealthStatus());
}
if (parsedMessage && parsedMessage['track-tx']) { if (parsedMessage && parsedMessage['track-tx']) {
if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) { if (/^[a-fA-F0-9]{64}$/.test(parsedMessage['track-tx'])) {
client['track-tx'] = parsedMessage['track-tx']; client['track-tx'] = parsedMessage['track-tx'];
@ -546,6 +552,10 @@ class WebsocketHandler {
response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks); response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks);
} }
if (client['want-tomahawk'] && config.MEMPOOL.BACKEND === 'esplora' && config.ESPLORA.FALLBACK?.length) {
response['tomahawk'] = getCachedResponse('tomahawk', bitcoinApi.getHealthStatus());
}
if (client['track-mempool-tx']) { if (client['track-mempool-tx']) {
const tx = newTransactions.find((t) => t.txid === client['track-mempool-tx']); const tx = newTransactions.find((t) => t.txid === client['track-mempool-tx']);
if (tx) { if (tx) {
@ -909,6 +919,10 @@ class WebsocketHandler {
response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks); response['mempool-blocks'] = getCachedResponse('mempool-blocks', mBlocks);
} }
if (client['want-tomahawk'] && config.MEMPOOL.BACKEND === 'esplora' && config.ESPLORA.FALLBACK?.length) {
response['tomahawk'] = getCachedResponse('tomahawk', bitcoinApi.getHealthStatus());
}
if (client['track-tx']) { if (client['track-tx']) {
const trackTxid = client['track-tx']; const trackTxid = client['track-tx'];
if (trackTxid && confirmedTxids[trackTxid]) { if (trackTxid && confirmedTxids[trackTxid]) {