-
Notifications
You must be signed in to change notification settings - Fork 250
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
Libp2p stream limits #1017
Merged
Merged
Libp2p stream limits #1017
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
6b3b68b
Remove setting yamux window update mode, the same value is already th…
nazar-pc 7d1247f
Restore tracking of number of connected peers
nazar-pc bfac7a9
Implement libp2p stream limits with wrappers
nazar-pc d50c0a9
Remove now unnecessary batching restrictions
nazar-pc acebfaa
Remove unnecessary boxing and pinning in various places
nazar-pc 5029bf8
Subtract Kademlia tasks concurrency from the rest, so that only one S…
nazar-pc b43bd6c
Write basic tests for `maintain_semaphore_permits_capacity`
nazar-pc 6060dd6
Drop permit when query is finished and not earlier
nazar-pc fc80120
Support peers boosting threshold customization, set it to 5 for more …
nazar-pc 6e989d3
Resizable semaphore (#1019)
rahulksnv ce20efc
Merge remote-tracking branch 'origin/main' into libp2p-stream-limits
nazar-pc 47fb5db
Update to newer libp2p revision
nazar-pc e98c358
Update libp2p to fork with kademlia handler buffering
nazar-pc 21e719c
Increase timeout and max interval for piece publishing to reduce prob…
nazar-pc 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 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 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,119 @@ | ||
use futures::future::{select, Either}; | ||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
use std::sync::Arc; | ||
use std::time::Duration; | ||
use tokio::sync::Semaphore; | ||
use tokio::time::sleep; | ||
|
||
#[tokio::test] | ||
async fn maintain_semaphore_permits_capacity() { | ||
let base_tasks = 2; | ||
let boost_per_peer = 1; | ||
let interval = Duration::from_micros(1); | ||
let connected_peers_count = Arc::new(AtomicUsize::new(0)); | ||
let tasks_semaphore = Arc::new(Semaphore::new(base_tasks)); | ||
|
||
tokio::spawn({ | ||
let tasks_semaphore = Arc::clone(&tasks_semaphore); | ||
let connected_peers_count_weak = Arc::downgrade(&connected_peers_count); | ||
|
||
async move { | ||
super::maintain_semaphore_permits_capacity( | ||
&tasks_semaphore, | ||
interval, | ||
connected_peers_count_weak, | ||
boost_per_peer, | ||
) | ||
.await; | ||
} | ||
}); | ||
|
||
let timeout = Duration::from_millis(100); | ||
|
||
// Let above function time to run at least one loop | ||
sleep(timeout).await; | ||
|
||
let permit_1_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_1_result, Either::Left(_)) { | ||
panic!("Must be able to acquire the permit"); | ||
} | ||
|
||
let permit_2_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_2_result, Either::Left(_)) { | ||
panic!("Must be able to acquire the second permit"); | ||
} | ||
|
||
{ | ||
let permit_3_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_3_result, Either::Right(_)) { | ||
panic!("Must not be able to acquire the third permit due to capacity"); | ||
} | ||
} | ||
|
||
// Increase capacity | ||
connected_peers_count.fetch_add(1, Ordering::SeqCst); | ||
|
||
{ | ||
let permit_3_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_3_result, Either::Right(_)) { | ||
panic!("Must not be able to acquire the third permit due to capacity"); | ||
} | ||
} | ||
|
||
// Increase capacity more | ||
connected_peers_count.fetch_add(1, Ordering::SeqCst); | ||
|
||
let permit_3_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_3_result, Either::Left(_)) { | ||
panic!("Must be able to acquire the third permit due to increased capacity"); | ||
} | ||
|
||
{ | ||
let permit_4_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_4_result, Either::Right(_)) { | ||
panic!("Must not be able to acquire the fourth permit due to capacity"); | ||
} | ||
} | ||
|
||
// Decrease capacity capacity | ||
connected_peers_count.fetch_sub(1, Ordering::SeqCst); | ||
|
||
drop(permit_3_result); | ||
|
||
sleep(timeout).await; | ||
|
||
{ | ||
let permit_3_result = select( | ||
Box::pin(tasks_semaphore.acquire()), | ||
Box::pin(sleep(timeout)), | ||
) | ||
.await; | ||
if !matches!(permit_3_result, Either::Right(_)) { | ||
panic!("Must not be able to acquire the third permit again due to capacity anymore"); | ||
} | ||
i1i1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guess semaphores are not best fit for this scenario, where the max limit can grow/shrink as peers come and go. Hence we need this complexity to hold excess permits, etc
How do you feel about this:
current_max_limit, current_usage
. These two are protected by async Mutex/CV.current_max_limit
can be updated with the boost_per_peer fromhandle_swarm_event()
during connection open/close.current_usage
drop()
of the permit: bump upcurrent_usage
, signal any waiters. But, any excess permits if the max limit shrinked are not returned to the free pool. This would need spawning async task from the dtor unfortunately, as rust doesn't support await from drop yet.Happy to make a branch off your PR and make a draft, if that helps (also to get my feet weet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit hacky, I agree, but not too much (for my taste).
I found CVs counter-intuitive to read in most cases and I feel like we'll implement essentially the same mechanism (practically speaking) in the end. It might be an interesting exercise, but I'm not sure it'll actually be a lot of value in that.
Supporting what we need here (resizing, dropping of unfinished acquisition) is probably quire a bit of effort.
Feel free to try unless you have anything more valuable to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, looks like tokio doesn't have CondVar, so that won't work :-( But found sem has a
forget()
API: https://docs.rs/tokio/1.23.0/tokio/sync/struct.SemaphorePermit.html#method.forgetThis can help avoid the reserved_permits stash, which would simplify this quite a bit:
Not a big deal: it may take a while for the changes to take effect (in the shrink case), as the sem acquisition in this path doesn't get higher priority (unless done from the dtor as in the other approach)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use
forget
, but that'll mean we'll permanently increase internal capacity of the semaphore every time we callforget
. And while we'll probably never reach https://docs.rs/tokio/1.23.0/tokio/sync/struct.Semaphore.html#associatedconstant.MAX_PERMITS on 64-bit platform, we can on 32-bit platform. It'll take a while, but when you hit it, it'll be hard to debug.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah you are right.
I still feel this path can be simplified (took a bit to figure out this shrinking business), let me try out the idea I mentioned above off your branch.