-
Notifications
You must be signed in to change notification settings - Fork 25
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
[Sharding 4] Base testing support for sharding #1369
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1369 +/- ##
==========================================
+ Coverage 93.64% 93.66% +0.01%
==========================================
Files 223 223
Lines 37112 37376 +264
==========================================
+ Hits 34755 35008 +253
- Misses 2357 2368 +11 ☔ View full report in Codecov by Sentry. |
ipa-core/src/net/test.rs
Outdated
|
||
impl TestNetwork<Shard> { | ||
#[must_use] | ||
|
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.
ipa-core/src/net/test.rs
Outdated
/// 3 mpc ports. | ||
ports_by_ring: Vec<Ports<Vec<u16>>>, | ||
/// Describes the number of shards per helper | ||
sharding_factor: usize, |
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.
when I see factor I think about the multipler. shard_count
is easy and straightforward
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.
That was intended because you're getting 3x hosts for the factor.
If you still think shard_count is easier I can do that, but this factor affects all 3 helpers.
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.
then /// Describes the number of shards per helper
confuses me even more
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.
Why? it says per helper aka 3x
#[must_use] | ||
pub fn with_open_ports() -> Self { | ||
pub fn with_http_and_default_test_ports() -> Self { |
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.
I don't see the functionality to request shard ports from the Kernel, is it planned? Static ports are constant source of pain for tests and I don't think we should go with static. It is fine to test configs, but all unit tests should run with random ports assigned at runtime
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.
Requesting ports from the kernel is part of the default method.
In the past we used to have both, with_open_ports and default, which did the same thing. I thought less code is better.
ipa-core/src/net/test.rs
Outdated
optional_ports: Option<Vec<u16>>, | ||
ring_index: usize, | ||
) -> TestServers { | ||
let mut sockets = vec![None, None, None]; |
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.
why not just Vec::with_capacity(3)
?
ipa-core/src/net/test.rs
Outdated
}); | ||
let (scheme, certs) = if self.disable_https { | ||
("http", [None, None, None]) | ||
// Ports will always be defined after this. Socks only if there were no ports set. |
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.
I don't understand this block of code and comment to it. Why we use all ports available to build servers in HTTPS mode and only 3 for HTTP?
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.
Not sure to what part you're referring...
But this function creates 3 servers.
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.
After line 270 all the port situation will be set. Either you gave it as param or we asked the os.
Not sure to what part you're referring when you say "Why we use all ports available to build servers"... But this function creates 3 servers.
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.
The confusion comes from either using optional_ports
or synthesizing ports
inside
optional_ports: Option<Vec<u16>>
which can be arbitrary size according to this function (it does not enforce size to be 3, if it should, then it would be better to take an array). So ports
may have variable size
ipa-core/src/net/test.rs
Outdated
} | ||
|
||
/// Defines a test network with a single MPC ring (3 helpers). | ||
fn build_ring_network( |
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.
After this change TestConfig
looks a lot more complicated that it was before. I think it will benefit from some refactoring to split MPC and shard network initializations
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, Test config holds now the entire sharded network. But on the other hand, having everything described on this single object makes it easy to operate on the network as a whole which is the use case (check the transport.rs test). Separating this into 2 seems like it will only add complexity on the caller side.
Can you be more explicit on how you want to split the network init?
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.
you don't have to make callers suffer. Providing a single entry point for them to construct the config is reasonable. Doing everything inside one struct after that is not necessary - power through composition
ipa-core/src/net/test.rs
Outdated
let rings: Vec<TestNetwork<Helper>> = (0..self.sharding_factor) | ||
.map(|s| { | ||
let ring_ports = self.ports_by_ring.get(s).map(|p| p.ring.clone()); | ||
let shards_ports = self.ports_by_ring.get(s).map(|p| p.shards.clone()); |
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.
let rings: Vec<TestNetwork<Helper>> = (0..self.sharding_factor) | |
.map(|s| { | |
let ring_ports = self.ports_by_ring.get(s).map(|p| p.ring.clone()); | |
let shards_ports = self.ports_by_ring.get(s).map(|p| p.shards.clone()); | |
let r = zip(ring_ports, shard_ports).map(|(r_port, s_port)| { ... }) |
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.
there are cases where ports_by_ring won't be set though.
ipa-core/src/net/test.rs
Outdated
} | ||
|
||
#[must_use] | ||
pub fn create_ids(disable_https: bool, id: HelperIdentity, ix: ShardIndex) -> ClientIdentities { |
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.
should it be a constructor on ClientIdentities
?
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.
Sure. The conditional on disable_https had me on the fence
ipa-core/src/net/test.rs
Outdated
let (mut certificate, mut private_key) = get_test_certificate_and_key(id); | ||
ClientIdentity::from_pkcs8(&mut certificate, &mut private_key).unwrap() | ||
pub fn get_client_test_identity(id: HelperIdentity, ix: ShardIndex) -> ClientIdentities { | ||
let pos = ix.as_index() * 3 + id.as_index(); |
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.
that deserves a comment and a common function to compute the absolute offset as I believe it is used in more than one place
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
ipa-core/src/net/test.rs
Outdated
/// The network itself won't be excersized as that's tested elsewhere. | ||
#[test] | ||
fn create_4_shard_http_network() { | ||
let ports: Vec<Ports<Vec<u16>>> = vec![ |
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.
do you want to test that overlapping ports are rejected?
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.
Sure, I can add that
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.
I think we shouldn't store a TcpListener
inside a configuration struct, so I'd like this to be fixed before pushing it, but other than that lgtm
/// Contains the ports | ||
pub config: ServerConfig, | ||
/// Sockets are created if no port was specified. | ||
pub socket: Option<TcpListener>, |
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.
as much as I like the idea of holding onto TcpListener until the moment when server is spawned - minimizes the chances of Kernel re-using that port in between - I don't think the approach of storing TcpListener
in the configuration struct is appropriate here. TcpListener
does not describe the configuration, it actively uses a system resource (socket)
Maybe we can get away with using file descriptors, but honestly I wouldn't bother as I've never seen an issue with bind/release.
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.
There's a chain of dependencies; PeerConfig requires a port, etc.
Changing this won't be simple. I can add a todo.
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.
just to be clear - I am not suggesting to get rid of the port here. If port is not provided by the caller, you can do the previous workaround (ask kernel for a port and drop the listener after that)
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.
Ahh, I think that might be problematic with many tests running at the same time.
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.
Kernel is good not releasing the port right away
ipa-core/src/net/test.rs
Outdated
.map(|r| r.shards[id.as_index()]) | ||
.map(Some) |
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.
.map(|r| r.shards[id.as_index()]) | |
.map(Some) | |
.map(|r| Some(r.shards[id.as_index()])) |
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.
In general, do you prefer larger blocks of code inside a map function instead of more map calls?
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 always a balance, cluttered map calls are bad for readability, more map calls result in more complex types used and more work for compiler to unwind them
} | ||
|
||
/// Get all the MPC ports in a ring specified by the shard index. | ||
fn get_ports_for_shard_index(&self, ix: ShardIndex) -> Vec<Option<u16>> { |
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.
from the ergonomic perspective, a better type to return would be Option<Vec<u16>>
here
@@ -9,46 +9,171 @@ | |||
|
|||
#![allow(clippy::missing_panics_doc)] | |||
use std::{ | |||
array, | |||
collections::HashSet, |
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.
Consider splitting this file into multiple - it grew too big and Github now hides all the changes for it by default
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.
I'm thinking on separating the TestServer stuff
This is the fourth of a series of pull requests (PR) to enable sharding on IPA. See #1364
This change doesn't meaningfully change any tests or the operation of IPA. This is just adding abstractions that are going to be useful later.
Biggest change here is in
net/test.rs
module, which was enhanced to create Sharded infrastructure. This will become more useful for the sharded shuffle test, but releasing early allows reviewers to check for the intended architecture and its usage.