github.com/ava-labs/avalanchego@v1.11.11/network/throttling/inbound_msg_throttler.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package throttling 5 6 import ( 7 "context" 8 9 "github.com/prometheus/client_golang/prometheus" 10 11 "github.com/ava-labs/avalanchego/ids" 12 "github.com/ava-labs/avalanchego/snow/networking/tracker" 13 "github.com/ava-labs/avalanchego/snow/validators" 14 "github.com/ava-labs/avalanchego/utils/logging" 15 ) 16 17 var _ InboundMsgThrottler = (*inboundMsgThrottler)(nil) 18 19 // InboundMsgThrottler rate-limits inbound messages from the network. 20 type InboundMsgThrottler interface { 21 // Blocks until [nodeID] can read a message of size [msgSize]. 22 // AddNode([nodeID], ...) must have been called since 23 // the last time RemoveNode([nodeID]) was called, if any. 24 // It's safe for multiple goroutines to concurrently call Acquire. 25 // Returns immediately if [ctx] is canceled. The returned release function 26 // needs to be called so that any allocated resources will be released 27 // invariant: There should be a maximum of 1 blocking call to Acquire for a 28 // given nodeID. Callers must enforce this invariant. 29 Acquire(ctx context.Context, msgSize uint64, nodeID ids.NodeID) ReleaseFunc 30 31 // Add a new node to this throttler. 32 // Must be called before Acquire(..., [nodeID]) is called. 33 // RemoveNode([nodeID]) must have been called since the last time 34 // AddNode([nodeID], ...) was called, if any. 35 AddNode(nodeID ids.NodeID) 36 37 // Remove a node from this throttler. 38 // AddNode([nodeID], ...) must have been called since 39 // the last time RemoveNode([nodeID]) was called, if any. 40 // Must be called when we stop reading messages from [nodeID]. 41 // It's safe for multiple goroutines to concurrently call RemoveNode. 42 RemoveNode(nodeID ids.NodeID) 43 } 44 45 type InboundMsgThrottlerConfig struct { 46 MsgByteThrottlerConfig `json:"byteThrottlerConfig"` 47 BandwidthThrottlerConfig `json:"bandwidthThrottlerConfig"` 48 CPUThrottlerConfig SystemThrottlerConfig `json:"cpuThrottlerConfig"` 49 DiskThrottlerConfig SystemThrottlerConfig `json:"diskThrottlerConfig"` 50 MaxProcessingMsgsPerNode uint64 `json:"maxProcessingMsgsPerNode"` 51 } 52 53 // Returns a new, sybil-safe inbound message throttler. 54 func NewInboundMsgThrottler( 55 log logging.Logger, 56 registerer prometheus.Registerer, 57 vdrs validators.Manager, 58 throttlerConfig InboundMsgThrottlerConfig, 59 resourceTracker tracker.ResourceTracker, 60 cpuTargeter tracker.Targeter, 61 diskTargeter tracker.Targeter, 62 ) (InboundMsgThrottler, error) { 63 byteThrottler, err := newInboundMsgByteThrottler( 64 log, 65 registerer, 66 vdrs, 67 throttlerConfig.MsgByteThrottlerConfig, 68 ) 69 if err != nil { 70 return nil, err 71 } 72 bufferThrottler, err := newInboundMsgBufferThrottler( 73 registerer, 74 throttlerConfig.MaxProcessingMsgsPerNode, 75 ) 76 if err != nil { 77 return nil, err 78 } 79 bandwidthThrottler, err := newBandwidthThrottler( 80 log, 81 registerer, 82 throttlerConfig.BandwidthThrottlerConfig, 83 ) 84 if err != nil { 85 return nil, err 86 } 87 cpuThrottler, err := NewSystemThrottler( 88 "cpu", 89 registerer, 90 throttlerConfig.CPUThrottlerConfig, 91 resourceTracker.CPUTracker(), 92 cpuTargeter, 93 ) 94 if err != nil { 95 return nil, err 96 } 97 diskThrottler, err := NewSystemThrottler( 98 "disk", 99 registerer, 100 throttlerConfig.DiskThrottlerConfig, 101 resourceTracker.DiskTracker(), 102 diskTargeter, 103 ) 104 if err != nil { 105 return nil, err 106 } 107 return &inboundMsgThrottler{ 108 byteThrottler: byteThrottler, 109 bufferThrottler: bufferThrottler, 110 bandwidthThrottler: bandwidthThrottler, 111 cpuThrottler: cpuThrottler, 112 diskThrottler: diskThrottler, 113 }, nil 114 } 115 116 // A sybil-safe inbound message throttler. 117 // Rate-limits reading of inbound messages to prevent peers from consuming 118 // excess resources. 119 // The three resources considered are: 120 // 121 // 1. An inbound message buffer, where each message that we're currently 122 // processing takes up 1 unit of space on the buffer. 123 // 2. An inbound message byte buffer, where a message of length n 124 // that we're currently processing takes up n units of space on the buffer. 125 // 3. Bandwidth. The bandwidth rate-limiting is implemented using a token 126 // bucket, where each token is 1 byte. See BandwidthThrottler. 127 // 128 // A call to Acquire([msgSize], [nodeID]) blocks until we've secured 129 // enough of both these resources to read a message of size [msgSize] from 130 // [nodeID]. 131 type inboundMsgThrottler struct { 132 // Rate-limits based on number of messages from a given node that we're 133 // currently processing. 134 bufferThrottler *inboundMsgBufferThrottler 135 // Rate-limits based on recent bandwidth usage 136 bandwidthThrottler bandwidthThrottler 137 // Rate-limits based on size of all messages from a given 138 // node that we're currently processing. 139 byteThrottler *inboundMsgByteThrottler 140 // Rate-limits based on CPU usage caused by a given node. 141 cpuThrottler SystemThrottler 142 // Rate-limits based on disk usage caused by a given node. 143 diskThrottler SystemThrottler 144 } 145 146 // Returns when we can read a message of size [msgSize] from node [nodeID]. 147 // Release([msgSize], [nodeID]) must be called (!) when done with the message 148 // or when we give up trying to read the message, if applicable. 149 // Even if [ctx] is canceled, The returned release function 150 // needs to be called so that any allocated resources will be released. 151 func (t *inboundMsgThrottler) Acquire(ctx context.Context, msgSize uint64, nodeID ids.NodeID) ReleaseFunc { 152 // Acquire space on the inbound message buffer 153 bufferRelease := t.bufferThrottler.Acquire(ctx, nodeID) 154 // Acquire bandwidth 155 t.bandwidthThrottler.Acquire(ctx, msgSize, nodeID) 156 // Wait until our CPU usage drops to an acceptable level. 157 t.cpuThrottler.Acquire(ctx, nodeID) 158 // Wait until our disk usage drops to an acceptable level. 159 t.diskThrottler.Acquire(ctx, nodeID) 160 // Acquire space on the inbound message byte buffer 161 byteRelease := t.byteThrottler.Acquire(ctx, msgSize, nodeID) 162 return func() { 163 bufferRelease() 164 byteRelease() 165 } 166 } 167 168 // See BandwidthThrottler. 169 func (t *inboundMsgThrottler) AddNode(nodeID ids.NodeID) { 170 t.bandwidthThrottler.AddNode(nodeID) 171 } 172 173 // See BandwidthThrottler. 174 func (t *inboundMsgThrottler) RemoveNode(nodeID ids.NodeID) { 175 t.bandwidthThrottler.RemoveNode(nodeID) 176 }