[ipfs/go-bitswap] Bitswap architecture thoughts
- Dominant language
- Go
- Stars
- 316
- Forks
- 163
- Avg merge
- 4d 12h
- Merged PRs (30d)
- 8
Description
# Bitswap Architecture
This is a loose sketch of a potential bitswap architecture. It's mostly based on the current architecture (plus https://github.com/ipfs/go-bitswap/pull/398), but I've tried to simplify some things.
Really, this is mostly to help me understand how everything is (and could be) structured. Unfortunately, reality is complected so I'm missing a lot of edge cases including:
* Handling timeouts.
* Handling unresponsive peers.
* Refcounting connections.
* Freeing resources when peers disconnect.
## GetBlocks
A single "request" from the user, attached to a session.
- Channels
- inBlockCh (blocks from session)
- outBlockCh (blocks to user)
- Sends
- to Session via requestCh
- `want(cids, inBlockCh)`
- `cancel(inBlockCh)`
- to the user via outBlockCh
- blocks
- Receives
- from Session via inBlockCh
- `blocks(blocks)`
- Blocks received on this channel should be buffered internally to avoid blocking the session (unless the channel buffers are sufficient).
- context (user)
- context (session)
## Session
The session managers related wants/peers and makes all decisions concerning where/when to send wants.
- Fields
- id `uint64` (session id)
- wants `map[cid]map[GetBlocks.inBlockCh]struct{}` (used when routing incoming blocks to GetBlocks calls)
- cancelMap `map[GetBlocks.inBlockCh]map[cid]struct{}` (used when canceling GetBlocks, not strictly necessary)
- Channels
- requestCh (want/cancel)
- blockInfoCh (blocks, haves, don't haves, etc.)
- peerInfoCh (peer availability changes)
- Receives
- from GetBlocks via requestCh
- wants
- cancels
- from BlockRouter via blockInfoCh
- `receive(block, have, havenot)`
- from PeerRouter via peerInfoCh
- `available(peer)`
- `gone(peer)`
- Sends
- to GetBlocks via GetBlocks.inBlockCh
- `blocks(blocks)`
- to BlockRouter via BlockRouter.requestCh
- `want(cids, Session.blockInfoCh)`
- `cancel(cids, Session.blockInfoCh)`
- `cancelAll(session.blockInfoCh)` (cancels all requests by this session)
- After sending this, the service must drain the blockInfoCh until it's closed.
- **note:** the session must also receive on blockInfoCh while sending on this channel to prevent a deadlock.
- to PeerRouter via PeerRouter.requestCh
- `interest(peers, Session.peerInfoCh)`
- `cancel(peers, Session.peerInfoCh)`
- `cancelAll(Session.peerInfoCh)`
- After sending this, the service must drain the peerInfoCh until it's closed.
- **note:** the session must also receive on `peerInfoCh` when sending these requests.
- to WantManager via WantManager.requestCh
- `send(Session.id, peers, wants, haves, peers)`
- `broadcast(cids, peers, Session.id)`
- `cancel(cids, Session.id)`
- `cancelAll(Session.id)`
- The service can walk away from the WantManager once it has sent a
cancel as the WantManager never sends anything back.
## BlockRouter
Routes blocks (and information about blocks) to the appropriate services. All
incoming blocks pass through this service.
**change:** This was the WantRequestManager (mostly).
- Fields
- wants `map[cid]map[Session.blockInfoCh]struct{}` (used to route incoming information)
- interest `map[cid]map[Session.blockInfoCh]struct{}` (used to route incoming information)
- cancelMap `map[Session.blockInfoCh]map[cid]struct{}` (used for canceling, probably not necessary)
- Channels
- requestCh (wants from sessions)
- Receives
- from Session via requestCh
- `want(cids, Session.blockInfoCh)`
- Checks for the block.
- If we have it, send it back.
- If we don't, register interest in the block.
- `cancel(cids, Session.blockInfoCh)`
- Unregister interest in the block
- `cancelAll(Session.blockInfoCh)` (cancels all requests by this session)
- Unregister interest in all blocks associated with the session.
- Closes `Session.blockInfoCh` when done to signal that the cancel is complete.
- from Network via requestCh
- `message(from, blocks, haves, donthave)`
- from Bitswap via requestCh
- `blockPut(blocks)`
- Sends
- to Session via blockInfoCh
- `message(from, blocks, haves, donthaves)`
- to Engine via Engine.requestCh
- `blocksAvailable(cids + block sizes)`
- to the blockstore
- Synchronously.
## PeerRouter
Routes incoming peer information to interested sessions.
- Fields
- peerInterest `map[peer]map[Session.peerCh]struct{}` // used to route incoming information
- cancelMap `map[Session.peerCh]map[cid]struct{}` // Used for canceling. Probably not necessary
- Channels
- requestCh (inbound requests from the session and network)
- Receives
- from Session via requestCh
- `interest(peers, Session.peerInfoCh)`
- Registers session interest for that peer.
- Checks to see if the peer is connected, notifying peerInfoCh when available.
- `cancel(peers, Session.peerInfoCh)`
- `cancelAll(Session.peerInfoCh)`
- Unregister interest in all peers associated with the session.
- Closes `Session.peerInfoCh` when done to signal that the cancel is complete.
- from Network via requestCh
- `peerConnected(peer)`
- `peerDisconnected(peer)`
- Sends
- to Session via blockInfoCh
- `receive(block, have, havenot)`
## WantManager
Manages our outbound, per-peer wantlists (refcounting per session).
- Fields
- broadcastWants `cidSessSet`
- peerWants `map[peer]*peerWant`
- wantPeers `map[cid]map[peer]struct{}`
- Channels
- requestCh (inbound requests from sessions)
- Receives
- from Session via requestCh
- `send(Session.id, peers, wants, haves, peers)`
- `broadcast(cids, peers, Session.id)`
- `cancel(cids, Session.id)`
- `cancelAll(Session.id)`
- Cancels all wants related to the session.
- Sends
- to PeerQueues
- Currently, this would just add to the peer queue synchronously, then signal it.
## Engine
Tracks inbound wantlists and sends back blocks.
- Channels
- requestCh (inbound requests from the network)
- Receives
- from Network via requestCh
- `wantlist(from, wantlist, cancels)`
- Synchronously checks to see if we have the requested blocks.
- Enqueues a task if we do.
- Records that the peer wants the blocks.
- from BlockRouter via requestCh
- `blocksAvailable(cids + sizes)`
- Checks to see if any peers want the blocks, enqueuing tasks as necessary.
- Sends
- Writes to peer task queues, signaling workers.
- Writes to network from workers.
## Network
Service to interact with the network. This one is kind of funky as there's no
central event loop.
- Receives (from per-stream worker)
- Messages
- Sends (from event handlers)
- to PeerRouter via via PeerRouter.requestCh
- `peerConnected(peer)`
- `peerDisconnected(peer)`
- TODO: ref counting?
- Sends (from each per-stream worker)
- to Engine via Engine.requestCh
- `wantlist(from, wantlist, cancels)`
- to BlockRouter via BlockRouter.requestCh
- `message(from, blocks, haves, donthave)`
## Ledger
Tracks our debt ratio with our peers. Updated synchronously by both the Engine (when sending blocks) and the BlockRouter (when receiving blocks).
**change:** Currently, this is embedded in the Engine. It should be it's own thing.
**change:** Currently, the ledger also tracks who wants what. Wantlists should be tracked in the engine.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.