-
Notifications
You must be signed in to change notification settings - Fork 132
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
feat(iroh): Gossip client #2258
base: main
Are you sure you want to change the base?
Conversation
I think it is safe to just ignore any messages that are not for us. See b88398e Obviously you can mess with things by subscribing to a namespace that is currently being synced, but doing so accidentally is highly unlikely. And you can also mess with other stuff by using the low level blobs api, so 🤷 . |
It seems that due to the fact that the rpc protocol is public, we can not ever do an additional rpc request without breaking compat.
|
we need to mark the rpc enums as non exhaustive, that should solve this |
longterm I wish the rpc types where all private anyway |
@Frando can we change this? seems unfortunate to subscribe to everything by default |
I think we can, yes. What is our primitive of choice these days to merge a changing list of streams? with futures and thus its merge combinators phased out in iroh - maybe https://docs.rs/futures-concurrency/latest/futures_concurrency/stream/trait.Merge.html ? |
I did not yet give the new gossip dispatcher module a real review, but my first thought is: What about we move the |
#2265 changes the sync engine usage of gossip to not use subscribe_all (based on this branch). |
@Frando so the basic idea of the gossip dispatcher is just to have a thing that wraps the gossip and does some management so that it is less fragile to use. It does the join/unjoin etc. for you and deals with laggy subscribers. If you agree with / can live with the public interface (which is just the subscribe function), we could move something with that signature into iroh-gossip as a higher level interface and then I would use it here, and you could update docs to use it (or not...). The interface is basically this: pub fn subscribe(
&self,
msg: GossipSubscribeRequest,
updates: UpdateStream,
) -> impl Stream<Item = RpcResult<GossipSubscribeResponse>> { where UpdateStream is just a stream of either broadcast or broadcast_neighbours: /// Send a gossip message
#[derive(Serialize, Deserialize, Debug)]
pub enum GossipSubscribeUpdate {
/// Broadcast a message to all nodes in the swarm
Broadcast(Bytes),
/// Broadcast a message to all direct neighbors
BroadcastNeighbors(Bytes),
} There is a hardcoded constant somewhere about how much you are allowed to lag before you are thrown out. We could make that configurable as well. |
I think the API is fine. And still very much in favor of having only one and not 2 high level dispatch interfaces to gossip. |
Ok, I will clean up the dispatcher a bit and make sure it is independent of the rpc protocol, in preparation for moving it into iroh-gossip. |
Moved the dispatcher to iroh-gossip and moved the types into the dispatcher file. I reexported some of the dispatcher types instead of making newtypes for them in the rpc protocol. Not 100% sure about this, but making so many single element newtypes is a drag... One question is if the dispatcher should present a simplified interface for creation. Currently it takes a Gossip. But I don't really see how this can be made really nice. Take an endpoint and an options object? |
# Conflicts: # Cargo.lock # iroh-docs/src/engine/gossip.rs # iroh-gossip/Cargo.toml # iroh/src/client.rs # iroh/src/node.rs # iroh/src/node/builder.rs # iroh/src/rpc_protocol.rs
This is missing adapting the docs stuff to the new gossip dispatcher. Other than that I think it is good to go. |
iroh-gossip/src/dispatcher.rs
Outdated
/// | ||
/// Basically just flattens the two stages of joining into one. | ||
async fn join(gossip: Gossip, topic: TopicId, bootstrap: Vec<NodeId>) -> anyhow::Result<()> { | ||
tracing::error!("Joining gossip topic {:?}", topic); |
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.
remove / downgrade to trace
(plus a few more in here)
/// This should not fail unless the gossip instance is faulty. | ||
async fn dispatch_loop(self) -> anyhow::Result<()> { | ||
use futures_lite::stream::StreamExt; | ||
let stream = self.gossip.clone().subscribe_all(); |
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.
So subscribe_all
sends a message into net.rs
, which has an actor loop task of itself, and in there then subscribes to a broadcast channel on which all events are emitted. If we say that the dispatcher is the main interface we use to access gossip, we could remove all the channeling from net.rs
alltogether and just expose a single, non-broadcast stream, which is then processed here directly. This is what I meant originally by "moving the dispatcher into gossip": Have only one place where multiple channels for subscriptions are supported.
However I'm also fine with merging this as-is and then potentially revisit in followups, as this has no consequence on the client api for iroh-gossip. We'd have to check in with current users maybe, especially deltachat, which API they prefer.
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, I did not look into the details of gossip that much so far. Just treated it as a black box. I guess once the dispatcher is moved to gossip we can optimize things. The important thing for me (and the reason for this PR in the first place) is to have a more high level way to use gossip.
iroh-cli/src/commands/gossip.rs
Outdated
verbose, | ||
} => { | ||
let bootstrap = bootstrap.into_iter().collect(); | ||
let topic = blake3::hash(topic.as_ref()).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.
We should document this somewhere (CLI help maybe) that the passed-in topic will be blake3 hashed.
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. Maybe we should just allow people to specify the exact gossip topic instead of hiding it. I was just too lazy to add a new command line arg.
Change the RPC to contain an actual topic-id, and have alternative CLI options topic: String (gets hashed on the client side) and --topic_raw: TopicId ?
iroh-gossip/src/dispatcher.rs
Outdated
/// Set of bootstrap nodes we are using. | ||
bootstrap: BTreeSet<NodeId>, | ||
/// The task that is driving the join future. | ||
#[allow(dead_code)] |
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 allow(dead_code)
?
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's an AbortingJoinHandle, so it only exists for Drop. But somehow rust does not consider that during dead code analysis. Any way to make it aware?
@@ -184,9 +184,6 @@ mod tests { | |||
// for testing a mapping is simply an ip, port pair | |||
type M = (Ipv4Addr, NonZeroU16); | |||
|
|||
const TEST_PORT: NonZeroU16 = // SAFETY: it's clearly non zero |
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.
unrelated change?
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.
Somehow I had dead code warnings without it 🤷
where | ||
C: ServiceConnection<RpcService>, | ||
{ | ||
/// Subscribe to a gossip topic. |
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 should add some more docs on the return value (sink and stream).
Also at least a smoke test for the gossip client API would be good so that we don't break things. |
…and use ref-cast to eliminate them entirely (#2350) ## Description With 4 different clients, the current approach might be OK. But we are going to have more. E.g. gossip, see #2258 . And in any case it feels weird to store the same thing multiple times. So this replaces public fields in the iroh client with accessors and use ref-cast to eliminate them entirely. There is now only one rpc field, and you can switch from that to the different subsystem client views without runtime overhead, not even an arc clone. ## Breaking Changes Everything that uses the iroh client will have to switch from field accesses to accessor fns. ## Notes & open questions <!-- Any notes, remarks or open questions you have to make about the PR. --> ## Change checklist - [ ] Self-review. - [ ] Documentation updates if relevant. - [ ] Tests if relevant. - [ ] All breaking changes documented.
Description
This makes gossip available in iroh. Using iroh-gossip directly, while not horrible, is a bit verbose.
Breaking Changes
Not sure. It exports a few more things, and adds things. But in theory it should not modify existing things.
Notes & open questions
It seems that the sync engine thinks that it "owns" gossip, so it uses subscribe_all and then will deserialize everything, even messages that are not for it. How can we avoid that? Using an entirely different ALPN for normal gossip?
I think that relying on guaranteed delivery for gossip is not a good idea in any case. so maybe just sending a Lagged but then continuing is best? Forcing the client to resubscribe could be a bit annoying.
Change checklist