@streamr/trackerless-network 102.0.0-beta.1 → 102.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +5 -5
- package/package.json +5 -5
- package/src/NetworkNode.ts +0 -142
- package/src/NetworkStack.ts +0 -197
- package/src/exports.ts +0 -18
- package/src/logic/ContentDeliveryLayerNode.ts +0 -424
- package/src/logic/ContentDeliveryManager.ts +0 -401
- package/src/logic/ContentDeliveryRpcLocal.ts +0 -48
- package/src/logic/ContentDeliveryRpcRemote.ts +0 -44
- package/src/logic/ControlLayerNode.ts +0 -17
- package/src/logic/DiscoveryLayerNode.ts +0 -30
- package/src/logic/DuplicateMessageDetector.ts +0 -167
- package/src/logic/ExternalNetworkRpc.ts +0 -42
- package/src/logic/NodeList.ts +0 -114
- package/src/logic/PeerDescriptorStoreManager.ts +0 -96
- package/src/logic/StreamPartNetworkSplitAvoidance.ts +0 -90
- package/src/logic/StreamPartReconnect.ts +0 -38
- package/src/logic/createContentDeliveryLayerNode.ts +0 -130
- package/src/logic/formStreamPartDeliveryServiceId.ts +0 -7
- package/src/logic/inspect/InspectSession.ts +0 -55
- package/src/logic/inspect/Inspector.ts +0 -100
- package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +0 -138
- package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +0 -66
- package/src/logic/neighbor-discovery/Handshaker.ts +0 -215
- package/src/logic/neighbor-discovery/NeighborFinder.ts +0 -77
- package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +0 -69
- package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +0 -75
- package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +0 -35
- package/src/logic/node-info/NodeInfoClient.ts +0 -23
- package/src/logic/node-info/NodeInfoRpcLocal.ts +0 -28
- package/src/logic/node-info/NodeInfoRpcRemote.ts +0 -11
- package/src/logic/propagation/FifoMapWithTTL.ts +0 -116
- package/src/logic/propagation/Propagation.ts +0 -84
- package/src/logic/propagation/PropagationTaskStore.ts +0 -41
- package/src/logic/proxy/ProxyClient.ts +0 -286
- package/src/logic/proxy/ProxyConnectionRpcLocal.ts +0 -106
- package/src/logic/proxy/ProxyConnectionRpcRemote.ts +0 -26
- package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +0 -73
- package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +0 -29
- package/src/logic/utils.ts +0 -18
- package/src/types.ts +0 -13
- package/test/benchmark/StreamPartIdDataKeyDistribution.test.ts +0 -60
- package/test/benchmark/first-message.ts +0 -171
- package/test/end-to-end/content-delivery-layer-node-with-real-connections.test.ts +0 -165
- package/test/end-to-end/external-network-rpc.test.ts +0 -67
- package/test/end-to-end/inspect.test.ts +0 -124
- package/test/end-to-end/proxy-and-full-node.test.ts +0 -143
- package/test/end-to-end/proxy-connections.test.ts +0 -226
- package/test/end-to-end/proxy-key-exchange.test.ts +0 -126
- package/test/end-to-end/webrtc-full-node-network.test.ts +0 -83
- package/test/end-to-end/websocket-full-node-network.test.ts +0 -82
- package/test/integration/ContentDeliveryLayerNode-Layer1Node-Latencies.test.ts +0 -139
- package/test/integration/ContentDeliveryLayerNode-Layer1Node.test.ts +0 -162
- package/test/integration/ContentDeliveryManager.test.ts +0 -160
- package/test/integration/ContentDeliveryRpcRemote.test.ts +0 -100
- package/test/integration/HandshakeRpcRemote.test.ts +0 -79
- package/test/integration/Handshakes.test.ts +0 -141
- package/test/integration/Inspect.test.ts +0 -89
- package/test/integration/NeighborUpdateRpcRemote.test.ts +0 -82
- package/test/integration/NetworkNode.test.ts +0 -115
- package/test/integration/NetworkRpc.test.ts +0 -52
- package/test/integration/NetworkStack.test.ts +0 -72
- package/test/integration/NodeInfoRpc.test.ts +0 -109
- package/test/integration/Propagation.test.ts +0 -76
- package/test/integration/joining-streams-on-offline-peers.test.ts +0 -82
- package/test/integration/stream-without-default-entrypoints.test.ts +0 -128
- package/test/integration/streamEntryPointReplacing.test.ts +0 -97
- package/test/types/global.d.ts +0 -1
- package/test/unit/ContentDeliveryLayerNode.test.ts +0 -112
- package/test/unit/ContentDeliveryManager.test.ts +0 -96
- package/test/unit/ContentDeliveryRpcLocal.test.ts +0 -60
- package/test/unit/DuplicateMessageDetector.test.ts +0 -192
- package/test/unit/ExternalNetworkRpc.test.ts +0 -48
- package/test/unit/FifoMapWithTtl.test.ts +0 -253
- package/test/unit/HandshakeRpcLocal.test.ts +0 -155
- package/test/unit/Handshaker.test.ts +0 -69
- package/test/unit/InspectSession.test.ts +0 -83
- package/test/unit/Inspector.test.ts +0 -51
- package/test/unit/NeighborFinder.test.ts +0 -51
- package/test/unit/NeighborUpdateRpcLocal.test.ts +0 -139
- package/test/unit/NetworkNode.test.ts +0 -42
- package/test/unit/NodeList.test.ts +0 -164
- package/test/unit/NumberPair.test.ts +0 -22
- package/test/unit/PeerDescriptorStoreManager.test.ts +0 -103
- package/test/unit/Propagation.test.ts +0 -151
- package/test/unit/ProxyConnectionRpcRemote.test.ts +0 -39
- package/test/unit/StreamPartIDDataKey.test.ts +0 -12
- package/test/unit/StreamPartNetworkSplitAvoidance.test.ts +0 -31
- package/test/unit/StreamPartReconnect.test.ts +0 -30
- package/test/unit/TemporaryConnectionRpcLocal.test.ts +0 -38
- package/test/utils/fake/FakePeerDescriptorStoreManager.ts +0 -29
- package/test/utils/mock/MockConnectionsView.ts +0 -18
- package/test/utils/mock/MockControlLayerNode.ts +0 -78
- package/test/utils/mock/MockDiscoveryLayerNode.ts +0 -60
- package/test/utils/mock/MockHandshaker.ts +0 -17
- package/test/utils/mock/MockNeighborFinder.ts +0 -20
- package/test/utils/mock/MockNeighborUpdateManager.ts +0 -21
- package/test/utils/mock/MockTransport.ts +0 -30
- package/test/utils/utils.ts +0 -144
- package/tsconfig.browser.json +0 -13
- package/tsconfig.jest.json +0 -17
- package/tsconfig.json +0 -3
- package/tsconfig.node.json +0 -17
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { DhtAddress, ListeningRpcCommunicator, PeerDescriptor, toNodeId } from '@streamr/dht'
|
|
2
|
-
import { Logger, StreamPartID, scheduleAtInterval } from '@streamr/utils'
|
|
3
|
-
import { NeighborUpdate } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
4
|
-
import { NeighborUpdateRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
|
|
5
|
-
import { NodeList } from '../NodeList'
|
|
6
|
-
import { NeighborFinder } from './NeighborFinder'
|
|
7
|
-
import { NeighborUpdateRpcLocal } from './NeighborUpdateRpcLocal'
|
|
8
|
-
import { NeighborUpdateRpcRemote } from './NeighborUpdateRpcRemote'
|
|
9
|
-
|
|
10
|
-
interface NeighborUpdateManagerOptions {
|
|
11
|
-
localPeerDescriptor: PeerDescriptor
|
|
12
|
-
neighbors: NodeList
|
|
13
|
-
nearbyNodeView: NodeList
|
|
14
|
-
neighborFinder: NeighborFinder
|
|
15
|
-
streamPartId: StreamPartID
|
|
16
|
-
rpcCommunicator: ListeningRpcCommunicator
|
|
17
|
-
neighborUpdateInterval: number
|
|
18
|
-
neighborTargetCount: number
|
|
19
|
-
ongoingHandshakes: Set<DhtAddress>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const logger = new Logger(module)
|
|
23
|
-
|
|
24
|
-
export class NeighborUpdateManager {
|
|
25
|
-
|
|
26
|
-
private readonly abortController: AbortController
|
|
27
|
-
private readonly options: NeighborUpdateManagerOptions
|
|
28
|
-
private readonly rpcLocal: NeighborUpdateRpcLocal
|
|
29
|
-
|
|
30
|
-
constructor(options: NeighborUpdateManagerOptions) {
|
|
31
|
-
this.abortController = new AbortController()
|
|
32
|
-
this.rpcLocal = new NeighborUpdateRpcLocal(options)
|
|
33
|
-
this.options = options
|
|
34
|
-
this.options.rpcCommunicator.registerRpcMethod(NeighborUpdate, NeighborUpdate, 'neighborUpdate',
|
|
35
|
-
(req: NeighborUpdate, context) => this.rpcLocal.neighborUpdate(req, context))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async start(): Promise<void> {
|
|
39
|
-
await scheduleAtInterval(() => this.updateNeighborInfo(), this.options.neighborUpdateInterval, false, this.abortController.signal)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
stop(): void {
|
|
43
|
-
this.abortController.abort()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private async updateNeighborInfo(): Promise<void> {
|
|
47
|
-
logger.trace(`Updating neighbor info to nodes`)
|
|
48
|
-
const neighborDescriptors = this.options.neighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor())
|
|
49
|
-
const startTime = Date.now()
|
|
50
|
-
await Promise.allSettled(this.options.neighbors.getAll().map(async (neighbor) => {
|
|
51
|
-
const res = await this.createRemote(neighbor.getPeerDescriptor()).updateNeighbors(this.options.streamPartId, neighborDescriptors)
|
|
52
|
-
const nodeId = toNodeId(neighbor.getPeerDescriptor())
|
|
53
|
-
this.options.neighbors.get(nodeId)!.setRtt(Date.now() - startTime)
|
|
54
|
-
if (res.removeMe) {
|
|
55
|
-
this.options.neighbors.remove(nodeId)
|
|
56
|
-
this.options.neighborFinder.start([nodeId])
|
|
57
|
-
}
|
|
58
|
-
}))
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private createRemote(targetPeerDescriptor: PeerDescriptor): NeighborUpdateRpcRemote {
|
|
62
|
-
return new NeighborUpdateRpcRemote(
|
|
63
|
-
this.options.localPeerDescriptor,
|
|
64
|
-
targetPeerDescriptor,
|
|
65
|
-
this.options.rpcCommunicator,
|
|
66
|
-
NeighborUpdateRpcClient
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
2
|
-
import { DhtAddress, DhtCallContext, ListeningRpcCommunicator, PeerDescriptor, toNodeId } from '@streamr/dht'
|
|
3
|
-
import { StreamPartID } from '@streamr/utils'
|
|
4
|
-
import { NeighborUpdate } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
5
|
-
import { ContentDeliveryRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
|
|
6
|
-
import { INeighborUpdateRpc } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.server'
|
|
7
|
-
import { ContentDeliveryRpcRemote } from '../ContentDeliveryRpcRemote'
|
|
8
|
-
import { NodeList } from '../NodeList'
|
|
9
|
-
import { NeighborFinder } from './NeighborFinder'
|
|
10
|
-
|
|
11
|
-
interface NeighborUpdateRpcLocalOptions {
|
|
12
|
-
localPeerDescriptor: PeerDescriptor
|
|
13
|
-
streamPartId: StreamPartID
|
|
14
|
-
neighbors: NodeList
|
|
15
|
-
nearbyNodeView: NodeList
|
|
16
|
-
neighborFinder: NeighborFinder
|
|
17
|
-
rpcCommunicator: ListeningRpcCommunicator
|
|
18
|
-
neighborTargetCount: number
|
|
19
|
-
ongoingHandshakes: Set<DhtAddress>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class NeighborUpdateRpcLocal implements INeighborUpdateRpc {
|
|
23
|
-
|
|
24
|
-
private readonly options: NeighborUpdateRpcLocalOptions
|
|
25
|
-
|
|
26
|
-
constructor(options: NeighborUpdateRpcLocalOptions) {
|
|
27
|
-
this.options = options
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private updateContacts(neighborDescriptors: PeerDescriptor[]): void {
|
|
31
|
-
const ownNodeId = toNodeId(this.options.localPeerDescriptor)
|
|
32
|
-
const newPeerDescriptors = neighborDescriptors.filter((peerDescriptor) => {
|
|
33
|
-
const nodeId = toNodeId(peerDescriptor)
|
|
34
|
-
return nodeId !== ownNodeId && !this.options.neighbors.getIds().includes(nodeId)
|
|
35
|
-
})
|
|
36
|
-
newPeerDescriptors.forEach((peerDescriptor) => this.options.nearbyNodeView.add(
|
|
37
|
-
new ContentDeliveryRpcRemote(
|
|
38
|
-
this.options.localPeerDescriptor,
|
|
39
|
-
peerDescriptor,
|
|
40
|
-
this.options.rpcCommunicator,
|
|
41
|
-
ContentDeliveryRpcClient
|
|
42
|
-
))
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private createResponse(removeMe: boolean): NeighborUpdate {
|
|
47
|
-
return {
|
|
48
|
-
streamPartId: this.options.streamPartId,
|
|
49
|
-
neighborDescriptors: this.options.neighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor()),
|
|
50
|
-
removeMe
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// INeighborUpdateRpc server method
|
|
55
|
-
async neighborUpdate(message: NeighborUpdate, context: ServerCallContext): Promise<NeighborUpdate> {
|
|
56
|
-
const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
|
|
57
|
-
const remoteNodeId = toNodeId(senderPeerDescriptor)
|
|
58
|
-
this.updateContacts(message.neighborDescriptors)
|
|
59
|
-
if (!this.options.neighbors.has(remoteNodeId) && !this.options.ongoingHandshakes.has(remoteNodeId)) {
|
|
60
|
-
return this.createResponse(true)
|
|
61
|
-
} else {
|
|
62
|
-
const isOverNeighborCount = this.options.neighbors.size() > this.options.neighborTargetCount
|
|
63
|
-
// Motivation: We don't know the remote's neighborTargetCount setting here. We only ask to cut connections
|
|
64
|
-
// if the remote has a "sufficient" number of neighbors, where "sufficient" means our neighborTargetCount
|
|
65
|
-
// setting.
|
|
66
|
-
&& message.neighborDescriptors.length > this.options.neighborTargetCount
|
|
67
|
-
if (!isOverNeighborCount) {
|
|
68
|
-
this.options.neighborFinder.start()
|
|
69
|
-
} else {
|
|
70
|
-
this.options.neighbors.remove(remoteNodeId)
|
|
71
|
-
}
|
|
72
|
-
return this.createResponse(isOverNeighborCount)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { PeerDescriptor, RpcRemote, toNodeId } from '@streamr/dht'
|
|
2
|
-
import { Logger, StreamPartID } from '@streamr/utils'
|
|
3
|
-
import { NeighborUpdate } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
4
|
-
import { NeighborUpdateRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
|
|
5
|
-
|
|
6
|
-
const logger = new Logger(module)
|
|
7
|
-
|
|
8
|
-
interface UpdateNeighborsResponse {
|
|
9
|
-
peerDescriptors: PeerDescriptor[]
|
|
10
|
-
removeMe: boolean
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class NeighborUpdateRpcRemote extends RpcRemote<NeighborUpdateRpcClient> {
|
|
14
|
-
|
|
15
|
-
async updateNeighbors(streamPartId: StreamPartID, neighbors: PeerDescriptor[]): Promise<UpdateNeighborsResponse> {
|
|
16
|
-
const request: NeighborUpdate = {
|
|
17
|
-
streamPartId,
|
|
18
|
-
neighborDescriptors: neighbors,
|
|
19
|
-
removeMe: false
|
|
20
|
-
}
|
|
21
|
-
try {
|
|
22
|
-
const response = await this.getClient().neighborUpdate(request, this.formDhtRpcOptions())
|
|
23
|
-
return {
|
|
24
|
-
peerDescriptors: response.neighborDescriptors,
|
|
25
|
-
removeMe: response.removeMe
|
|
26
|
-
}
|
|
27
|
-
} catch (err: any) {
|
|
28
|
-
logger.debug(`updateNeighbors to ${toNodeId(this.getPeerDescriptor())} failed`, { err })
|
|
29
|
-
return {
|
|
30
|
-
peerDescriptors: [],
|
|
31
|
-
removeMe: true
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { DhtCallContext, PeerDescriptor } from '@streamr/dht'
|
|
2
|
-
import { RpcCommunicator } from '@streamr/proto-rpc'
|
|
3
|
-
import { NodeInfo } from '../../types'
|
|
4
|
-
import { NodeInfoRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
|
|
5
|
-
import { NodeInfoRpcRemote } from './NodeInfoRpcRemote'
|
|
6
|
-
|
|
7
|
-
export class NodeInfoClient {
|
|
8
|
-
private readonly ownPeerDescriptor: PeerDescriptor
|
|
9
|
-
private readonly rpcCommunicator: RpcCommunicator<DhtCallContext>
|
|
10
|
-
|
|
11
|
-
constructor(ownPeerDescriptor: PeerDescriptor, rpcCommunicator: RpcCommunicator<DhtCallContext>) {
|
|
12
|
-
this.ownPeerDescriptor = ownPeerDescriptor
|
|
13
|
-
this.rpcCommunicator = rpcCommunicator
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async getInfo(node: PeerDescriptor): Promise<NodeInfo> {
|
|
17
|
-
const remote = new NodeInfoRpcRemote(this.ownPeerDescriptor, node, this.rpcCommunicator, NodeInfoRpcClient)
|
|
18
|
-
// TODO remove casting when we validate NodeInfoResponse messages and therefore can annotate
|
|
19
|
-
// each of the field as required in the decorated type
|
|
20
|
-
return remote.getInfo() as unknown as NodeInfo
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { NodeInfoRequest, NodeInfoResponse } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
2
|
-
import { INodeInfoRpc } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.server'
|
|
3
|
-
import { NetworkStack } from '../../NetworkStack'
|
|
4
|
-
import { ListeningRpcCommunicator } from '@streamr/dht'
|
|
5
|
-
|
|
6
|
-
export const NODE_INFO_RPC_SERVICE_ID = 'system/node-info-rpc'
|
|
7
|
-
|
|
8
|
-
export class NodeInfoRpcLocal implements INodeInfoRpc {
|
|
9
|
-
|
|
10
|
-
private readonly stack: NetworkStack
|
|
11
|
-
private readonly rpcCommunicator: ListeningRpcCommunicator
|
|
12
|
-
|
|
13
|
-
constructor(stack: NetworkStack, rpcCommunicator: ListeningRpcCommunicator) {
|
|
14
|
-
this.stack = stack
|
|
15
|
-
this.rpcCommunicator = rpcCommunicator
|
|
16
|
-
this.registerDefaultServerMethods()
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
private registerDefaultServerMethods(): void {
|
|
20
|
-
this.rpcCommunicator.registerRpcMethod(NodeInfoRequest, NodeInfoResponse, 'getInfo',
|
|
21
|
-
() => this.getInfo())
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async getInfo(): Promise<NodeInfoResponse> {
|
|
25
|
-
return this.stack.createNodeInfo()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { NodeInfoResponse } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
2
|
-
import { NodeInfoRpcClient } from '../../../generated/packages/trackerless-network/protos/NetworkRpc.client'
|
|
3
|
-
import { RpcRemote } from '@streamr/dht'
|
|
4
|
-
|
|
5
|
-
export class NodeInfoRpcRemote extends RpcRemote<NodeInfoRpcClient> {
|
|
6
|
-
|
|
7
|
-
async getInfo(): Promise<NodeInfoResponse> {
|
|
8
|
-
return this.getClient().getInfo({}, this.formDhtRpcOptions())
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { Node, Yallist } from 'yallist'
|
|
2
|
-
|
|
3
|
-
interface Item<K, V> {
|
|
4
|
-
value: V
|
|
5
|
-
dropQueueNode: Node<K>
|
|
6
|
-
expiresAt: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface FifoMapWithTtlOptions<K> {
|
|
10
|
-
ttlInMs: number
|
|
11
|
-
maxSize: number
|
|
12
|
-
onItemDropped?: (key: K) => void
|
|
13
|
-
timeProvider?: () => number
|
|
14
|
-
debugMode?: boolean
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A "Map" implementation with a maximum size and TTL expiration on entries.
|
|
19
|
-
*
|
|
20
|
-
* When full, room is made for new entries by dropping existing by FIFO method.
|
|
21
|
-
*
|
|
22
|
-
* Entries have a TTL after which they are considered stale. Stale items are
|
|
23
|
-
* not returned when querying.
|
|
24
|
-
*
|
|
25
|
-
*/
|
|
26
|
-
export class FifoMapWithTTL<K, V> {
|
|
27
|
-
// class invariant: the keys present in `items` and `dropQueue` are the same set.
|
|
28
|
-
private readonly items = new Map<K, Item<K, V>>()
|
|
29
|
-
private readonly dropQueue = Yallist.create<K>() // queue is used to determine deletion order when full
|
|
30
|
-
private readonly ttlInMs: number
|
|
31
|
-
private readonly maxSize: number
|
|
32
|
-
private readonly onItemDropped: (key: K) => void
|
|
33
|
-
private readonly timeProvider: () => number
|
|
34
|
-
|
|
35
|
-
constructor({
|
|
36
|
-
ttlInMs,
|
|
37
|
-
maxSize,
|
|
38
|
-
onItemDropped = () => {},
|
|
39
|
-
timeProvider = Date.now
|
|
40
|
-
}: FifoMapWithTtlOptions<K>) {
|
|
41
|
-
if (ttlInMs < 0) {
|
|
42
|
-
throw new Error(`ttlInMs (${ttlInMs}) cannot be < 0`)
|
|
43
|
-
}
|
|
44
|
-
if (maxSize < 0) {
|
|
45
|
-
throw new Error(`maxSize (${maxSize}) cannot be < 0`)
|
|
46
|
-
}
|
|
47
|
-
this.ttlInMs = ttlInMs
|
|
48
|
-
this.maxSize = maxSize
|
|
49
|
-
this.onItemDropped = onItemDropped
|
|
50
|
-
this.timeProvider = timeProvider
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
set(key: K, value: V): void {
|
|
54
|
-
if (this.maxSize === 0) {
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
if (this.items.size > this.maxSize) {
|
|
58
|
-
throw new Error('assertion error: maximum size exceeded')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// delete an existing entry if exists
|
|
62
|
-
this.delete(key)
|
|
63
|
-
|
|
64
|
-
// make room for new entry
|
|
65
|
-
if (this.items.size === this.maxSize) {
|
|
66
|
-
const keyToDel = this.dropQueue.shift()
|
|
67
|
-
if (keyToDel === undefined) {
|
|
68
|
-
throw new Error('assertion error: queue empty but still have items')
|
|
69
|
-
}
|
|
70
|
-
this.items.delete(keyToDel)
|
|
71
|
-
this.onItemDropped(keyToDel)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// add entry
|
|
75
|
-
const dropQueueNode = new Node<K>(key)
|
|
76
|
-
this.dropQueue.pushNode(dropQueueNode)
|
|
77
|
-
this.items.set(key, {
|
|
78
|
-
value,
|
|
79
|
-
dropQueueNode,
|
|
80
|
-
expiresAt: this.timeProvider() + this.ttlInMs
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
delete(key: K): void {
|
|
85
|
-
const item = this.items.get(key)
|
|
86
|
-
if (item !== undefined) {
|
|
87
|
-
this.items.delete(key)
|
|
88
|
-
this.dropQueue.removeNode(item.dropQueueNode)
|
|
89
|
-
this.onItemDropped(key)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
get(key: K): V | undefined {
|
|
94
|
-
const item = this.items.get(key)
|
|
95
|
-
if (item === undefined) {
|
|
96
|
-
return undefined
|
|
97
|
-
}
|
|
98
|
-
if (item.expiresAt <= this.timeProvider()) {
|
|
99
|
-
this.delete(key)
|
|
100
|
-
return undefined
|
|
101
|
-
}
|
|
102
|
-
return item.value
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
values(): V[] {
|
|
106
|
-
const keys = [...this.items.keys()]
|
|
107
|
-
const values = []
|
|
108
|
-
for (const key of keys) {
|
|
109
|
-
const value = this.get(key)
|
|
110
|
-
if (value !== undefined) {
|
|
111
|
-
values.push(value)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return values
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { DhtAddress } from '@streamr/dht'
|
|
2
|
-
import { StreamMessage } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
3
|
-
import { PropagationTask, PropagationTaskStore } from './PropagationTaskStore'
|
|
4
|
-
|
|
5
|
-
type SendToNeighborFn = (neighborId: DhtAddress, msg: StreamMessage) => Promise<void>
|
|
6
|
-
|
|
7
|
-
interface ConstructorOptions {
|
|
8
|
-
sendToNeighbor: SendToNeighborFn
|
|
9
|
-
minPropagationTargets: number
|
|
10
|
-
ttl?: number
|
|
11
|
-
maxMessages?: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const DEFAULT_MAX_MESSAGES = 150
|
|
15
|
-
const DEFAULT_TTL = 10 * 1000
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Message propagation logic of a node. Given a message, this class will actively attempt to propagate it to
|
|
19
|
-
* `minPropagationTargets` neighbors until success or TTL expiration.
|
|
20
|
-
*
|
|
21
|
-
* Setting `minPropagationTargets = 0` effectively disables any propagation reattempts. A message will then
|
|
22
|
-
* only be propagated exactly once, to neighbors that are present at that moment, in a fire-and-forget manner.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
export class Propagation {
|
|
26
|
-
private readonly sendToNeighbor: SendToNeighborFn
|
|
27
|
-
private readonly minPropagationTargets: number
|
|
28
|
-
private readonly activeTaskStore: PropagationTaskStore
|
|
29
|
-
|
|
30
|
-
constructor({
|
|
31
|
-
sendToNeighbor,
|
|
32
|
-
minPropagationTargets,
|
|
33
|
-
ttl = DEFAULT_TTL,
|
|
34
|
-
maxMessages = DEFAULT_MAX_MESSAGES
|
|
35
|
-
}: ConstructorOptions) {
|
|
36
|
-
this.sendToNeighbor = sendToNeighbor
|
|
37
|
-
this.minPropagationTargets = minPropagationTargets
|
|
38
|
-
this.activeTaskStore = new PropagationTaskStore(ttl, maxMessages)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Node should invoke this when it learns about a new message
|
|
43
|
-
*/
|
|
44
|
-
feedUnseenMessage(message: StreamMessage, targets: DhtAddress[], source: DhtAddress | null): void {
|
|
45
|
-
const task = {
|
|
46
|
-
message,
|
|
47
|
-
source,
|
|
48
|
-
handledNeighbors: new Set<DhtAddress>()
|
|
49
|
-
}
|
|
50
|
-
this.activeTaskStore.add(task)
|
|
51
|
-
for (const target of targets) {
|
|
52
|
-
this.sendAndAwaitThenMark(task, target)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Node should invoke this when it learns about a new node stream assignment
|
|
58
|
-
*/
|
|
59
|
-
onNeighborJoined(neighborId: DhtAddress): void {
|
|
60
|
-
const tasks = this.activeTaskStore.get()
|
|
61
|
-
for (const task of tasks) {
|
|
62
|
-
this.sendAndAwaitThenMark(task, neighborId)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private sendAndAwaitThenMark({ message, source, handledNeighbors }: PropagationTask, neighborId: DhtAddress): void {
|
|
67
|
-
if (!handledNeighbors.has(neighborId) && neighborId !== source) {
|
|
68
|
-
(async () => {
|
|
69
|
-
try {
|
|
70
|
-
await this.sendToNeighbor(neighborId, message)
|
|
71
|
-
} catch {
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
// Side-note: due to asynchronicity, the task being modified at this point could already be stale and
|
|
75
|
-
// deleted from `activeTaskStore`. However, as modifying it or re-deleting it is pretty much
|
|
76
|
-
// inconsequential at this point, leaving the logic as is.
|
|
77
|
-
handledNeighbors.add(neighborId)
|
|
78
|
-
if (handledNeighbors.size >= this.minPropagationTargets) {
|
|
79
|
-
this.activeTaskStore.delete(message.messageId!)
|
|
80
|
-
}
|
|
81
|
-
})()
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { DhtAddress } from '@streamr/dht'
|
|
2
|
-
import { MessageRef, StreamMessage } from '../../../generated/packages/trackerless-network/protos/NetworkRpc'
|
|
3
|
-
import { FifoMapWithTTL } from './FifoMapWithTTL'
|
|
4
|
-
|
|
5
|
-
export interface PropagationTask {
|
|
6
|
-
message: StreamMessage
|
|
7
|
-
source: string | null
|
|
8
|
-
handledNeighbors: Set<DhtAddress>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Keeps track of propagation tasks for the needs of message propagation logic.
|
|
13
|
-
*
|
|
14
|
-
* Properties:
|
|
15
|
-
* - Allows fetching propagation tasks by StreamPartID
|
|
16
|
-
* - Upper bound on number of tasks stored, replacement policy if FIFO
|
|
17
|
-
* - Items have a TTL, after which they are considered stale and not returned when querying
|
|
18
|
-
**/
|
|
19
|
-
export class PropagationTaskStore {
|
|
20
|
-
private readonly tasks: FifoMapWithTTL<MessageRef, PropagationTask>
|
|
21
|
-
|
|
22
|
-
constructor(ttlInMs: number, maxTasks: number) {
|
|
23
|
-
this.tasks = new FifoMapWithTTL<MessageRef, PropagationTask>({
|
|
24
|
-
ttlInMs,
|
|
25
|
-
maxSize: maxTasks
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get(): PropagationTask[] {
|
|
30
|
-
return this.tasks.values()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
add(task: PropagationTask): void {
|
|
34
|
-
const messageId = task.message.messageId!
|
|
35
|
-
this.tasks.set(messageId, task)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
delete(messageId: MessageRef): void {
|
|
39
|
-
this.tasks.delete(messageId) // causes `onKeyDropped` to be invoked
|
|
40
|
-
}
|
|
41
|
-
}
|