network/src/ipfs/loadBalancerService.ts
2025-05-16 11:56:23 +03:00

113 lines
2.9 KiB
TypeScript

import * as ipfsService from './ipfsService';
import { config } from '../config';
import { createServiceLogger } from '../utils/logger';
const logger = createServiceLogger('LOAD_BALANCER');
// Track last peer chosen for round-robin strategy
let lastPeerIndex = -1;
// Type definitions
export interface PeerInfo {
peerId: string;
load: number;
publicAddress: string;
}
export interface PeerStatus extends PeerInfo {
lastSeen: number;
}
export interface NodeStatus {
fingerprint: string;
peerCount: number;
isHealthy: boolean;
}
type LoadBalancerStrategy = 'leastLoaded' | 'roundRobin' | 'random';
/**
* Strategies for peer selection
*/
const strategies = {
leastLoaded: (peers: PeerStatus[]): PeerStatus => {
return peers.reduce((min, current) => (current.load < min.load ? current : min), peers[0]);
},
roundRobin: (peers: PeerStatus[]): PeerStatus => {
lastPeerIndex = (lastPeerIndex + 1) % peers.length;
return peers[lastPeerIndex];
},
random: (peers: PeerStatus[]): PeerStatus => {
const randomIndex = Math.floor(Math.random() * peers.length);
return peers[randomIndex];
},
};
/**
* Get the optimal peer based on the configured load balancing strategy
*/
export const getOptimalPeer = (): PeerInfo | null => {
const connectedPeers = ipfsService.getConnectedPeers();
if (connectedPeers.size === 0) {
logger.info('No peers available for load balancing');
return null;
}
// Convert Map to Array for easier manipulation
const peersArray = Array.from(connectedPeers.entries()).map(([peerId, data]) => ({
peerId,
load: data.load,
lastSeen: data.lastSeen,
publicAddress: data.publicAddress,
}));
// Apply the selected load balancing strategy
const strategy = config.loadBalancer.strategy as LoadBalancerStrategy;
let selectedPeer;
// Select strategy function or default to least loaded
const strategyFn = strategies[strategy] || strategies.leastLoaded;
selectedPeer = strategyFn(peersArray);
logger.info(
`Selected peer (${strategy}): ${selectedPeer.peerId.substring(0, 15)}... with load ${selectedPeer.load}%`,
);
return {
peerId: selectedPeer.peerId,
load: selectedPeer.load,
publicAddress: selectedPeer.publicAddress,
};
};
/**
* Get all available peers with their load information
*/
export const getAllPeers = (): PeerStatus[] => {
const connectedPeers = ipfsService.getConnectedPeers();
return Array.from(connectedPeers.entries()).map(([peerId, data]) => ({
peerId,
load: data.load,
lastSeen: data.lastSeen,
publicAddress: data.publicAddress,
}));
};
/**
* Get information about the current node's load
*/
export const getNodeStatus = (): NodeStatus => {
const connectedPeers = ipfsService.getConnectedPeers();
return {
fingerprint: config.env.fingerprint,
peerCount: connectedPeers.size,
isHealthy: true,
};
};
export default { getOptimalPeer, getAllPeers, getNodeStatus };