generated from ipfs/ipfs-repository-template
-
Notifications
You must be signed in to change notification settings - Fork 146
bitswap/httpnet: Disconnect peers after client errors #919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a701672
bitswap/httpnet: Disconnect peers after client errors
hsanjuan 9de3a01
Fix error tracking test
hsanjuan ac569b2
Update changelog
hsanjuan 354981e
bitswap/httpnet: error_tracker: use normal mutex-locked map.
hsanjuan 28266d4
Merge remote-tracking branch 'origin/main' into bitswap-httpnet-disco…
hsanjuan 4073ce6
Merge branch 'main' into bitswap-httpnet-disconnect-eventually
hsanjuan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package httpnet | ||
|
|
||
| import ( | ||
| "errors" | ||
| "sync" | ||
| "sync/atomic" | ||
|
|
||
| "github.com/libp2p/go-libp2p/core/peer" | ||
| ) | ||
|
|
||
| var errThresholdCrossed = errors.New("the peer crossed the error threshold") | ||
|
|
||
| type errorTracker struct { | ||
| ht *Network | ||
|
|
||
| mux sync.RWMutex | ||
| errors map[peer.ID]*atomic.Uint64 | ||
| } | ||
|
|
||
| func newErrorTracker(ht *Network) *errorTracker { | ||
| return &errorTracker{ | ||
| ht: ht, | ||
| errors: make(map[peer.ID]*atomic.Uint64), | ||
| } | ||
| } | ||
|
|
||
| func (et *errorTracker) startTracking(p peer.ID) { | ||
| et.mux.Lock() | ||
| defer et.mux.Unlock() | ||
|
|
||
| // Initialize the error count for the peer if it doesn't exist | ||
| if _, exists := et.errors[p]; !exists { | ||
| et.errors[p] = &atomic.Uint64{} | ||
| } | ||
| } | ||
|
|
||
| func (et *errorTracker) stopTracking(p peer.ID) { | ||
| et.mux.Lock() | ||
| defer et.mux.Unlock() | ||
| delete(et.errors, p) | ||
| } | ||
|
|
||
| // logErrors adds n to the current error count for p. If the total count is above the threshold, then an error is returned. If n is 0, the the total count is reset to 0. | ||
| func (et *errorTracker) logErrors(p peer.ID, n uint64, threshold uint64) error { | ||
| et.mux.RLock() | ||
| count, ok := et.errors[p] | ||
| et.mux.RUnlock() | ||
|
|
||
| if !ok { | ||
| // i.e. we disconnected but there were pending requests | ||
| log.Debug("logging errors for untracked peer: %s", p) | ||
| return nil | ||
| } | ||
|
|
||
| if n == 0 { // reset error count | ||
| count.Store(0) | ||
| return nil | ||
| } | ||
|
|
||
| total := count.Add(n) | ||
| if total > threshold { | ||
| return errThresholdCrossed | ||
| } | ||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| package httpnet | ||
|
|
||
| // Write tests for the errorTracker implementation found in watcher.go | ||
| import ( | ||
| "sync" | ||
| "testing" | ||
|
|
||
| "github.com/libp2p/go-libp2p/core/peer" | ||
| ) | ||
|
|
||
| func TestErrorTracker_StartTracking(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Start tracking the peer | ||
| et.startTracking(p) | ||
|
|
||
| // Check if the error count is initialized to 0 | ||
| count, ok := et.errors[p] | ||
| if !ok { | ||
| t.Errorf("Expected peer %s to be tracked but it was not", p) | ||
| } | ||
| if count.Load() != 0 { | ||
| t.Errorf("Expected initial error count for peer %s to be 0 but got %d", p, count.Load()) | ||
| } | ||
| } | ||
|
|
||
| func TestErrorTracker_StopTracking(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Start tracking the peer | ||
| et.startTracking(p) | ||
|
|
||
| // Stop tracking the peer | ||
| et.stopTracking(p) | ||
|
|
||
| // Check if the error count is removed | ||
| if _, ok := et.errors[p]; ok { | ||
| t.Errorf("Expected peer %s to be untracked but it was still tracked", p) | ||
| } | ||
| } | ||
|
|
||
| func TestErrorTracker_LogErrors_Reset(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Start tracking the peer | ||
| et.startTracking(p) | ||
|
|
||
| // Log some errors | ||
| err := et.logErrors(p, 5, 10) | ||
| if err != nil { | ||
| t.Errorf("Expected no error when logging errors but got %v", err) | ||
| } | ||
|
|
||
| // Reset error count | ||
| err = et.logErrors(p, 0, 10) | ||
| if err != nil { | ||
| t.Errorf("Expected no error when resetting error count but got %v", err) | ||
| } | ||
|
|
||
| // Check if the error count is reset to 0 | ||
| count, ok := et.errors[p] | ||
| if !ok { | ||
| t.Errorf("Expected peer %s to be tracked but it was not", p) | ||
| } | ||
| if count.Load() != 0 { | ||
| t.Errorf("Expected error count for peer %s to be 0 after reset but got %d", p, count.Load()) | ||
| } | ||
| } | ||
|
|
||
| func TestErrorTracker_LogErrors_ThresholdCrossed(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Start tracking the peer | ||
| et.startTracking(p) | ||
|
|
||
| // Log errors until threshold is crossed | ||
| err := et.logErrors(p, 11, 10) | ||
| if err != errThresholdCrossed { | ||
| t.Errorf("Expected errorThresholdCrossed when logging errors above threshold but got %v", err) | ||
| } | ||
|
|
||
| // Check if the error count reflects the logged errors | ||
| count, ok := et.errors[p] | ||
| if !ok { | ||
| t.Errorf("Expected peer %s to be tracked but it was not", p) | ||
| } | ||
| if count.Load() != 11 { | ||
| t.Errorf("Expected error count for peer %s to be 10 after logging errors above threshold but got %d", p, count.Load()) | ||
| } | ||
| } | ||
|
|
||
| func TestErrorTracker_LogErrors_UntrackedPeer(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Log errors for an untracked peer | ||
| err := et.logErrors(p, 5, 10) | ||
| if err != nil { | ||
| t.Errorf("Expected no error when logging errors for an untracked peer but got %v", err) | ||
| } | ||
| } | ||
|
|
||
| // Write a test that tests concurrent access to the methods | ||
| func TestErrorTracker_ConcurrentAccess(t *testing.T) { | ||
| et := newErrorTracker(&Network{}) | ||
| p := peer.ID("testpeer") | ||
|
|
||
| // Start tracking the peer | ||
| et.startTracking(p) | ||
|
|
||
| var wg sync.WaitGroup | ||
| numRoutines := 10 | ||
| threshold := uint64(100) | ||
|
|
||
| for i := 0; i < numRoutines; i++ { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| for j := 0; j < int(threshold)/numRoutines; j++ { | ||
| et.logErrors(p, 1, threshold) | ||
| } | ||
| }() | ||
| } | ||
|
|
||
| wg.Wait() | ||
|
|
||
| // Check if the error count is correct | ||
| count, ok := et.errors[p] | ||
| if !ok { | ||
| t.Errorf("Expected peer %s to be tracked but it was not", p) | ||
| } | ||
| expectedCount := threshold | ||
| actualCount := count.Load() | ||
| if actualCount != expectedCount { | ||
| t.Errorf("Expected error count for peer %s to be %d after concurrent logging but got %d", p, expectedCount, actualCount) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.