mirror of
https://github.com/status-im/go-waku.git
synced 2025-02-26 20:10:44 +00:00
feat(peer-exchange): rate limit (#1043)
Co-authored-by: Prem Chaitanya Prathi <chaitanyaprem@gmail.com>
This commit is contained in:
parent
d4bda1255c
commit
4d828bdf70
@ -19,8 +19,8 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts ...PeerExchangeOption) error {
|
func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts ...RequestOption) error {
|
||||||
params := new(PeerExchangeParameters)
|
params := new(PeerExchangeRequestParameters)
|
||||||
params.host = wakuPX.h
|
params.host = wakuPX.h
|
||||||
params.log = wakuPX.log
|
params.log = wakuPX.log
|
||||||
params.pm = wakuPX.pm
|
params.pm = wakuPX.pm
|
||||||
@ -103,7 +103,7 @@ func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts
|
|||||||
return wakuPX.handleResponse(ctx, responseRPC.Response, params)
|
return wakuPX.handleResponse(ctx, responseRPC.Response, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wakuPX *WakuPeerExchange) handleResponse(ctx context.Context, response *pb.PeerExchangeResponse, params *PeerExchangeParameters) error {
|
func (wakuPX *WakuPeerExchange) handleResponse(ctx context.Context, response *pb.PeerExchangeResponse, params *PeerExchangeRequestParameters) error {
|
||||||
var discoveredPeers []struct {
|
var discoveredPeers []struct {
|
||||||
addrInfo peer.AddrInfo
|
addrInfo peer.AddrInfo
|
||||||
enr *enode.Node
|
enr *enode.Node
|
||||||
|
@ -39,6 +39,7 @@ var (
|
|||||||
decodeRPCFailure metricsErrCategory = "decode_rpc_failure"
|
decodeRPCFailure metricsErrCategory = "decode_rpc_failure"
|
||||||
pxFailure metricsErrCategory = "px_failure"
|
pxFailure metricsErrCategory = "px_failure"
|
||||||
dialFailure metricsErrCategory = "dial_failure"
|
dialFailure metricsErrCategory = "dial_failure"
|
||||||
|
rateLimitFailure metricsErrCategory = "ratelimit_failure"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecordError increases the counter for different error types
|
// RecordError increases the counter for different error types
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/service"
|
"github.com/waku-org/go-waku/waku/v2/service"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PeerExchangeID_v20alpha1 is the current Waku Peer Exchange protocol identifier
|
// PeerExchangeID_v20alpha1 is the current Waku Peer Exchange protocol identifier
|
||||||
@ -47,12 +48,13 @@ type WakuPeerExchange struct {
|
|||||||
|
|
||||||
peerConnector PeerConnector
|
peerConnector PeerConnector
|
||||||
enrCache *enrCache
|
enrCache *enrCache
|
||||||
|
limiter *rate.Limiter
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWakuPeerExchange returns a new instance of WakuPeerExchange struct
|
// NewWakuPeerExchange returns a new instance of WakuPeerExchange struct
|
||||||
// Takes an optional peermanager if WakuPeerExchange is being created along with WakuNode.
|
// Takes an optional peermanager if WakuPeerExchange is being created along with WakuNode.
|
||||||
// If using libp2p host, then pass peermanager as nil
|
// If using libp2p host, then pass peermanager as nil
|
||||||
func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger) (*WakuPeerExchange, error) {
|
func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger, opts ...Option) (*WakuPeerExchange, error) {
|
||||||
wakuPX := new(WakuPeerExchange)
|
wakuPX := new(WakuPeerExchange)
|
||||||
wakuPX.disc = disc
|
wakuPX.disc = disc
|
||||||
wakuPX.metrics = newMetrics(reg)
|
wakuPX.metrics = newMetrics(reg)
|
||||||
@ -62,6 +64,12 @@ func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector,
|
|||||||
wakuPX.pm = pm
|
wakuPX.pm = pm
|
||||||
wakuPX.CommonService = service.NewCommonService()
|
wakuPX.CommonService = service.NewCommonService()
|
||||||
|
|
||||||
|
params := &PeerExchangeParameters{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
wakuPX.limiter = params.limiter
|
||||||
return wakuPX, nil
|
return wakuPX, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +95,14 @@ func (wakuPX *WakuPeerExchange) start() error {
|
|||||||
func (wakuPX *WakuPeerExchange) onRequest() func(network.Stream) {
|
func (wakuPX *WakuPeerExchange) onRequest() func(network.Stream) {
|
||||||
return func(stream network.Stream) {
|
return func(stream network.Stream) {
|
||||||
logger := wakuPX.log.With(logging.HostID("peer", stream.Conn().RemotePeer()))
|
logger := wakuPX.log.With(logging.HostID("peer", stream.Conn().RemotePeer()))
|
||||||
|
|
||||||
|
if wakuPX.limiter != nil && !wakuPX.limiter.Allow() {
|
||||||
|
wakuPX.metrics.RecordError(rateLimitFailure)
|
||||||
|
wakuPX.log.Error("exceeds the rate limit")
|
||||||
|
// TODO: peer exchange protocol should contain an err field
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
requestRPC := &pb.PeerExchangeRPC{}
|
requestRPC := &pb.PeerExchangeRPC{}
|
||||||
reader := pbio.NewDelimitedReader(stream, math.MaxInt32)
|
reader := pbio.NewDelimitedReader(stream, math.MaxInt32)
|
||||||
err := reader.ReadMsg(requestRPC)
|
err := reader.ReadMsg(requestRPC)
|
||||||
|
@ -8,9 +8,23 @@ import (
|
|||||||
"github.com/multiformats/go-multiaddr"
|
"github.com/multiformats/go-multiaddr"
|
||||||
"github.com/waku-org/go-waku/waku/v2/peermanager"
|
"github.com/waku-org/go-waku/waku/v2/peermanager"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PeerExchangeParameters struct {
|
type PeerExchangeParameters struct {
|
||||||
|
limiter *rate.Limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*PeerExchangeParameters)
|
||||||
|
|
||||||
|
// WithRateLimiter is an option used to specify a rate limiter for requests received in lightpush protocol
|
||||||
|
func WithRateLimiter(r rate.Limit, b int) Option {
|
||||||
|
return func(params *PeerExchangeParameters) {
|
||||||
|
params.limiter = rate.NewLimiter(r, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PeerExchangeRequestParameters struct {
|
||||||
host host.Host
|
host host.Host
|
||||||
selectedPeer peer.ID
|
selectedPeer peer.ID
|
||||||
peerAddr multiaddr.Multiaddr
|
peerAddr multiaddr.Multiaddr
|
||||||
@ -22,11 +36,11 @@ type PeerExchangeParameters struct {
|
|||||||
clusterID int
|
clusterID int
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerExchangeOption func(*PeerExchangeParameters) error
|
type RequestOption func(*PeerExchangeRequestParameters) error
|
||||||
|
|
||||||
// WithPeer is an option used to specify the peerID to fetch peers from
|
// WithPeer is an option used to specify the peerID to fetch peers from
|
||||||
func WithPeer(p peer.ID) PeerExchangeOption {
|
func WithPeer(p peer.ID) RequestOption {
|
||||||
return func(params *PeerExchangeParameters) error {
|
return func(params *PeerExchangeRequestParameters) error {
|
||||||
params.selectedPeer = p
|
params.selectedPeer = p
|
||||||
if params.peerAddr != nil {
|
if params.peerAddr != nil {
|
||||||
return errors.New("peerAddr and peerId options are mutually exclusive")
|
return errors.New("peerAddr and peerId options are mutually exclusive")
|
||||||
@ -38,8 +52,8 @@ func WithPeer(p peer.ID) PeerExchangeOption {
|
|||||||
// WithPeerAddr is an option used to specify a peerAddress to fetch peers from
|
// WithPeerAddr is an option used to specify a peerAddress to fetch peers from
|
||||||
// This new peer will be added to peerStore.
|
// This new peer will be added to peerStore.
|
||||||
// Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used.
|
// Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used.
|
||||||
func WithPeerAddr(pAddr multiaddr.Multiaddr) PeerExchangeOption {
|
func WithPeerAddr(pAddr multiaddr.Multiaddr) RequestOption {
|
||||||
return func(params *PeerExchangeParameters) error {
|
return func(params *PeerExchangeRequestParameters) error {
|
||||||
params.peerAddr = pAddr
|
params.peerAddr = pAddr
|
||||||
if params.selectedPeer != "" {
|
if params.selectedPeer != "" {
|
||||||
return errors.New("peerAddr and peerId options are mutually exclusive")
|
return errors.New("peerAddr and peerId options are mutually exclusive")
|
||||||
@ -53,8 +67,8 @@ func WithPeerAddr(pAddr multiaddr.Multiaddr) PeerExchangeOption {
|
|||||||
// from that list assuming it supports the chosen protocol, otherwise it will chose a peer
|
// from that list assuming it supports the chosen protocol, otherwise it will chose a peer
|
||||||
// from the node peerstore
|
// from the node peerstore
|
||||||
// Note: this option can only be used if WakuNode is initialized which internally intializes the peerManager
|
// Note: this option can only be used if WakuNode is initialized which internally intializes the peerManager
|
||||||
func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption {
|
func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) RequestOption {
|
||||||
return func(params *PeerExchangeParameters) error {
|
return func(params *PeerExchangeRequestParameters) error {
|
||||||
params.peerSelectionType = peermanager.Automatic
|
params.peerSelectionType = peermanager.Automatic
|
||||||
params.preferredPeers = fromThesePeers
|
params.preferredPeers = fromThesePeers
|
||||||
return nil
|
return nil
|
||||||
@ -65,8 +79,8 @@ func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption {
|
|||||||
// with the lowest ping. If a list of specific peers is passed, the peer will be chosen
|
// with the lowest ping. If a list of specific peers is passed, the peer will be chosen
|
||||||
// from that list assuming it supports the chosen protocol, otherwise it will chose a peer
|
// from that list assuming it supports the chosen protocol, otherwise it will chose a peer
|
||||||
// from the node peerstore
|
// from the node peerstore
|
||||||
func WithFastestPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption {
|
func WithFastestPeerSelection(fromThesePeers ...peer.ID) RequestOption {
|
||||||
return func(params *PeerExchangeParameters) error {
|
return func(params *PeerExchangeRequestParameters) error {
|
||||||
params.peerSelectionType = peermanager.LowestRTT
|
params.peerSelectionType = peermanager.LowestRTT
|
||||||
params.preferredPeers = fromThesePeers
|
params.preferredPeers = fromThesePeers
|
||||||
return nil
|
return nil
|
||||||
@ -74,15 +88,15 @@ func WithFastestPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultOptions are the default options to be used when using the lightpush protocol
|
// DefaultOptions are the default options to be used when using the lightpush protocol
|
||||||
func DefaultOptions(host host.Host) []PeerExchangeOption {
|
func DefaultOptions(host host.Host) []RequestOption {
|
||||||
return []PeerExchangeOption{
|
return []RequestOption{
|
||||||
WithAutomaticPeerSelection(),
|
WithAutomaticPeerSelection(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this if you want to filter peers by specific shards
|
// Use this if you want to filter peers by specific shards
|
||||||
func FilterByShard(clusterID int, shard int) PeerExchangeOption {
|
func FilterByShard(clusterID int, shard int) RequestOption {
|
||||||
return func(params *PeerExchangeParameters) error {
|
return func(params *PeerExchangeRequestParameters) error {
|
||||||
params.shard = shard
|
params.shard = shard
|
||||||
params.clusterID = clusterID
|
params.clusterID = clusterID
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user