Skip to content

Commit

Permalink
Introduced new relayKeepAlive option
Browse files Browse the repository at this point in the history
..on `Waku` with a default to 5min to send ping messages over relay
to ensure the relay stream stays open.

This is a workaround until
[js-libp2p#744](libp2p/js-libp2p#744) is done
as there are issues when TCP(?) timeouts and the stream gets closed.
  • Loading branch information
D4nte committed Jul 27, 2021
1 parent e8fa9c3 commit dcbf074
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Breaking**: Renamed `WakuRelay.(add|delete)PrivateDecryptionKey` to `WakuRelay.(add|delete)DecryptionKey` to make it clearer that it accepts both symmetric keys and asymmetric private keys.
- Upgrade libp2p to 0.32.0.
- **Breaking**: Rename `keepAlive` option to `pingKeepAlive`.
- Introduced new `relayKeepAlive` option on `Waku` with a default to 59s to send ping messages over relay to ensure the relay stream stays open.
This is a workaround until [js-libp2p#744](https://github.com/libp2p/js-libp2p/issues/744) is done as there are issues when TCP(?) timeouts and the stream gets closed
([#185](https://github.com/status-im/js-waku/issues/185), [js-libp2p#939](https://github.com/libp2p/js-libp2p/issues/939))

### Fixed
- Align `WakuMessage` readme example with actual code behaviour.
Expand Down
77 changes: 58 additions & 19 deletions src/lib/waku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ import { Multiaddr, multiaddr } from 'multiaddr';
import PeerId from 'peer-id';

import { WakuLightPush } from './waku_light_push';
import { WakuMessage } from './waku_message';
import { RelayCodecs, WakuRelay } from './waku_relay';
import { StoreCodec, WakuStore } from './waku_store';
import { RelayPingContentTopic } from './waku_relay/constants';

const websocketsTransportKey = Websockets.prototype[Symbol.toStringTag];

const DefaultPingKeepAliveValueSecs = 0;
const DefaultRelayKeepAliveValueSecs = 5 * 60;

export interface CreateOptions {
/**
* The PubSub Topic to use. Defaults to {@link DefaultPubsubTopic}.
Expand All @@ -40,9 +45,16 @@ export interface CreateOptions {
* Set keep alive frequency in seconds: Waku will send a `/ipfs/ping/1.0.0`
* request to each peer after the set number of seconds. Set to 0 to disable.
*
* @default 0
* @default {@link DefaultPingKeepAliveValueSecs}
*/
pingKeepAlive?: number;
/**
* Set keep alive frequency in seconds: Waku will send a ping message over
* relay to each peer after the set number of seconds. Set to 0 to disable.
*
* @default {@link DefaultRelayKeepAliveValueSecs}
*/
relayKeepAlive?: number;
/**
* You can pass options to the `Libp2p` instance used by {@link Waku} using the {@link CreateOptions.libp2p} property.
* This property is the same type than the one passed to [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create)
Expand Down Expand Up @@ -70,6 +82,9 @@ export class Waku {
private pingKeepAliveTimers: {
[peer: string]: ReturnType<typeof setInterval>;
};
private relayKeepAliveTimers: {
[peer: string]: ReturnType<typeof setInterval>;
};

private constructor(
options: CreateOptions,
Expand All @@ -82,21 +97,24 @@ export class Waku {
this.store = store;
this.lightPush = lightPush;
this.pingKeepAliveTimers = {};
this.relayKeepAliveTimers = {};

const pingKeepAlive = options.pingKeepAlive || 0;

if (pingKeepAlive !== 0) {
libp2p.connectionManager.on('peer:connect', (connection: Connection) => {
this.startPingKeepAlive(connection.remotePeer, pingKeepAlive);
});
const pingKeepAlive =
options.pingKeepAlive || DefaultPingKeepAliveValueSecs;
const relayKeepAlive =
options.relayKeepAlive || DefaultRelayKeepAliveValueSecs;

libp2p.connectionManager.on(
'peer:disconnect',
(connection: Connection) => {
this.stopPingKeepAlive(connection.remotePeer);
}
libp2p.connectionManager.on('peer:connect', (connection: Connection) => {
this.startKeepAlives(
connection.remotePeer,
pingKeepAlive,
relayKeepAlive
);
}
});

libp2p.connectionManager.on('peer:disconnect', (connection: Connection) => {
this.stopKeepAlives(connection.remotePeer);
});
}

/**
Expand Down Expand Up @@ -214,21 +232,42 @@ export class Waku {
return localMultiaddr + '/p2p/' + this.libp2p.peerId.toB58String();
}

private startPingKeepAlive(peerId: PeerId, periodSecs: number): void {
private startKeepAlives(
peerId: PeerId,
pingPeriodSecs: number,
relayPeriodSecs: number
): void {
// Just in case a timer already exist for this peer
this.stopPingKeepAlive(peerId);
this.stopKeepAlives(peerId);

const peerIdStr = peerId.toB58String();
this.pingKeepAliveTimers[peerIdStr] = setInterval(() => {
Ping(this.libp2p, peerId);
}, periodSecs * 1000);

if (pingPeriodSecs !== 0) {
this.pingKeepAliveTimers[peerIdStr] = setInterval(() => {
Ping(this.libp2p, peerId);
}, pingPeriodSecs * 1000);
}

if (relayPeriodSecs !== 0) {
this.relayKeepAliveTimers[peerIdStr] = setInterval(() => {
WakuMessage.fromBytes(new Uint8Array(), {
contentTopic: RelayPingContentTopic,
}).then((wakuMsg) => this.relay.send(wakuMsg));
}, relayPeriodSecs * 1000);
}
}

private stopPingKeepAlive(peerId: PeerId): void {
private stopKeepAlives(peerId: PeerId): void {
const peerIdStr = peerId.toB58String();

if (this.pingKeepAliveTimers[peerIdStr]) {
clearInterval(this.pingKeepAliveTimers[peerIdStr]);
delete this.pingKeepAliveTimers[peerIdStr];
}

if (this.relayKeepAliveTimers[peerIdStr]) {
clearInterval(this.relayKeepAliveTimers[peerIdStr]);
delete this.relayKeepAliveTimers[peerIdStr];
}
}
}
2 changes: 2 additions & 0 deletions src/lib/waku_relay/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const RelayCodecs = [
*/
export const DefaultPubsubTopic = '/waku/2/default-waku/proto';

export const RelayPingContentTopic = '/relay-ping/1/ping/null';

/**
* RelayGossipFactor affects how many peers we will emit gossip to at each heartbeat.
* We will send gossip to RelayGossipFactor * (total number of non-mesh peers), or
Expand Down

0 comments on commit dcbf074

Please sign in to comment.