/** * @file Node health tracking for smart failover. * @license BSD-3-Clause-No-Military-License * * Tracks per-node, per-API health to enable intelligent failover decisions. * Nodes that fail for specific APIs are deprioritized for those APIs while * remaining available for others. Stale nodes (behind on head block) are * also deprioritized. */ export interface HealthTrackerOptions { /** * How long (ms) to deprioritize a node after consecutive failures. * Default: 30 seconds. */ nodeCooldownMs?: number; /** * How long (ms) to deprioritize a node for a specific API after failures. * Default: 60 seconds. */ apiCooldownMs?: number; /** * Number of consecutive failures before a node enters cooldown. * Default: 3. */ maxFailuresBeforeCooldown?: number; /** * Number of API-specific failures before deprioritizing for that API. * Default: 2. */ maxApiFailuresBeforeCooldown?: number; /** * How many blocks behind the best known head block a node can be * before being considered stale. Default: 30. */ staleBlockThreshold?: number; /** * How long (ms) head block data remains valid for staleness checks. * Default: 2 minutes. */ headBlockTtlMs?: number; /** * Default duration (ms) to skip a node after receiving a 429 response, * used when the server doesn't provide a Retry-After header. * Default: 10 seconds. */ defaultRateLimitMs?: number; } export declare class NodeHealthTracker { private health; private bestKnownHeadBlock; private bestKnownHeadBlockTime; private readonly nodeCooldownMs; private readonly apiCooldownMs; private readonly maxFailuresBeforeCooldown; private readonly maxApiFailuresBeforeCooldown; private readonly staleBlockThreshold; private readonly headBlockTtlMs; private readonly defaultRateLimitMs; constructor(options?: HealthTrackerOptions); private getOrCreate; /** * Record a successful call to a node for a specific API. * Clears consecutive failure counter and API-specific failures for this API. */ recordSuccess(node: string, api: string): void; /** * Record a network-level failure (timeout, connection refused, HTTP error). * Increments both the global consecutive failure counter and the API-specific counter. */ recordFailure(node: string, api: string): void; /** * Record that a node returned HTTP 429 (Too Many Requests). * The node will be skipped until the rate limit expires. * @param retryAfterSeconds Value from the Retry-After header, or undefined to use default. */ recordRateLimit(node: string, retryAfterSeconds?: number): void; /** * Check if a node is currently rate-limited (429 cooldown active). */ isRateLimited(node: string): boolean; /** * Record an API/plugin-specific failure (e.g. "method not found", "plugin not enabled"). * Only increments the per-API counter, NOT the global consecutive failure counter. * This prevents a node with a disabled plugin from being penalized for all APIs. */ recordApiFailure(node: string, api: string): void; private incrementApiFailure; /** * Update head block number for a node. * Called passively when get_dynamic_global_properties responses are observed. */ updateHeadBlock(node: string, headBlock: number): void; /** * Check if a node is considered healthy for a given API. */ isNodeHealthy(node: string, api?: string): boolean; /** * Return nodes ordered by health for a specific API call. * Healthy nodes come first (preserving original order), then unhealthy nodes as fallback. */ getOrderedNodes(allNodes: string[], api?: string): string[]; /** * Reset all health tracking data. */ reset(): void; /** * Get a snapshot of current health state for diagnostics. */ getHealthSnapshot(): Map; healthy: boolean; }>; }