@chainsafe/libp2p-gossipsub
Version:
A typescript implementation of gossipsub
456 lines • 16.7 kB
TypeScript
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<TopicStr, TopicLabel>;
export declare enum MessageSource {
forward = "forward",
publish = "publish"
}
type NoLabels = Record<string, never>;
type LabelsGeneric = Record<string, string | number>;
type LabelKeys<Labels extends LabelsGeneric> = Extract<keyof Labels, string>;
interface CollectFn<Labels extends LabelsGeneric> {
(metric: Gauge<Labels>): void;
}
export interface Gauge<Labels extends LabelsGeneric = NoLabels> {
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<Labels>): void;
}
export interface Histogram<Labels extends LabelsGeneric = NoLabels> {
startTimer(): () => void;
observe: NoLabels extends Labels ? (value: number) => void : (labels: Labels, value: number) => void;
reset(): void;
}
export interface AvgMinMax<Labels extends LabelsGeneric = NoLabels> {
set: NoLabels extends Labels ? (values: number[]) => void : (labels: Labels, values: number[]) => void;
}
export type GaugeConfig<Labels extends LabelsGeneric> = {
name: string;
help: string;
} & (NoLabels extends Labels ? {
labelNames?: never;
} : {
labelNames: [LabelKeys<Labels>, ...Array<LabelKeys<Labels>>];
});
export type HistogramConfig<Labels extends LabelsGeneric> = GaugeConfig<Labels> & {
buckets?: number[];
};
export type AvgMinMaxConfig<Labels extends LabelsGeneric> = GaugeConfig<Labels>;
export interface MetricsRegister {
gauge<Labels extends LabelsGeneric = NoLabels>(config: GaugeConfig<Labels>): Gauge<Labels>;
histogram<Labels extends LabelsGeneric = NoLabels>(config: HistogramConfig<Labels>): Histogram<Labels>;
avgMinMax<Labels extends LabelsGeneric = NoLabels>(config: AvgMinMaxConfig<Labels>): AvgMinMax<Labels>;
}
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<ScoreThreshold, number>;
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<T> {
p1w: T;
p2w: T;
p3w: T;
p3bw: T;
p4w: T;
}
export interface ScoreWeights<T> {
byTopic: Map<TopicLabel, TopicScoreWeights<T>>;
p5w: T;
p6w: T;
p7w: T;
score: T;
}
export type Metrics = ReturnType<typeof getMetrics>;
/**
* 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<NoLabels>;
/** Heartbeat run took longer than heartbeat interval so next is skipped */
heartbeatSkipped: Gauge<NoLabels>;
/**
* 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<NoLabels>;
asyncValidationUnknownFirstSeen: Gauge<NoLabels>;
peerReadStreamError: Gauge<NoLabels>;
rpcRecvBytes: Gauge<NoLabels>;
rpcRecvCount: Gauge<NoLabels>;
rpcRecvSubscription: Gauge<NoLabels>;
rpcRecvMessage: Gauge<NoLabels>;
rpcRecvControl: Gauge<NoLabels>;
rpcRecvIHave: Gauge<NoLabels>;
rpcRecvIWant: Gauge<NoLabels>;
rpcRecvGraft: Gauge<NoLabels>;
rpcRecvPrune: Gauge<NoLabels>;
rpcDataError: Gauge<NoLabels>;
rpcRecvError: Gauge<NoLabels>;
/** Total count of RPC dropped because acceptFrom() == false */
rpcRecvNotAccepted: Gauge<NoLabels>;
rpcSentBytes: Gauge<NoLabels>;
rpcSentCount: Gauge<NoLabels>;
rpcSentSubscription: Gauge<NoLabels>;
rpcSentMessage: Gauge<NoLabels>;
rpcSentControl: Gauge<NoLabels>;
rpcSentIHave: Gauge<NoLabels>;
rpcSentIWant: Gauge<NoLabels>;
rpcSentGraft: Gauge<NoLabels>;
rpcSentPrune: Gauge<NoLabels>;
rpcSentIDontWant: Gauge<NoLabels>;
/** 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<NoLabels>;
/** Total times score() call actually computed computeScore(), no cache */
scoreFnRuns: Gauge<NoLabels>;
scoreCachedDelta: Histogram<NoLabels>;
/** Current count of peers by score threshold */
peersByScoreThreshold: Gauge<{
threshold: ScoreThreshold;
}>;
score: AvgMinMax<NoLabels>;
/**
* 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<NoLabels>;
/** 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<NoLabels>;
/** Total received IDONTWANT messages */
idontwantRcvMsgids: Gauge<NoLabels>;
/** Total received IDONTWANT messageIDs that we don't have */
idontwantRcvDonthaveMsgids: Gauge<NoLabels>;
iwantPromiseStarted: Gauge<NoLabels>;
/** Total count of resolved IWANT promises */
iwantPromiseResolved: Gauge<NoLabels>;
/** Total count of resolved IWANT promises from duplicate messages */
iwantPromiseResolvedFromDuplicate: Gauge<NoLabels>;
/** Total count of peers we have asked IWANT promises that are resolved */
iwantPromiseResolvedPeers: Gauge<NoLabels>;
iwantPromiseBroken: Gauge<NoLabels>;
iwantMessagePruned: Gauge<NoLabels>;
/** Histogram of delivery time of resolved IWANT promises */
iwantPromiseDeliveryTime: Histogram<NoLabels>;
iwantPromiseUntracked: Gauge<NoLabels>;
/** Backoff time */
connectedPeersBackoffSec: Histogram<NoLabels>;
/** Unbounded cache sizes */
cacheSize: Gauge<{
cache: string;
}>;
/** Current mcache msg count */
mcacheSize: Gauge<NoLabels>;
mcacheNotValidatedCount: Gauge<NoLabels>;
fastMsgIdCacheCollision: Gauge<NoLabels>;
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<TopicStr, number>, 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<number[]>): void;
registerScorePerMesh(mesh: Map<TopicStr, Set<PeerIdStr>>, scoreByPeer: Map<PeerIdStr, number>): void;
};
export {};
//# sourceMappingURL=metrics.d.ts.map