github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/unicast/manager_test.go (about) 1 package unicast_test 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 libp2pnet "github.com/libp2p/go-libp2p/core/network" 9 "github.com/libp2p/go-libp2p/core/protocol" 10 "github.com/libp2p/go-libp2p/p2p/net/swarm" 11 "github.com/stretchr/testify/mock" 12 "github.com/stretchr/testify/require" 13 14 "github.com/onflow/flow-go/config" 15 "github.com/onflow/flow-go/module/metrics" 16 mockp2p "github.com/onflow/flow-go/network/p2p/mock" 17 p2ptest "github.com/onflow/flow-go/network/p2p/test" 18 "github.com/onflow/flow-go/network/p2p/unicast" 19 unicastcache "github.com/onflow/flow-go/network/p2p/unicast/cache" 20 "github.com/onflow/flow-go/network/p2p/unicast/stream" 21 "github.com/onflow/flow-go/utils/unittest" 22 ) 23 24 func unicastManagerFixture(t *testing.T) (*unicast.Manager, *mockp2p.StreamFactory, unicast.ConfigCache) { 25 streamFactory := mockp2p.NewStreamFactory(t) 26 streamFactory.On("SetStreamHandler", mock.AnythingOfType("protocol.ID"), mock.AnythingOfType("network.StreamHandler")).Return().Once() 27 28 cfg, err := config.DefaultConfig() 29 require.NoError(t, err) 30 31 unicastConfigCache := unicastcache.NewUnicastConfigCache(cfg.NetworkConfig.Unicast.UnicastManager.ConfigCacheSize, 32 unittest.Logger(), 33 metrics.NewNoopCollector(), 34 func() unicast.Config { 35 return unicast.Config{ 36 StreamCreationRetryAttemptBudget: cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, 37 } 38 }) 39 40 mgr, err := unicast.NewUnicastManager(&unicast.ManagerConfig{ 41 Logger: unittest.Logger(), 42 StreamFactory: streamFactory, 43 SporkId: unittest.IdentifierFixture(), 44 Metrics: metrics.NewNoopCollector(), 45 Parameters: &cfg.NetworkConfig.Unicast.UnicastManager, 46 UnicastConfigCacheFactory: func(func() unicast.Config) unicast.ConfigCache { 47 return unicastConfigCache 48 }, 49 }) 50 require.NoError(t, err) 51 mgr.SetDefaultHandler(func(libp2pnet.Stream) {}) // no-op handler, we don't care about the handler for this test 52 53 return mgr, streamFactory, unicastConfigCache 54 } 55 56 // TestManagerConfigValidation tests the validation of the unicast manager config. 57 // It tests that the config is valid when all the required fields are provided. 58 func TestManagerConfigValidation(t *testing.T) { 59 cfg, err := config.DefaultConfig() 60 require.NoError(t, err) 61 62 validConfig := unicast.ManagerConfig{ 63 Logger: unittest.Logger(), 64 StreamFactory: mockp2p.NewStreamFactory(t), 65 SporkId: unittest.IdentifierFixture(), 66 Parameters: &cfg.NetworkConfig.Unicast.UnicastManager, 67 Metrics: metrics.NewNoopCollector(), 68 UnicastConfigCacheFactory: func(func() unicast.Config) unicast.ConfigCache { 69 return unicastcache.NewUnicastConfigCache(cfg.NetworkConfig.Unicast.UnicastManager.ConfigCacheSize, 70 unittest.Logger(), 71 metrics.NewNoopCollector(), 72 func() unicast.Config { 73 return unicast.Config{ 74 StreamCreationRetryAttemptBudget: cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, 75 } 76 }) 77 }, 78 } 79 80 t.Run("Valid Config", func(t *testing.T) { 81 mgr, err := unicast.NewUnicastManager(&validConfig) 82 require.NoError(t, err) 83 require.NotNil(t, mgr) 84 }) 85 86 t.Run("Missing Fields", func(t *testing.T) { 87 cfg := &unicast.ManagerConfig{} 88 mgr, err := unicast.NewUnicastManager(cfg) 89 require.Error(t, err) 90 require.Nil(t, mgr) 91 }) 92 93 t.Run("Nil Parameters", func(t *testing.T) { 94 cfg := validConfig 95 cfg.Parameters = nil 96 mgr, err := unicast.NewUnicastManager(&cfg) 97 require.Error(t, err) 98 require.Nil(t, mgr) 99 }) 100 101 t.Run("Invalid UnicastConfigCacheFactory", func(t *testing.T) { 102 cfg := validConfig 103 cfg.UnicastConfigCacheFactory = nil 104 mgr, err := unicast.NewUnicastManager(&cfg) 105 require.Error(t, err) 106 require.Nil(t, mgr) 107 }) 108 109 t.Run("Missing StreamFactory", func(t *testing.T) { 110 cfg := validConfig 111 cfg.StreamFactory = nil 112 mgr, err := unicast.NewUnicastManager(&cfg) 113 require.Error(t, err) 114 require.Nil(t, mgr) 115 }) 116 117 t.Run("Missing Metrics", func(t *testing.T) { 118 cfg := validConfig 119 cfg.Metrics = nil 120 mgr, err := unicast.NewUnicastManager(&cfg) 121 require.Error(t, err) 122 require.Nil(t, mgr) 123 }) 124 } 125 126 // TestUnicastManager_SuccessfulStream tests that when CreateStream is successful on the first attempt for stream creation, 127 // it updates the consecutive successful stream counter. 128 func TestUnicastManager_SuccessfulStream(t *testing.T) { 129 peerID := unittest.PeerIdFixture(t) 130 mgr, streamFactory, configCache := unicastManagerFixture(t) 131 132 cfg, err := config.DefaultConfig() 133 require.NoError(t, err) 134 135 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Once() 136 137 ctx, cancel := context.WithCancel(context.Background()) 138 defer cancel() 139 140 s, err := mgr.CreateStream(ctx, peerID) 141 require.NoError(t, err) 142 require.NotNil(t, s) 143 144 // The unicast config must be updated with the backoff budget decremented. 145 unicastCfg, err := configCache.GetWithInit(peerID) 146 require.NoError(t, err) 147 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget) // stream backoff budget must remain intact. 148 require.Equal(t, uint64(1), unicastCfg.ConsecutiveSuccessfulStream) // consecutive successful stream must incremented. 149 } 150 151 // TestUnicastManager_StreamBackoff tests the backoff mechanism of the unicast manager for stream creation. 152 // It tests the situation that CreateStream is called but the stream creation fails. 153 // It tests that it tries to create a stream some number of times (unicastmodel.MaxStreamCreationAttemptTimes), before giving up. 154 // It also checks the consecutive successful stream counter is reset when the stream creation fails. 155 func TestUnicastManager_StreamBackoff(t *testing.T) { 156 peerID := unittest.PeerIdFixture(t) 157 mgr, streamFactory, configCache := unicastManagerFixture(t) 158 159 cfg, err := config.DefaultConfig() 160 require.NoError(t, err) 161 162 // mocks that it attempts to create a stream some number of times, before giving up. 163 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 164 Return(nil, fmt.Errorf("some error")). 165 Times(int(cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes + 1)) 166 167 ctx, cancel := context.WithCancel(context.Background()) 168 defer cancel() 169 170 s, err := mgr.CreateStream(ctx, peerID) 171 require.Error(t, err) 172 require.Nil(t, s) 173 174 // The unicast config must be updated with the backoff budget decremented. 175 unicastCfg, err := configCache.GetWithInit(peerID) 176 require.NoError(t, err) 177 // stream backoff budget must be decremented by 1 since all budget is used up. 178 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget) 179 // consecutive successful stream must be reset to zero, since the stream creation failed. 180 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 181 } 182 183 // TestUnicastManager_StreamFactory_StreamBackoff tests the backoff mechanism of the unicast manager for stream creation. 184 // It tests when there is a connection, but no stream, it tries to create a stream some number of times (unicastmodel.MaxStreamCreationAttemptTimes), before 185 // giving up. 186 func TestUnicastManager_StreamFactory_StreamBackoff(t *testing.T) { 187 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 188 peerID := unittest.PeerIdFixture(t) 189 190 cfg, err := config.DefaultConfig() 191 require.NoError(t, err) 192 193 // mocks that it attempts to create a stream some number of times, before giving up. 194 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 195 Return(nil, fmt.Errorf("some error")). 196 Times(int(cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes + 1)) 197 198 ctx, cancel := context.WithCancel(context.Background()) 199 defer cancel() 200 s, err := mgr.CreateStream(ctx, peerID) 201 require.Error(t, err) 202 require.Nil(t, s) 203 204 // The unicast config must be updated with the stream backoff budget decremented. 205 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 206 require.NoError(t, err) 207 // stream backoff budget must be decremented by 1. 208 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget) 209 // consecutive successful stream must be zero as we have not created a successful stream yet. 210 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 211 } 212 213 // TestUnicastManager_Stream_ConsecutiveStreamCreation_Increment tests that when stream creation is successful, 214 // it increments the consecutive successful stream counter in the unicast config. 215 func TestUnicastManager_Stream_ConsecutiveStreamCreation_Increment(t *testing.T) { 216 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 217 peerID := unittest.PeerIdFixture(t) 218 219 cfg, err := config.DefaultConfig() 220 require.NoError(t, err) 221 222 // total times we successfully create a stream to the peer. 223 totalSuccessAttempts := 10 224 225 // mocks that it attempts to create a stream 10 times, and each time it succeeds. 226 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Times(totalSuccessAttempts) 227 228 ctx, cancel := context.WithCancel(context.Background()) 229 defer cancel() 230 231 for i := 0; i < totalSuccessAttempts; i++ { 232 s, err := mgr.CreateStream(ctx, peerID) 233 require.NoError(t, err) 234 require.NotNil(t, s) 235 236 // The unicast config must be updated with the stream backoff budget decremented. 237 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 238 require.NoError(t, err) 239 // stream backoff budget must be intact (all stream creation attempts are successful). 240 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget) 241 // consecutive successful stream must be incremented. 242 require.Equal(t, uint64(i+1), unicastCfg.ConsecutiveSuccessfulStream) 243 } 244 } 245 246 // TestUnicastManager_Stream_ConsecutiveStreamCreation_Reset tests that when the stream creation fails, it resets 247 // the consecutive successful stream counter in the unicast config. 248 func TestUnicastManager_Stream_ConsecutiveStreamCreation_Reset(t *testing.T) { 249 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 250 peerID := unittest.PeerIdFixture(t) 251 252 // mocks that it attempts to create a stream once and fails. 253 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 254 Return(nil, fmt.Errorf("some error")). 255 Once() 256 257 adjustedUnicastConfig, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) { 258 // sets the consecutive successful stream to 5 meaning that the last 5 stream creation attempts were successful. 259 unicastConfig.ConsecutiveSuccessfulStream = 5 260 // sets the stream back budget to 0 meaning that the stream backoff budget is exhausted. 261 unicastConfig.StreamCreationRetryAttemptBudget = 0 262 263 return unicastConfig, nil 264 }) 265 require.NoError(t, err) 266 require.Equal(t, uint64(5), adjustedUnicastConfig.ConsecutiveSuccessfulStream) 267 268 ctx, cancel := context.WithCancel(context.Background()) 269 defer cancel() 270 271 s, err := mgr.CreateStream(ctx, peerID) 272 require.Error(t, err) 273 require.Nil(t, s) 274 275 // The unicast config must be updated with the stream backoff budget decremented. 276 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 277 require.NoError(t, err) 278 279 // stream backoff budget must be intact (we can't decrement it below 0). 280 require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) 281 // consecutive successful stream must be reset to 0. 282 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 283 } 284 285 // TestUnicastManager_StreamFactory_ErrProtocolNotSupported tests that when there is a protocol not supported error, it does not retry creating a stream. 286 func TestUnicastManager_StreamFactory_ErrProtocolNotSupported(t *testing.T) { 287 mgr, streamFactory, _ := unicastManagerFixture(t) 288 peerID := unittest.PeerIdFixture(t) 289 290 // mocks that upon creating a stream, it returns a protocol not supported error, the mock is set to once, meaning that it won't retry stream creation again. 291 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 292 Return(nil, stream.NewProtocolNotSupportedErr(peerID, protocol.ID("protocol-1"), fmt.Errorf("some error"))). 293 Once() 294 295 ctx, cancel := context.WithCancel(context.Background()) 296 defer cancel() 297 s, err := mgr.CreateStream(ctx, peerID) 298 require.Error(t, err) 299 require.Nil(t, s) 300 } 301 302 // TestUnicastManager_StreamFactory_ErrNoAddresses tests that when stream creation returns a no addresses error, 303 // it does not retry stream creation again and returns an error immediately. 304 func TestUnicastManager_StreamFactory_ErrNoAddresses(t *testing.T) { 305 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 306 307 cfg, err := config.DefaultConfig() 308 require.NoError(t, err) 309 310 peerID := unittest.PeerIdFixture(t) 311 312 // mocks that stream creation returns a no addresses error, and the mock is set to once, meaning that it won't retry stream creation again. 313 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 314 Return(nil, fmt.Errorf("some error to ensure wrapping works fine: %w", swarm.ErrNoAddresses)). 315 Once() 316 317 ctx, cancel := context.WithCancel(context.Background()) 318 defer cancel() 319 s, err := mgr.CreateStream(ctx, peerID) 320 require.Error(t, err) 321 require.Nil(t, s) 322 323 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 324 require.NoError(t, err) 325 326 // stream backoff budget must be reduced by 1 due to failed stream creation. 327 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget) 328 // consecutive successful stream must be set to zero. 329 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 330 } 331 332 // TestUnicastManager_Stream_ErrSecurityProtocolNegotiationFailed tests that when there is a security protocol negotiation error, it does not retry stream creation. 333 func TestUnicastManager_Stream_ErrSecurityProtocolNegotiationFailed(t *testing.T) { 334 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 335 336 cfg, err := config.DefaultConfig() 337 require.NoError(t, err) 338 339 peerID := unittest.PeerIdFixture(t) 340 341 // mocks that stream creation returns a security protocol negotiation error, and the mock is set to once, meaning that it won't retry stream creation. 342 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 343 Return(nil, stream.NewSecurityProtocolNegotiationErr(peerID, fmt.Errorf("some error"))). 344 Once() 345 346 ctx, cancel := context.WithCancel(context.Background()) 347 defer cancel() 348 s, err := mgr.CreateStream(ctx, peerID) 349 require.Error(t, err) 350 require.Nil(t, s) 351 352 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 353 require.NoError(t, err) 354 // stream retry budget must be decremented by 1 (since we didn't have a successful stream creation, the budget is decremented). 355 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget) 356 // consecutive successful stream must be set to zero. 357 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 358 } 359 360 // TestUnicastManager_StreamFactory_ErrGaterDisallowedConnection tests that when there is a connection-gater disallow listing error, it does not retry stream creation. 361 func TestUnicastManager_StreamFactory_ErrGaterDisallowedConnection(t *testing.T) { 362 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 363 peerID := unittest.PeerIdFixture(t) 364 365 cfg, err := config.DefaultConfig() 366 require.NoError(t, err) 367 368 // mocks that stream creation to the peer returns a connection gater disallow-listing, and the mock is set to once, meaning that it won't retry stream creation. 369 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 370 Return(nil, stream.NewGaterDisallowedConnectionErr(fmt.Errorf("some error"))). 371 Once() 372 373 ctx, cancel := context.WithCancel(context.Background()) 374 defer cancel() 375 s, err := mgr.CreateStream(ctx, peerID) 376 require.Error(t, err) 377 require.Nil(t, s) 378 379 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 380 require.NoError(t, err) 381 // stream backoff budget must be reduced by 1 due to failed stream creation. 382 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes-1, unicastCfg.StreamCreationRetryAttemptBudget) 383 // consecutive successful stream must be set to zero. 384 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) 385 } 386 387 // TestUnicastManager_Connection_BackoffBudgetDecremented tests that everytime the unicast manger gives up on creating a stream (after retrials), 388 // it decrements the backoff budget for the remote peer. 389 func TestUnicastManager_Stream_BackoffBudgetDecremented(t *testing.T) { 390 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 391 peerID := unittest.PeerIdFixture(t) 392 393 cfg, err := config.DefaultConfig() 394 require.NoError(t, err) 395 396 // totalAttempts is the total number of times that unicast manager calls NewStream on the stream factory to create stream to the peer. 397 // Note that it already assumes that the connection is established, so it does not try to connect to the peer. 398 // Let's consider x = unicastmodel.MaxStreamCreationRetryAttemptTimes + 1. Then the test tries x times CreateStream. With dynamic backoffs, 399 // the first CreateStream call will try to NewStream x times, the second CreateStream call will try to NewStream x-1 times, 400 // and so on. So the total number of Connect calls is x + (x-1) + (x-2) + ... + 1 = x(x+1)/2. 401 maxStreamRetryBudget := cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes 402 maxStreamAttempt := maxStreamRetryBudget + 1 // 1 attempt + retry times 403 totalAttempts := maxStreamAttempt * (maxStreamAttempt + 1) / 2 404 405 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything). 406 Return(nil, fmt.Errorf("some error")). 407 Times(int(totalAttempts)) 408 409 ctx, cancel := context.WithCancel(context.Background()) 410 defer cancel() 411 for i := 0; i < int(maxStreamRetryBudget); i++ { 412 s, err := mgr.CreateStream(ctx, peerID) 413 require.Error(t, err) 414 require.Nil(t, s) 415 416 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 417 require.NoError(t, err) 418 419 if i == int(maxStreamRetryBudget)-1 { 420 require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) 421 } else { 422 require.Equal(t, maxStreamRetryBudget-uint64(i)-1, unicastCfg.StreamCreationRetryAttemptBudget) 423 } 424 } 425 // At this time the backoff budget for connection must be 0. 426 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 427 require.NoError(t, err) 428 require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) 429 430 // After all the backoff budget is used up, it should stay at 0. 431 s, err := mgr.CreateStream(ctx, peerID) 432 require.Error(t, err) 433 require.Nil(t, s) 434 435 unicastCfg, err = unicastConfigCache.GetWithInit(peerID) 436 require.NoError(t, err) 437 require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) 438 } 439 440 // TestUnicastManager_Stream_BackoffBudgetResetToDefault tests that when the stream retry attempt budget is zero, and the consecutive successful stream counter is above the reset threshold, 441 // it resets the stream retry attempt budget to the default value and increments the consecutive successful stream counter. 442 func TestUnicastManager_Stream_BackoffBudgetResetToDefault(t *testing.T) { 443 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 444 peerID := unittest.PeerIdFixture(t) 445 446 cfg, err := config.DefaultConfig() 447 require.NoError(t, err) 448 449 // mocks that it attempts to create a stream once and succeeds. 450 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(&p2ptest.MockStream{}, nil).Once() 451 452 // update the unicast config of the peer to have a zero stream backoff budget but a consecutive successful stream counter above the reset threshold. 453 adjustedCfg, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) { 454 unicastConfig.StreamCreationRetryAttemptBudget = 0 455 unicastConfig.ConsecutiveSuccessfulStream = cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold + 1 456 return unicastConfig, nil 457 }) 458 require.NoError(t, err) 459 require.Equal(t, uint64(0), adjustedCfg.StreamCreationRetryAttemptBudget) 460 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold+1, adjustedCfg.ConsecutiveSuccessfulStream) 461 462 ctx, cancel := context.WithCancel(context.Background()) 463 defer cancel() 464 465 s, err := mgr.CreateStream(ctx, peerID) 466 require.NoError(t, err) 467 require.NotNil(t, s) 468 469 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 470 require.NoError(t, err) 471 // stream backoff budget must reset to default. 472 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.MaxStreamCreationRetryAttemptTimes, unicastCfg.StreamCreationRetryAttemptBudget) 473 // consecutive successful stream must increment by 1 (it was threshold + 1 before). 474 require.Equal(t, cfg.NetworkConfig.Unicast.UnicastManager.StreamZeroRetryResetThreshold+1+1, unicastCfg.ConsecutiveSuccessfulStream) 475 } 476 477 // TestUnicastManager_Stream_NoBackoff_When_Budget_Is_Zero tests that when the stream backoff budget is zero and the consecutive successful stream counter is not above the 478 // zero rest threshold, the unicast manager does not backoff if the stream creation attempt fails. 479 func TestUnicastManager_Stream_NoBackoff_When_Budget_Is_Zero(t *testing.T) { 480 mgr, streamFactory, unicastConfigCache := unicastManagerFixture(t) 481 peerID := unittest.PeerIdFixture(t) 482 483 // mocks that it attempts to create a stream once and fails, and does not retry. 484 streamFactory.On("NewStream", mock.Anything, peerID, mock.Anything).Return(nil, fmt.Errorf("some error")).Once() 485 486 adjustedCfg, err := unicastConfigCache.AdjustWithInit(peerID, func(unicastConfig unicast.Config) (unicast.Config, error) { 487 unicastConfig.ConsecutiveSuccessfulStream = 2 // set the consecutive successful stream to 2, which is below the reset threshold. 488 unicastConfig.StreamCreationRetryAttemptBudget = 0 // set the stream backoff budget to 0, meaning that the stream backoff budget is exhausted. 489 return unicastConfig, nil 490 }) 491 require.NoError(t, err) 492 require.Equal(t, uint64(0), adjustedCfg.StreamCreationRetryAttemptBudget) 493 require.Equal(t, uint64(2), adjustedCfg.ConsecutiveSuccessfulStream) 494 495 ctx, cancel := context.WithCancel(context.Background()) 496 defer cancel() 497 498 s, err := mgr.CreateStream(ctx, peerID) 499 require.Error(t, err) 500 require.Nil(t, s) 501 502 unicastCfg, err := unicastConfigCache.GetWithInit(peerID) 503 require.NoError(t, err) 504 require.Equal(t, uint64(0), unicastCfg.StreamCreationRetryAttemptBudget) // stream backoff budget must remain zero. 505 require.Equal(t, uint64(0), unicastCfg.ConsecutiveSuccessfulStream) // consecutive successful stream must be set to zero. 506 }