github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/tracer/internal/rpc_sent_tracker.go (about) 1 package internal 2 3 import ( 4 "crypto/rand" 5 "fmt" 6 "sync" 7 "time" 8 9 pubsub "github.com/libp2p/go-libp2p-pubsub" 10 pb "github.com/libp2p/go-libp2p-pubsub/pb" 11 "github.com/rs/zerolog" 12 13 "github.com/onflow/flow-go/engine/common/worker" 14 "github.com/onflow/flow-go/module" 15 "github.com/onflow/flow-go/module/component" 16 "github.com/onflow/flow-go/module/mempool/queue" 17 p2pmsg "github.com/onflow/flow-go/network/p2p/message" 18 ) 19 20 const ( 21 iHaveRPCTrackedLog = "ihave rpc tracked successfully" 22 ) 23 24 // trackableRPC is an internal data structure for "temporarily" storing *pubsub.RPC sent in the queue before they are processed 25 // by the *RPCSentTracker. 26 type trackableRPC struct { 27 // Nonce prevents deduplication in the hero store 28 Nonce []byte 29 rpc *pubsub.RPC 30 } 31 32 // lastHighestIHaveRPCSize tracks the last highest rpc control message size the time stamp it was last updated. 33 type lastHighestIHaveRPCSize struct { 34 sync.RWMutex 35 lastSize int64 36 lastUpdate time.Time 37 } 38 39 // RPCSentTracker tracks RPC messages and the size of the last largest iHave rpc control message sent. 40 type RPCSentTracker struct { 41 component.Component 42 *lastHighestIHaveRPCSize 43 logger zerolog.Logger 44 cache *rpcSentCache 45 workerPool *worker.Pool[trackableRPC] 46 lastHighestIHaveRPCSizeResetInterval time.Duration 47 } 48 49 // RPCSentTrackerConfig configuration for the RPCSentTracker. 50 type RPCSentTrackerConfig struct { 51 Logger zerolog.Logger 52 //RPCSentCacheSize size of the *rpcSentCache cache. 53 RPCSentCacheSize uint32 54 // RPCSentCacheCollector metrics collector for the *rpcSentCache cache. 55 RPCSentCacheCollector module.HeroCacheMetrics 56 // WorkerQueueCacheCollector metrics factory for the worker pool. 57 WorkerQueueCacheCollector module.HeroCacheMetrics 58 // WorkerQueueCacheSize the worker pool herostore cache size. 59 WorkerQueueCacheSize uint32 60 // NumOfWorkers number of workers in the worker pool. 61 NumOfWorkers int 62 // LastHighestIhavesSentResetInterval the refresh interval to reset the lastHighestIHaveRPCSize. 63 LastHighestIhavesSentResetInterval time.Duration 64 } 65 66 // NewRPCSentTracker returns a new *NewRPCSentTracker. 67 func NewRPCSentTracker(config *RPCSentTrackerConfig) *RPCSentTracker { 68 cacheConfig := &rpcCtrlMsgSentCacheConfig{ 69 sizeLimit: config.RPCSentCacheSize, 70 logger: config.Logger, 71 collector: config.RPCSentCacheCollector, 72 } 73 74 store := queue.NewHeroStore( 75 config.WorkerQueueCacheSize, 76 config.Logger, 77 config.WorkerQueueCacheCollector) 78 79 tracker := &RPCSentTracker{ 80 logger: config.Logger.With().Str("component", "rpc_sent_tracker").Logger(), 81 lastHighestIHaveRPCSize: &lastHighestIHaveRPCSize{sync.RWMutex{}, 0, time.Now()}, 82 cache: newRPCSentCache(cacheConfig), 83 lastHighestIHaveRPCSizeResetInterval: config.LastHighestIhavesSentResetInterval, 84 } 85 tracker.workerPool = worker.NewWorkerPoolBuilder[trackableRPC]( 86 config.Logger, 87 store, 88 tracker.rpcSentWorkerLogic).Build() 89 90 builder := component.NewComponentManagerBuilder() 91 for i := 0; i < config.NumOfWorkers; i++ { 92 builder.AddWorker(tracker.workerPool.WorkerLogic()) 93 } 94 tracker.Component = builder.Build() 95 return tracker 96 } 97 98 // Track submits the control message to the worker queue for async tracking. 99 // Args: 100 // - *pubsub.RPC: the rpc sent. 101 // All errors returned from this function can be considered benign. 102 func (t *RPCSentTracker) Track(rpc *pubsub.RPC) error { 103 n, err := nonce() 104 if err != nil { 105 return fmt.Errorf("failed to get track rpc work nonce: %w", err) 106 } 107 108 if ok := t.workerPool.Submit(trackableRPC{Nonce: n, rpc: rpc}); !ok { 109 return fmt.Errorf("failed to track RPC could not submit work to worker pool") 110 } 111 return nil 112 } 113 114 // rpcSentWorkerLogic tracks control messages sent in *pubsub.RPC. 115 func (t *RPCSentTracker) rpcSentWorkerLogic(work trackableRPC) error { 116 switch { 117 case len(work.rpc.GetControl().GetIhave()) > 0: 118 iHave := work.rpc.GetControl().GetIhave() 119 numOfMessageIdsTracked := t.iHaveRPCSent(iHave) 120 lastHighestIHaveCount := t.updateLastHighestIHaveRPCSize(int64(numOfMessageIdsTracked)) 121 t.logger.Debug(). 122 Int("num_of_ihaves", len(iHave)). 123 Int("num_of_message_ids", numOfMessageIdsTracked). 124 Int64("last_highest_ihave_count", lastHighestIHaveCount). 125 Msg(iHaveRPCTrackedLog) 126 } 127 return nil 128 } 129 130 // updateLastHighestIHaveRPCSize updates the last highest if the provided size is larger than the current last highest or the reset interval has passed. 131 // Args: 132 // - size: size that was cached. 133 // Returns: 134 // - int64: the last highest size. 135 func (t *RPCSentTracker) updateLastHighestIHaveRPCSize(size int64) int64 { 136 t.Lock() 137 defer t.Unlock() 138 if t.lastSize < size || time.Since(t.lastUpdate) > t.lastHighestIHaveRPCSizeResetInterval { 139 // The last highest ihave RPC size is updated if the new size is larger than the current size, or if the time elapsed since the last update surpasses the reset interval. 140 t.lastSize = size 141 t.lastUpdate = time.Now() 142 } 143 return t.lastSize 144 } 145 146 // iHaveRPCSent caches a unique entity message ID for each message ID included in each rpc iHave control message. 147 // Args: 148 // - []*pb.ControlIHave: list of iHave control messages. 149 // Returns: 150 // - int: the number of message ids cached by the tracker. 151 func (t *RPCSentTracker) iHaveRPCSent(iHaves []*pb.ControlIHave) int { 152 controlMsgType := p2pmsg.CtrlMsgIHave 153 messageIDCount := 0 154 for _, iHave := range iHaves { 155 messageIDCount += len(iHave.GetMessageIDs()) 156 for _, messageID := range iHave.GetMessageIDs() { 157 t.cache.add(messageID, controlMsgType) 158 } 159 } 160 return messageIDCount 161 } 162 163 // WasIHaveRPCSent checks if an iHave control message with the provided message ID was sent. 164 // Args: 165 // - messageID: the message ID of the iHave RPC. 166 // Returns: 167 // - bool: true if the iHave rpc with the provided message ID was sent. 168 func (t *RPCSentTracker) WasIHaveRPCSent(messageID string) bool { 169 return t.cache.has(messageID, p2pmsg.CtrlMsgIHave) 170 } 171 172 // LastHighestIHaveRPCSize returns the last highest size of iHaves sent in a rpc. 173 // Returns: 174 // - int64: the last highest size. 175 func (t *RPCSentTracker) LastHighestIHaveRPCSize() int64 { 176 t.RLock() 177 defer t.RUnlock() 178 return t.lastSize 179 } 180 181 // nonce returns random string that is used to store unique items in herocache. 182 func nonce() ([]byte, error) { 183 b := make([]byte, 16) 184 _, err := rand.Read(b) 185 if err != nil { 186 return nil, err 187 } 188 return b, nil 189 }