yjs / y-webrtc

WebRTC Connector for Yjs
MIT License
448 stars 109 forks source link

Implement a peer to peer mesh topology/routing #22

Closed disarticulate closed 3 years ago

disarticulate commented 3 years ago

Is your feature request related to a problem? Please describe. Multiple webrtc connections create unnecessary load with direct connect to all users. Implement a mesh network topology.

Prior art: https://github.com/lazorfuzz/liowebrtc

Describe the solution you'd like As seen above, past a certain threshold, implement something like a Kademlia hash table for peer-to-peer mesh network.

Useful example/potential dependency: https://github.com/jeanlauliac/kademlia-dht

Describe alternatives you've considered I used liowebrtc initially, but the complicated handshakes and necessary wiring made it burdensome. Also, it's just another dependency that duplicates what this library seems intent on doing: improving peer to peer use of yjs

Additional context It may make sense to do this alonside the potential fixing of https://github.com/yjs/y-webrtc/issues/20 as it would require some heavy lifting and additional lifetime maintenance

dmonad commented 3 years ago

Hi @disarticulate ,

I didn't know about liowebrtc, thanks for sharing.

y-webrtc works similarly to lio, as it creates a partially connected network of peers. So similarly to lio, not every peer needs to be conected to every other peer. This was tested at a conference with ~100 peers, and a maximum connection threshold of ~15-30 peers.

I will have a closer look at lio and see if they had some ideas that I could adapt for y-webrtc. I'm interested how they achieved video-sharing over partially connected networks.

The signaling approach (how to find other peers) is currently centralized via a websocket server. I wrote y-webrtc to support alternative signaling approaches (that would need to be implemented as a separate SignalingConn class).

If you want, I could help you to get started with writing an alternative signaling approach for y-webrtc. My projects are practice oriented and the last time I checked DHT approaches are too slow in browsers. The short-lived nature of web-sessions also makes it a bad target for finding peers using DHTs. This is why bittorrent networks favor peers that stay connected for a long time.

I would rather like to implement DHTs in the signaling server. This is similarly to how webtorrent works. They have multiple signaling servers that store peers using DHTs. So you still get all the advantages of decentralization (everyone can maintain their own signaling server) and none of the drawbacks (fast peer-lookup and a resilient network).

disarticulate commented 3 years ago

looks like I misread a bit in the docs about the connections. It now sounds like additional connections to indirect peers send updates to direct peers, who then send updates to the client. That's sufficient for what I'm looking at. However, it sounds like a better p2p strategy is still the DHT route. Eventually, I'd like to have anonymous peering which is the benefit of things like torrent. Also, consider that webseeds which webtorrent uses has now been added to libtorrent: https://libtorrent.org/manual-ref.html which means you could potentially build that signalling server implementation using existing torrent software.

To drill down, the liowebrtc included a 'direct messaging' option which allowed for file sending which when trying to 'sync' peers, I don't want to send all files to everyone every time as I've got a whole offline mode setup for large attachments/images. I ran into the same bug we discussed here: https://github.com/yjs/y-webrtc/issues/20

The other plugins I'm using is yjs/y-prosemirror inside https://github.com/ueberdosis/tiptap

What I'm stuck on is closer to https://github.com/yjs/y-webrtc/issues/20 as I have clients which already synced, and only need updates versus other clients that are completely new needing all the files. My ydoc's are already just using hashes to identify larger media and storing those separately, so I've streamlined the doc itself, but then I need to figure out how to move files to specific clients without tons of useless data transfer, since I'm gearing towards mobile data collection platform.

I'll message you what it looks like and a brief description. I appreciate the responsiveness.

dmonad commented 3 years ago

The sync approach in y-webrtc relies on more-than-once delivery. You might be able to explicitly request files from connected peers. You can still inspect the list of connected peers and send direct messages to them.

disarticulate commented 3 years ago

What I ended up implementing was just taking the webrtc peer constructor and created a new simple peer, and connected that using the ydoc as the signaling structure.

I did test trying to send other messages through the y-webrtc peer but ran into this code:

const readMessage = (room, buf, syncedCallback) => {
  const decoder = decoding.createDecoder(buf)
  const encoder = encoding.createEncoder()
  const messageType = decoding.readVarUint(decoder)
  if (room === undefined) {
    return null
  }
  const awareness = room.awareness
  const doc = room.doc
  let sendReply = false
  switch (messageType) {
    ...
    default:
      console.error('Unable to compute message')
      return encoder
  }

You may consider emitting that message instead of running it as an error. Alternatively, you could wire a function into it to handle those messages. Not sure how that would work. When I tried to emit it, I think the return value or something caused everything to break, so, its just an observation.

dmonad commented 3 years ago

It should be rather easy to adapt y-webrtc to allow custom messages. You can simply create another messageType and send JSON structures as custom messages. These custom messages could fire an event handler on the provider.

If you implement an event handler for a custom message type, you need to read the message, emit it, and then return null to indicate that you don't want to respond. The default handler should probably also return null.

disarticulate commented 3 years ago

https://github.com/yjs/y-webrtc/issues/20 reduces the implementable size of the messages one can send. Since I needed to implement a buffer for my large messages, everything had to leave y-webrtc into a custom messaging system, which is why I piggybacked (hacked) the simple peers created. If we had a fix for message size, I could build everything into yjs.

dmonad commented 3 years ago

It will be a while until I get to #20. When the ticket is fixed you should open another ticket to emit the received messages. I think we can close this ticket. Feel free to continue commenting or reopen this issue if here is still something to discuss.