Initial Commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Super-peer topology — well-resourced "super peers" form a full
|
||||
* mesh among themselves and accept connections from any peer; ordinary
|
||||
* peers connect to a bounded number of super peers (default 3).
|
||||
*/
|
||||
|
||||
import type { PeerInfo } from "../../types/peer.types.js";
|
||||
import {
|
||||
type AdmitDecision,
|
||||
pickLowestScore,
|
||||
type TopologySelectOptions,
|
||||
type TopologyStrategy,
|
||||
} from "./topology-strategy.js";
|
||||
|
||||
export interface SuperPeerStrategyOptions {
|
||||
/** Whether this node is itself a super peer. */
|
||||
readonly selfIsSuperPeer: boolean;
|
||||
/** Set of peer ids known to be super peers. */
|
||||
readonly superPeerIds: ReadonlySet<string>;
|
||||
/** Cap on outbound super-peer connections for non-super peers. */
|
||||
readonly targetSuperPeers?: number;
|
||||
}
|
||||
|
||||
const DEFAULT_TARGET_SUPER_PEERS = 3;
|
||||
|
||||
export class SuperPeerStrategy implements TopologyStrategy {
|
||||
public readonly name = "super-peer" as const;
|
||||
private readonly selfIsSuperPeer: boolean;
|
||||
private readonly superPeerIds: ReadonlySet<string>;
|
||||
private readonly targetSuperPeers: number;
|
||||
|
||||
public constructor(opts: SuperPeerStrategyOptions) {
|
||||
this.selfIsSuperPeer = opts.selfIsSuperPeer;
|
||||
this.superPeerIds = opts.superPeerIds;
|
||||
this.targetSuperPeers =
|
||||
opts.targetSuperPeers ?? DEFAULT_TARGET_SUPER_PEERS;
|
||||
}
|
||||
|
||||
public selectConnections(
|
||||
currentPeers: readonly PeerInfo[],
|
||||
newPeer: PeerInfo,
|
||||
options: TopologySelectOptions
|
||||
): readonly PeerInfo[] {
|
||||
if (newPeer.id === options.selfPeerId) return [];
|
||||
if (this.selfIsSuperPeer) {
|
||||
if (currentPeers.length >= options.maxPeers) return [];
|
||||
return [newPeer];
|
||||
}
|
||||
if (!this.superPeerIds.has(newPeer.id)) return [];
|
||||
const connectedSuperPeers = currentPeers.filter((p) =>
|
||||
this.superPeerIds.has(p.id)
|
||||
).length;
|
||||
if (connectedSuperPeers >= this.targetSuperPeers) return [];
|
||||
if (currentPeers.length >= options.maxPeers) return [];
|
||||
return [newPeer];
|
||||
}
|
||||
|
||||
public admitOrEvict(
|
||||
currentPeers: readonly PeerInfo[],
|
||||
newPeer: PeerInfo,
|
||||
options: TopologySelectOptions
|
||||
): AdmitDecision {
|
||||
if (currentPeers.some((p) => p.id === newPeer.id)) {
|
||||
return { admit: false };
|
||||
}
|
||||
if (this.selfIsSuperPeer) {
|
||||
if (currentPeers.length < options.maxPeers) {
|
||||
return { admit: true };
|
||||
}
|
||||
const evict = pickLowestScore(
|
||||
currentPeers.filter((p) => !this.superPeerIds.has(p.id)),
|
||||
options.metricsFor
|
||||
);
|
||||
if (evict === undefined) return { admit: false };
|
||||
return { admit: true, evict };
|
||||
}
|
||||
if (!this.superPeerIds.has(newPeer.id)) return { admit: false };
|
||||
const connectedSuperPeers = currentPeers.filter((p) =>
|
||||
this.superPeerIds.has(p.id)
|
||||
);
|
||||
if (
|
||||
connectedSuperPeers.length < this.targetSuperPeers &&
|
||||
currentPeers.length < options.maxPeers
|
||||
) {
|
||||
return { admit: true };
|
||||
}
|
||||
return { admit: false };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user