ssbc / ssb-conn

SSB plugin for establishing and managing peer connections
MIT License
16 stars 5 forks source link

tracking who I'm connected to #23

Closed mixmix closed 3 years ago

mixmix commented 3 years ago

The problem I'm solving

In Ahau we want to track which peers we currently have a connection with. I turned to the ssb-conn api to answer that questions and the main method which seemed to be relevant was ssb-conn.peers

In our context in Ahau:

Here's the code I wrote :

var connected = new Set([])

pull(
  sbot.conn.peers(),
  flatMap(connections => connections.map(c => c[1])),
  pull.filter(peer => {
    if (peer.state === 'connected') return true

    delete connected.delete(peer.key)
    return false
  }),
  pull.drain(profile => {
    if (!connected.has(profile.key)) {
      getProfile(profile.key, () => {})
      // pre-load it!
    }
    connected.add(profile.key)
  })
)

// our async graphql then uses Array.from(connected))

The unexpected behaviour

What we see is that connected works for new peers connecting, but it's never updated to reflect that some peer is no longer connected.

Looking closer we tested this manually by setting two peers up, watching them connect, then turning one off. We saw (I think) several unexpected things:

mixmix commented 3 years ago

oh that getProfile thing isn't relevant here ... actually we're not returning raw feedIds (keys) in our graphql api, we're returning profiles .. but that's not relevant to this problem

staltz commented 3 years ago

Thanks @mixmix. What's that flatMap?

staltz commented 3 years ago

So my gut instinct (without running code, and doing some guessing what the rest of your code looks like, (by the way, feel free to point to Ahau code lines since its open source)) is that you're using sbot.conn.peers() as if it were a stream of events, but the right way of using it is a stream of arrays (like the Set you're manually constructing). Thus the usage should be much simpler:

pull(
  sbot.conn.peers(),
  flatMap(entries => 
    entries
      .map(entry => entry[1])
      .filter(peer => peer.state === 'connected')
  ),
  pull.drain(profile => {
    getProfile(profile.key, () => {})
  })
)

If you wanted to listen to the stream of events, that would be:

pull(
  sbot.conn.hub().listen(),
  pull.drain(ev => {
    console.log(ev.address) // multiserver address
    console.log(ev.type) // 'connecting' or 'connecting-failed' or 'connected' or 'disconnecting' or 'disconnecting-failed' or 'disconnected'
  })
)

Notice that this API was available under conn.hub(). Read more at ssb-conn-hub.

I can update the docs to reflect how to use these APIs.


PS

delete connected.delete(peer.key) raised a WTF in my eyes. You don't need that first delete, which is meant for removing properties from an object. It should be connected.delete(peer.key). I don't even know what delete connected.delete(peer.key) would do, probably something mysterious. Maybe that explains some of mystery you experienced.

mixmix commented 3 years ago

pull-flatmap

mixmix commented 3 years ago

Thanks. I tried reading the source but noticed you were exporting a array of functions, which is not something I've ever seen secret stack consume.

I had started looking into the gossip file because that had mentions of disconnect but couldn't see how that was working easily.

I'm just telling you this so you know what first impression of another coder was.

Look forward to checking conn.hub stuff out. I think my assumption was that ssb-conn would be the top level api and hub was some internal thing which was advanced and had apis but they were probably more private /internal. I think notes in ssb-conn pointing to what's friendly and public that you should know about would be really helpful.

staltz commented 3 years ago

Thanks. I tried reading the source but noticed you were exporting a array of functions, which is not something I've ever seen secret stack consume.

Which file you're referring to?

I think my assumption was that ssb-conn would be the top level api and hub was some internal thing which was advanced and had apis but they were probably more private /internal.

That's correct. Hub APIs aren't meant for using (simply because they might change more often than the top-level API), unless you really have to. I designed the top-level API so that it covers the vast majority of use cases.

I think notes in ssb-conn pointing to what's friendly and public that you should know about would be really helpful.

I'm happy to make those notes in the docs, actually I just now updated the docs with some. I guess what I need first are questions, because then I can answer those questions. I was assuming that the docs were clear, but it's only when people raise questions when I get feedback on what exactly is not clear.

staltz commented 3 years ago

I may have found an even easier way!

ssb.conn.query().peersConnected() // returns an array, synchronously
mixmix commented 3 years ago

I ended up going with the streaming solution because it let's me preload profiles, such offers a little performance boost for the api I've written.