-
Notifications
You must be signed in to change notification settings - Fork 11
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
Zombienet Improve DevX with new methods #261
Conversation
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 sorry but I fail to see how this is any better than the previous ideas. It seems like a shortcut, not an improvement. Furthermore, it seems to me that you're still misusing/misunderstanding the typestate pattern.
It's pretty clear to me that you see the value in restricting the APIs the user has access and when, but the way you're going about it shows you're missing a bigger and much more import part of the puzzle.
The whole point of typestates is to restrict the possible state and transition space to one where you can't go into invalid/inconsistent states. However, you're doing precisely that by allowing a transition to WithRelaychain
where the relay chain has an empty name, effectively made worse by the fact that you're effectively lying to the user by saying you were able to transition to WithRelaychain
when that is not true!!
I get that you don't want to abandon the typestate pattern and taking it out is too much work now (even though I believe it would be beneficial for both the library authors and the users), but this is not a solution to the DX issue. It does not solve the fact that you're lying about the true current state which then leads to providing defaults that you can't trust.
In #251 I provided suggestions (and backing to them) that would mitigate this issues, it seems to me that they were not taken seriously, which frustrates me as a user of the library and as just someone that spent time trying to provide constructive feedback. If this iterating process is not meant to include/consider outsider (users) feedback, then either move it to a private channel or just note it as such.
@jmg-duarte all your comments have been packed with information and I appreciate them greatly! I read all of them really carefully. However I assume your contributions here are 100% voluntary just as mines. Nobody here is obliged to obey or working under anyone. The condescending tone in your comment is uncalled for and does not resonate with the open-source culture. I'm spending time and effort on this voluntarily because I simply want to improve the ecosystem. Having said that, I really appreciate the content and information you provide, so let me address your concerns one by one:
You say it like this is something I'm introducing with my code, but the very same issues also exist for the current code: https://github.com/paritytech/zombienet-sdk/blob/main/crates/configuration/src/network.rs#L328 So, yes, at first, I thought I also would prefer to not transition into that state and return a Result instead for example (or any other approach that will prevent transitioning into the next state (although is it propagating the error). So if @pepoviola also agrees, I can gladly introduce this breaking change and make both my method and the current method in the library safer in this regard.
If you mean the comments:
I definitely did take them seriously. That's the reason I didn't submit this PR just out of nowhere but provided the whole context of what I did so far. I honestly think this approach will be better than creating an additional wrapper which might confuse the current users of the library and the newcomers. More context on your comments:
It saddened me that you felt that I'm not taking your comments seriously. Coming back to the main topic:
The current way: let mut network = NetworkConfigBuilder::new()
.with_relaychain(|r| {
r.with_chain("rococo-local")
.with_default_command("polkadot")
.with_node(|node| node.with_name("alice"))
.with_node(|node| node.with_name("bob"))
})
.with_parachain(|p| {
p.with_id(100)
.cumulus_based(true)
//.with_registration_strategy(RegistrationStrategy::UsingExtrinsic)
.with_collator(|n| n.with_name("collator").with_command("polkadot-parachain"))
})
.build()
.unwrap()
.spawn_native()
.await?; The way I'm proposing now: let network_config = NetworkConfigBuilder::with_nodes_and_chain(
vec!["alice".to_string(), "bob".to_string()],
"rococo-local",
)
.with_collators_and_parachain_id(vec!["collator1".to_string(), "collator2".to_string()], 100)
.build()
.unwrap(); Let's say that this solution obeys the safety of type-state-pattern (we can easily make it so), then I think what I'm proposing is a much better way than other solutions. The reasons are:
The main goal for this initiative was to make Our first suggestion was to create another library that wraps My aim was to shorten the current code I gave above, and I think this solution achieves it. If I somehow misinterpreted any of your comments or intentions, feel free to correct me. But I assure you I'm delighted by your exquisite comments! |
Morning @ozgunozerk! Let me start by apologizing for my tone, it was not my intention to come out as condescending. My contributions are indeed voluntary and I am very aware that no one is obliged to pick them up, however, I was frustrated as a consequence of not feeling that my suggestions were taken. Having read your comment I see where the mismatch happened. Let me try to explain myself — I'm not interested in beef, I want to collaborate and to make this library better since if it's an headache, it affects my work too.
I see now that I didn't understand your original goal when you spoke about improving DX, we ended up talking a bit past each other, I also misjudged your role in the project, my bad! My main issue with the current API happens at the core level, which ends up undermining your current efforts. As the API currently works, I think your suggestions will help people getting up and running faster, however, once they need to change something they'll still run into issues due to the current API limitations. I don't think our suggestions are incompatible, we're just aiming for different levels! IMO there is core work that needs to be done to improve the whole API, a breaking change will be in order to achieve it — normally I wouldn't suggest it so lightly, however, the crate explicitly states it is a WIP and |
Morning @jmg-duarte,
I see, and it makes perfect sense to me. As you said, I guess we are aiming for different levels at this point. At OpenZeppelin, I volunteered to simplify this API and achieve this in the fastest and most efficient way possible since I'm also busy with other stuff as well. What I'm proposing here is solving our problem, and I believe it will be helpful for the other projects and hopefully for the newcomers as well by making the whole code less intimidating. Maybe some time in the future, if I have more free time, I'd gladly take a whack on revizing the core API, and of course keeping your suggestions in mind. Already learned a new neat trick from you (simplifying the error handling via traits) 💯 Cheers! |
Hi @jmg-duarte / @ozgunozerk, thanks both for your feedback and contributions 🙌 . I think we have two different scopes of improvements to tack. One is simplify the api for newcomers in order to make more simple and less confusing how to just spawn a simple network as exploration. In this direction, I think adding some helpers methods to shortcut the builder could help but we need to ensure not allow to construct invalid configurations. Again, thanks both for your feedback and contributions! |
Yes, one of the pain points for me when writing the wrapper SDK (which I abandoned) was the closures. I think it makes the code unnecessarily complex, hard to work with, and also not an eye-candy to look at it. I'd prefer having objects as parameters. For example, This would also help to ensure type safety across states as @jmg-duarte mentioned. For example, if we ensure the The same approach can be followed for nearly everything across this crate. I can tackle this change in another PR if you guys like the idea. Won't be a huge task and won't consume too much of my time, so I'm definitely willing to do this. |
Aside from closures, I think @jmg-duarte mentioned some really nice ideas on how to improve the core library, I agree with most if not all on what he suggested. I can't foresee how much time that it would take to implement those. So with basically 3 things, I think this library would be much better:
|
crates/configuration/src/network.rs
Outdated
} | ||
|
||
if relay_name.is_empty() { | ||
errors.push(ConfigError::Relaychain(anyhow!("relaychain name can't be empty")).into()); |
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.
errors.push(ConfigError::Relaychain(anyhow!("relaychain name can't be empty")).into()); | |
errors.push(ConfigError::Relaychain("relaychain name can't be empty").into()); |
why wrapped in anyhow
if we already have an error variant.
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 ConfigError::Relaychain
variant expects an anyhow::Error
, but the suggestion passes a &str
. Casting into()
was unfortunately not sufficient to make the compiler happy.
anyhow!("relaychain name can't be empty")
wraps the &str
into an Error
as required by the enum.
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.
If you want, I can replace anyhow::Error
with String
in the Error
enum in my next PR that will address closures and objects. Makes sense to merge both of the refactorings into a single PR imo.
f8b4563
to
c7ca8ca
Compare
Thanks for the suggestions! Accepted all of them. I guess everything has resolved aside from the error handling @pepoviola. We can proceed with wrapping them into If there is a better way and if I'm missing something, please let me know! |
Hi @jmg-duarte, I will add a Thanks for your feedback! |
crates/configuration/src/network.rs
Outdated
let network_config = NetworkConfigBuilder::new().with_relaychain(|relaychain| { | ||
let mut relaychain_with_node = relaychain.with_chain(relay_name).with_node(|node| { | ||
node.with_name(node_names.first().unwrap_or(&"node1".to_string())) | ||
}); // TODO: in order to not panic this, we provide a dummy value. However, returning a Result may be a better choice for this constructor |
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.
If first
fails means that we already have errors, so will be fail the whole config.
Hi @ozgunozerk, sorry about the delay. I checked the current validation and we are ensuring that the |
@pepoviola I also removed the checks for empty lists (node and collator name lists). There were no helper method for them, but then I realized we don't need these helper methods for lists. If the list is empty, the code provides an empty string as the default option (to evade returning Hence, adding new code is not necessary, all tests are still passing. Should be good to go! |
Fix: - Grandpa keys injected. #267 Added: - Name validation (for nodes) #266 - Improve dx (@ozgunozerk ) #261
Revamped version of #252, didn't overwrite that PR in case you guys want to compare both solutions.
Fixes #251
TL;DR -> here is the suggestion:
I found an even better way than creating a wrapper. Here is the summary:
configuration
crate offers. Even though the aim is to grant better DevX to the community, it should still preserve the configuration options.configuration
crate if they wanted to” is not a good argument imo. Here is the reason:configuration
crate.configuration
crate’s type state pattern, I had to export nearly all the states fromconfiguration
crate for the builder types in thelib.rs
. The whole thing was quickly becoming ugly.configuration
crate was, basically the extra methods that granted better DevX (for initializing the structs with default settings).configuration
crate itself!Notes:
with_nodes_and_chain
with_collators_and_parachain_id
Hope it makes sense!