Skip to content

Commit 57944fa

Browse files
achingbrainmaschad
andauthored
feat: add versions of peer lists/sets/maps that report their sizes (#2300)
Adds collections that report their sizes to the libp2p metrics service. Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com> --------- Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com>
1 parent 3bf6387 commit 57944fa

File tree

11 files changed

+607
-7
lines changed

11 files changed

+607
-7
lines changed

packages/peer-collections/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
},
5959
"devDependencies": {
6060
"@libp2p/peer-id-factory": "^4.0.0",
61-
"aegir": "^41.0.2"
61+
"@types/sinon": "^17.0.2",
62+
"aegir": "^41.0.2",
63+
"sinon": "^17.0.1",
64+
"sinon-ts": "^2.0.0"
6265
}
6366
}

packages/peer-collections/src/index.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*
66
* PeerIds cache stringified versions of themselves so this should be a cheap operation.
77
*
8+
* Tracked versions are also available which report their current size to the libp2p Metrics collector.
9+
*
810
* @example Peer lists
911
*
1012
* ```JavaScript
@@ -14,6 +16,18 @@
1416
* list.push(peerId)
1517
* ```
1618
*
19+
* @example Tracked peer lists
20+
*
21+
* * ```Typescript
22+
* import { trackedPeerList } from '@libp2p/peer-collections'
23+
* import { createLibp2p } from 'libp2p'
24+
*
25+
* const libp2p = await createLibp2p()
26+
*
27+
* const list = trackedPeerList({ name: 'my_metric_name', metrics: libp2p.metrics })
28+
* list.push(peerId)
29+
* ```
30+
*
1731
* @example Peer maps
1832
*
1933
* ```JavaScript
@@ -23,6 +37,18 @@
2337
* map.set(peerId, 'value')
2438
* ```
2539
*
40+
* @example Tracked peer maps
41+
*
42+
* * ```Typescript
43+
* import { trackedPeerMap } from '@libp2p/peer-collections'
44+
* import { createLibp2p } from 'libp2p'
45+
*
46+
* const libp2p = await createLibp2p()
47+
*
48+
* const list = trackedPeerMap({ name: 'my_metric_name', metrics: libp2p.metrics })
49+
* map.set(peerId, 'value')
50+
* ```
51+
*
2652
* @example Peer sets
2753
*
2854
* ```JavaScript
@@ -31,8 +57,24 @@
3157
* const set = peerSet()
3258
* set.add(peerId)
3359
* ```
60+
*
61+
* @example Tracked peer sets
62+
*
63+
* * ```Typescript
64+
* import { trackedPeerSet } from '@libp2p/peer-collections'
65+
* import { createLibp2p } from 'libp2p'
66+
*
67+
* const libp2p = await createLibp2p()
68+
*
69+
* const list = trackedPeerSet({ name: 'my_metric_name', metrics: libp2p.metrics })
70+
* map.add(peerId)
71+
* ```
3472
*/
3573

36-
export { PeerMap } from './map.js'
37-
export { PeerSet } from './set.js'
38-
export { PeerList } from './list.js'
74+
export { PeerMap, peerMap } from './map.js'
75+
export { PeerSet, peerSet } from './set.js'
76+
export { PeerList, peerList } from './list.js'
77+
78+
export { trackedPeerMap } from './tracked-map.js'
79+
export { trackedPeerSet } from './tracked-set.js'
80+
export { trackedPeerList } from './tracked-list.js'

packages/peer-collections/src/list.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { PeerId } from '@libp2p/interface'
2020
* ```
2121
*/
2222
export class PeerList {
23-
private readonly list: string[]
23+
private list: string[]
2424

2525
constructor (list?: PeerList | Iterable<PeerId>) {
2626
this.list = []
@@ -148,7 +148,15 @@ export class PeerList {
148148
return len
149149
}
150150

151+
clear (): void {
152+
this.list = []
153+
}
154+
151155
get length (): number {
152156
return this.list.length
153157
}
154158
}
159+
160+
export function peerList (): PeerList {
161+
return new PeerList()
162+
}

packages/peer-collections/src/map.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export class PeerMap <T> {
4040
this.map.clear()
4141
}
4242

43-
delete (peer: PeerId): void {
44-
this.map.delete(peer.toString())
43+
delete (peer: PeerId): boolean {
44+
return this.map.delete(peer.toString())
4545
}
4646

4747
entries (): IterableIterator<[PeerId, T]> {
@@ -88,3 +88,7 @@ export class PeerMap <T> {
8888
return this.map.size
8989
}
9090
}
91+
92+
export function peerMap <T> (): PeerMap<T> {
93+
return new PeerMap<T>()
94+
}

packages/peer-collections/src/set.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,7 @@ export class PeerSet {
122122
return output
123123
}
124124
}
125+
126+
export function peerSet (): PeerSet {
127+
return new PeerSet()
128+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { PeerList } from './list.js'
2+
import type { Metric, Metrics, PeerId } from '@libp2p/interface'
3+
4+
export interface TrackedPeerListInit {
5+
name: string
6+
metrics: Metrics
7+
}
8+
9+
class TrackedPeerList extends PeerList {
10+
private readonly metric: Metric
11+
12+
constructor (init: TrackedPeerListInit) {
13+
super()
14+
15+
const { name, metrics } = init
16+
17+
this.metric = metrics.registerMetric(name)
18+
this.updateComponentMetric()
19+
}
20+
21+
pop (): PeerId | undefined {
22+
const peerId = super.pop()
23+
this.updateComponentMetric()
24+
return peerId
25+
}
26+
27+
push (...peerIds: PeerId[]): void {
28+
super.push(...peerIds)
29+
this.updateComponentMetric()
30+
}
31+
32+
shift (): PeerId | undefined {
33+
const peerId = super.shift()
34+
this.updateComponentMetric()
35+
return peerId
36+
}
37+
38+
unshift (...peerIds: PeerId[]): number {
39+
const result = super.unshift(...peerIds)
40+
this.updateComponentMetric()
41+
return result
42+
}
43+
44+
clear (): void {
45+
super.clear()
46+
this.updateComponentMetric()
47+
}
48+
49+
private updateComponentMetric (): void {
50+
this.metric.update(this.length)
51+
}
52+
}
53+
54+
export interface CreateTrackedPeerListInit {
55+
/**
56+
* The metric name to use
57+
*/
58+
name: string
59+
60+
/**
61+
* A metrics implementation
62+
*/
63+
metrics?: Metrics
64+
}
65+
66+
/**
67+
* Creates a PeerList that reports it's size to the libp2p Metrics service
68+
*
69+
* @example
70+
*
71+
* * ```Typescript
72+
* import { trackedPeerList } from '@libp2p/peer-collections'
73+
* import { createLibp2p } from 'libp2p'
74+
*
75+
* const libp2p = await createLibp2p()
76+
*
77+
* const list = trackedPeerList({ name: 'my_metric_name', metrics: libp2p.metrics })
78+
* list.push(peerId)
79+
* ```
80+
*/
81+
export function trackedPeerList (config: CreateTrackedPeerListInit): PeerList {
82+
const { name, metrics } = config
83+
let map: PeerList
84+
85+
if (metrics != null) {
86+
map = new TrackedPeerList({ name, metrics })
87+
} else {
88+
map = new PeerList()
89+
}
90+
91+
return map
92+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { PeerMap } from './map.js'
2+
import type { Metric, Metrics, PeerId } from '@libp2p/interface'
3+
4+
export interface TrackedPeerMapInit {
5+
name: string
6+
metrics: Metrics
7+
}
8+
9+
class TrackedPeerMap<V> extends PeerMap<V> {
10+
private readonly metric: Metric
11+
12+
constructor (init: TrackedPeerMapInit) {
13+
super()
14+
15+
const { name, metrics } = init
16+
17+
this.metric = metrics.registerMetric(name)
18+
this.updateComponentMetric()
19+
}
20+
21+
set (key: PeerId, value: V): this {
22+
super.set(key, value)
23+
this.updateComponentMetric()
24+
return this
25+
}
26+
27+
delete (key: PeerId): boolean {
28+
const deleted = super.delete(key)
29+
this.updateComponentMetric()
30+
return deleted
31+
}
32+
33+
clear (): void {
34+
super.clear()
35+
this.updateComponentMetric()
36+
}
37+
38+
private updateComponentMetric (): void {
39+
this.metric.update(this.size)
40+
}
41+
}
42+
43+
export interface CreateTrackedPeerMapInit {
44+
/**
45+
* The metric name to use
46+
*/
47+
name: string
48+
49+
/**
50+
* A metrics implementation
51+
*/
52+
metrics?: Metrics
53+
}
54+
55+
/**
56+
* Creates a PeerMap that reports it's size to the libp2p Metrics service
57+
*
58+
* @example
59+
*
60+
* * ```Typescript
61+
* import { trackedPeerMap } from '@libp2p/peer-collections'
62+
* import { createLibp2p } from 'libp2p'
63+
*
64+
* const libp2p = await createLibp2p()
65+
*
66+
* const list = trackedPeerMap({ name: 'my_metric_name', metrics: libp2p.metrics })
67+
* map.set(peerId, 'value')
68+
* ```
69+
*/
70+
export function trackedPeerMap <V> (config: CreateTrackedPeerMapInit): PeerMap<V> {
71+
const { name, metrics } = config
72+
let map: PeerMap<V>
73+
74+
if (metrics != null) {
75+
map = new TrackedPeerMap<V>({ name, metrics })
76+
} else {
77+
map = new PeerMap<V>()
78+
}
79+
80+
return map
81+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { PeerSet } from './set.js'
2+
import type { Metric, Metrics, PeerId } from '@libp2p/interface'
3+
4+
export interface TrackedPeerSetInit {
5+
name: string
6+
metrics: Metrics
7+
}
8+
9+
class TrackedPeerSet extends PeerSet {
10+
private readonly metric: Metric
11+
12+
constructor (init: TrackedPeerSetInit) {
13+
super()
14+
15+
const { name, metrics } = init
16+
17+
this.metric = metrics.registerMetric(name)
18+
this.updateComponentMetric()
19+
}
20+
21+
add (peer: PeerId): void {
22+
super.add(peer)
23+
this.updateComponentMetric()
24+
}
25+
26+
delete (peer: PeerId): void {
27+
super.delete(peer)
28+
this.updateComponentMetric()
29+
}
30+
31+
clear (): void {
32+
super.clear()
33+
this.updateComponentMetric()
34+
}
35+
36+
private updateComponentMetric (): void {
37+
this.metric.update(this.size)
38+
}
39+
}
40+
41+
export interface CreateTrackedPeerSetInit {
42+
/**
43+
* The metric name to use
44+
*/
45+
name: string
46+
47+
/**
48+
* A metrics implementation
49+
*/
50+
metrics?: Metrics
51+
}
52+
53+
/**
54+
* Creates a PeerSet that reports it's size to the libp2p Metrics service
55+
*
56+
* @example Tracked peer sets
57+
*
58+
* * ```Typescript
59+
* import { trackedPeerSet } from '@libp2p/peer-collections'
60+
* import { createLibp2p } from 'libp2p'
61+
*
62+
* const libp2p = await createLibp2p()
63+
*
64+
* const list = trackedPeerSet({ name: 'my_metric_name', metrics: libp2p.metrics })
65+
* map.add(peerId)
66+
* ```
67+
*/
68+
export function trackedPeerSet (config: CreateTrackedPeerSetInit): PeerSet {
69+
const { name, metrics } = config
70+
let map: PeerSet
71+
72+
if (metrics != null) {
73+
map = new TrackedPeerSet({ name, metrics })
74+
} else {
75+
map = new PeerSet()
76+
}
77+
78+
return map
79+
}

0 commit comments

Comments
 (0)