import { TopicValidatorResult } from '@libp2p/interface'; import { MessageStatus, type PeerIdStr, RejectReason, type RejectReasonObj, type TopicStr, type ValidateError } from './types.js'; import type { RPC } from './message/rpc.js'; import type { PeerScoreThresholds } from './score/peer-score-thresholds.js'; /** Topic label as provided in `topicStrToLabel` */ export type TopicLabel = string; export type TopicStrToLabel = Map; export declare enum MessageSource { forward = "forward", publish = "publish" } type NoLabels = Record; type LabelsGeneric = Record; type LabelKeys = Extract; interface CollectFn { (metric: Gauge): void; } export interface Gauge { inc: NoLabels extends Labels ? (value?: number) => void : (labels: Labels, value?: number) => void; set: NoLabels extends Labels ? (value: number) => void : (labels: Labels, value: number) => void; addCollect(collectFn: CollectFn): void; } export interface Histogram { startTimer(): () => void; observe: NoLabels extends Labels ? (value: number) => void : (labels: Labels, value: number) => void; reset(): void; } export interface AvgMinMax { set: NoLabels extends Labels ? (values: number[]) => void : (labels: Labels, values: number[]) => void; } export type GaugeConfig = { name: string; help: string; } & (NoLabels extends Labels ? { labelNames?: never; } : { labelNames: [LabelKeys, ...Array>]; }); export type HistogramConfig = GaugeConfig & { buckets?: number[]; }; export type AvgMinMaxConfig = GaugeConfig; export interface MetricsRegister { gauge(config: GaugeConfig): Gauge; histogram(config: HistogramConfig): Histogram; avgMinMax(config: AvgMinMaxConfig): AvgMinMax; } export declare enum InclusionReason { /** Peer was a fanaout peer. */ Fanout = "fanout", /** Included from random selection. */ Random = "random", /** Peer subscribed. */ Subscribed = "subscribed", /** On heartbeat, peer was included to fill the outbound quota. */ Outbound = "outbound", /** On heartbeat, not enough peers in mesh */ NotEnough = "not_enough", /** On heartbeat opportunistic grafting due to low mesh score */ Opportunistic = "opportunistic" } export declare enum ChurnReason { Dc = "disconnected", BadScore = "bad_score", Prune = "prune", Excess = "excess" } export declare enum ScorePenalty { GraftBackoff = "graft_backoff", BrokenPromise = "broken_promise", MessageDeficit = "message_deficit", IPColocation = "IP_colocation" } export declare enum IHaveIgnoreReason { LowScore = "low_score", MaxIhave = "max_ihave", MaxIasked = "max_iasked" } export declare enum ScoreThreshold { graylist = "graylist", publish = "publish", gossip = "gossip", mesh = "mesh" } export type PeersByScoreThreshold = Record; export interface ToSendGroupCount { direct: number; floodsub: number; mesh: number; fanout: number; } export interface ToAddGroupCount { fanout: number; random: number; } export type PromiseDeliveredStats = { expired: false; requestedCount: number; maxDeliverMs: number; } | { expired: true; maxDeliverMs: number; }; export interface TopicScoreWeights { p1w: T; p2w: T; p3w: T; p3bw: T; p4w: T; } export interface ScoreWeights { byTopic: Map>; p5w: T; p6w: T; p7w: T; score: T; } export type Metrics = ReturnType; /** * A collection of metrics used throughout the Gossipsub behaviour. * NOTE: except for special reasons, do not add more than 1 label for frequent metrics, * there's a performance penalty as of June 2023. */ export declare function getMetrics(register: MetricsRegister, topicStrToLabel: TopicStrToLabel, opts: { gossipPromiseExpireSec: number; behaviourPenaltyThreshold: number; maxMeshMessageDeliveriesWindowSec: number; }): { protocolsEnabled: Gauge<{ protocol: string; }>; /** * Status of our subscription to this topic. This metric allows analyzing other topic metrics * filtered by our current subscription status. * = rust-libp2p `topic_subscription_status` */ topicSubscriptionStatus: Gauge<{ topicStr: TopicStr; }>; /** Number of peers subscribed to each topic. This allows us to analyze a topic's behaviour * regardless of our subscription status. */ topicPeersCount: Gauge<{ topicStr: TopicStr; }>; /** * Number of peers in our mesh. This metric should be updated with the count of peers for a * topic in the mesh regardless of inclusion and churn events. * = rust-libp2p `mesh_peer_counts` */ meshPeerCounts: Gauge<{ topicStr: TopicStr; }>; /** * Number of times we include peers in a topic mesh for different reasons. * = rust-libp2p `mesh_peer_inclusion_events` */ meshPeerInclusionEventsFanout: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsRandom: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsSubscribed: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsOutbound: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsNotEnough: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsOpportunistic: Gauge<{ topic: TopicLabel; }>; meshPeerInclusionEventsUnknown: Gauge<{ topic: TopicLabel; }>; /** * Number of times we remove peers in a topic mesh for different reasons. * = rust-libp2p `mesh_peer_churn_events` */ meshPeerChurnEventsDisconnected: Gauge<{ topic: TopicLabel; }>; meshPeerChurnEventsBadScore: Gauge<{ topic: TopicLabel; }>; meshPeerChurnEventsPrune: Gauge<{ topic: TopicLabel; }>; meshPeerChurnEventsExcess: Gauge<{ topic: TopicLabel; }>; meshPeerChurnEventsUnknown: Gauge<{ topic: TopicLabel; }>; /** * Gossipsub supports floodsub, gossipsub v1.0, v1.1, and v1.2. Peers are classified based * on which protocol they support. This metric keeps track of the number of peers that are * connected of each type. */ peersPerProtocol: Gauge<{ protocol: string; }>; /** The time it takes to complete one iteration of the heartbeat. */ heartbeatDuration: Histogram; /** Heartbeat run took longer than heartbeat interval so next is skipped */ heartbeatSkipped: Gauge; /** * Message validation results for each topic. * Invalid == Reject? * = rust-libp2p `invalid_messages`, `accepted_messages`, `ignored_messages`, `rejected_messages` */ acceptedMessagesTotal: Gauge<{ topic: TopicLabel; }>; ignoredMessagesTotal: Gauge<{ topic: TopicLabel; }>; rejectedMessagesTotal: Gauge<{ topic: TopicLabel; }>; unknownValidationResultsTotal: Gauge<{ topic: TopicLabel; }>; /** * When the user validates a message, it tries to re propagate it to its mesh peers. If the * message expires from the memcache before it can be validated, we count this a cache miss * and it is an indicator that the memcache size should be increased. * = rust-libp2p `mcache_misses` */ asyncValidationMcacheHit: Gauge<{ hit: 'hit' | 'miss'; }>; asyncValidationDelayFromFirstSeenSec: Histogram; asyncValidationUnknownFirstSeen: Gauge; peerReadStreamError: Gauge; rpcRecvBytes: Gauge; rpcRecvCount: Gauge; rpcRecvSubscription: Gauge; rpcRecvMessage: Gauge; rpcRecvControl: Gauge; rpcRecvIHave: Gauge; rpcRecvIWant: Gauge; rpcRecvGraft: Gauge; rpcRecvPrune: Gauge; rpcDataError: Gauge; rpcRecvError: Gauge; /** Total count of RPC dropped because acceptFrom() == false */ rpcRecvNotAccepted: Gauge; rpcSentBytes: Gauge; rpcSentCount: Gauge; rpcSentSubscription: Gauge; rpcSentMessage: Gauge; rpcSentControl: Gauge; rpcSentIHave: Gauge; rpcSentIWant: Gauge; rpcSentGraft: Gauge; rpcSentPrune: Gauge; rpcSentIDontWant: Gauge; /** Total count of msg published by topic */ msgPublishCount: Gauge<{ topic: TopicLabel; }>; /** Total count of peers that we publish a msg to */ msgPublishPeersByTopic: Gauge<{ topic: TopicLabel; }>; /** Total count of peers (by group) that we publish a msg to */ directPeersPublishedTotal: Gauge<{ topic: TopicLabel; }>; floodsubPeersPublishedTotal: Gauge<{ topic: TopicLabel; }>; meshPeersPublishedTotal: Gauge<{ topic: TopicLabel; }>; fanoutPeersPublishedTotal: Gauge<{ topic: TopicLabel; }>; /** Total count of msg publish data.length bytes */ msgPublishBytes: Gauge<{ topic: TopicLabel; }>; /** Total time in seconds to publish a message */ msgPublishTime: Histogram<{ topic: TopicLabel; }>; /** Total count of msg forwarded by topic */ msgForwardCount: Gauge<{ topic: TopicLabel; }>; /** Total count of peers that we forward a msg to */ msgForwardPeers: Gauge<{ topic: TopicLabel; }>; /** Total count of recv msgs before any validation */ msgReceivedPreValidation: Gauge<{ topic: TopicLabel; }>; /** Total count of recv msgs error */ msgReceivedError: Gauge<{ topic: TopicLabel; }>; /** Tracks distribution of recv msgs by duplicate, invalid, valid */ prevalidationInvalidTotal: Gauge<{ topic: TopicLabel; }>; prevalidationValidTotal: Gauge<{ topic: TopicLabel; }>; prevalidationDuplicateTotal: Gauge<{ topic: TopicLabel; }>; prevalidationUnknownTotal: Gauge<{ topic: TopicLabel; }>; /** Tracks specific reason of invalid */ msgReceivedInvalid: Gauge<{ error: RejectReason | ValidateError; }>; msgReceivedInvalidByTopic: Gauge<{ topic: TopicLabel; }>; /** Track duplicate message delivery time */ duplicateMsgDeliveryDelay: Histogram<{ topic: TopicLabel; }>; /** Total count of late msg delivery total by topic */ duplicateMsgLateDelivery: Gauge<{ topic: TopicLabel; }>; duplicateMsgIgnored: Gauge<{ topic: TopicLabel; }>; /** Total times score() is called */ scoreFnCalls: Gauge; /** Total times score() call actually computed computeScore(), no cache */ scoreFnRuns: Gauge; scoreCachedDelta: Histogram; /** Current count of peers by score threshold */ peersByScoreThreshold: Gauge<{ threshold: ScoreThreshold; }>; score: AvgMinMax; /** * Separate score weights * Need to use 2-label metrics in this case to debug the score weights **/ scoreWeights: AvgMinMax<{ topic?: string | undefined; p: string; }>; /** Histogram of the scores for each mesh topic. */ scorePerMesh: AvgMinMax<{ topic: TopicLabel; }>; /** A counter of the kind of penalties being applied to peers. */ scoringPenalties: Gauge<{ penalty: ScorePenalty; }>; behaviourPenalty: Histogram; /** Total received IHAVE messages that we ignore for some reason */ ihaveRcvIgnored: Gauge<{ reason: IHaveIgnoreReason; }>; /** Total received IHAVE messages by topic */ ihaveRcvMsgids: Gauge<{ topic: TopicLabel; }>; /** * Total messages per topic we don't have. Not actual requests. * The number of times we have decided that an IWANT control message is required for this * topic. A very high metric might indicate an underperforming network. * = rust-libp2p `topic_iwant_msgs` */ ihaveRcvNotSeenMsgids: Gauge<{ topic: TopicLabel; }>; /** Total received IWANT messages by topic */ iwantRcvMsgids: Gauge<{ topic: TopicLabel; }>; /** Total requested messageIDs that we don't have */ iwantRcvDonthaveMsgids: Gauge; /** Total received IDONTWANT messages */ idontwantRcvMsgids: Gauge; /** Total received IDONTWANT messageIDs that we don't have */ idontwantRcvDonthaveMsgids: Gauge; iwantPromiseStarted: Gauge; /** Total count of resolved IWANT promises */ iwantPromiseResolved: Gauge; /** Total count of resolved IWANT promises from duplicate messages */ iwantPromiseResolvedFromDuplicate: Gauge; /** Total count of peers we have asked IWANT promises that are resolved */ iwantPromiseResolvedPeers: Gauge; iwantPromiseBroken: Gauge; iwantMessagePruned: Gauge; /** Histogram of delivery time of resolved IWANT promises */ iwantPromiseDeliveryTime: Histogram; iwantPromiseUntracked: Gauge; /** Backoff time */ connectedPeersBackoffSec: Histogram; /** Unbounded cache sizes */ cacheSize: Gauge<{ cache: string; }>; /** Current mcache msg count */ mcacheSize: Gauge; mcacheNotValidatedCount: Gauge; fastMsgIdCacheCollision: Gauge; newConnectionCount: Gauge<{ status: string; }>; topicStrToLabel: TopicStrToLabel; toTopic(topicStr: TopicStr): TopicLabel; /** We joined a topic */ onJoin(topicStr: TopicStr): void; /** We left a topic */ onLeave(topicStr: TopicStr): void; /** Register the inclusion of peers in our mesh due to some reason. */ onAddToMesh(topicStr: TopicStr, reason: InclusionReason, count: number): void; /** Register the removal of peers in our mesh due to some reason */ onRemoveFromMesh(topicStr: TopicStr, reason: ChurnReason, count: number): void; /** * Update validation result to metrics * * @param messageRecord - null means the message's mcache record was not known at the time of acceptance report */ onReportValidation(messageRecord: { message: { topic: TopicStr; }; } | null, acceptance: TopicValidatorResult, firstSeenTimestampMs: number | null): void; /** * - in handle_graft() Penalty::GraftBackoff * - in apply_iwant_penalties() Penalty::BrokenPromise * - in metric_score() P3 Penalty::MessageDeficit * - in metric_score() P6 Penalty::IPColocation */ onScorePenalty(penalty: ScorePenalty): void; onIhaveRcv(topicStr: TopicStr, ihave: number, idonthave: number): void; onIwantRcv(iwantByTopic: Map, iwantDonthave: number): void; onIdontwantRcv(idontwant: number, idontwantDonthave: number): void; onForwardMsg(topicStr: TopicStr, tosendCount: number): void; onPublishMsg(topicStr: TopicStr, tosendGroupCount: ToSendGroupCount, tosendCount: number, dataLen: number, ms: number): void; onMsgRecvPreValidation(topicStr: TopicStr): void; onMsgRecvError(topicStr: TopicStr): void; onPrevalidationResult(topicStr: TopicStr, status: MessageStatus): void; onMsgRecvInvalid(topicStr: TopicStr, reason: RejectReasonObj): void; onDuplicateMsgDelivery(topicStr: TopicStr, deliveryDelayMs: number, isLateDelivery: boolean): void; onPublishDuplicateMsg(topicStr: TopicStr): void; onPeerReadStreamError(): void; onRpcRecvError(): void; onRpcDataError(): void; onRpcRecv(rpc: RPC, rpcBytes: number): void; onRpcSent(rpc: RPC, rpcBytes: number): void; registerScores(scores: number[], scoreThresholds: PeerScoreThresholds): void; registerScoreWeights(sw: ScoreWeights): void; registerScorePerMesh(mesh: Map>, scoreByPeer: Map): void; }; export {}; //# sourceMappingURL=metrics.d.ts.map