type requestablePiece struct {
index pieceIndex
t Torrent
+ alwaysReallocate bool
NumPendingChunks int
IterPendingChunks ChunksIter
}
t: piece.t.Torrent,
NumPendingChunks: piece.NumPendingChunks,
IterPendingChunks: piece.iterPendingChunksWrapper,
+ alwaysReallocate: piece.Priority >= types.PiecePriorityNext,
})
}
return
}
}
}()
- sortPeersForPiece := func(byHasRequest *Request) {
+ sortPeersForPiece := func(req *Request) {
sort.Slice(peersForPiece, func(i, j int) bool {
- ml := multiless.New().Int(
- peersForPiece[i].requestsInPiece,
- peersForPiece[j].requestsInPiece,
- ).Int(
+ byHasRequest := func() multiless.Computation {
+ ml := multiless.New()
+ if req != nil {
+ _, iHas := peersForPiece[i].nextState.Requests[*req]
+ _, jHas := peersForPiece[j].nextState.Requests[*req]
+ ml = ml.Bool(jHas, iHas)
+ }
+ return ml
+ }()
+ ml := multiless.New()
+ // We always "reallocate", that is force even striping amongst peers that are either on
+ // the last piece they can contribute too, or for pieces marked for this behaviour.
+ // Striping prevents starving peers of requests, and will always re-balance to the
+ // fastest known peers.
+ if !p.alwaysReallocate {
+ ml = ml.Bool(
+ peersForPiece[j].requestablePiecesRemaining == 1,
+ peersForPiece[i].requestablePiecesRemaining == 1)
+ }
+ if p.alwaysReallocate || peersForPiece[j].requestablePiecesRemaining == 1 {
+ ml = ml.Int(
+ peersForPiece[i].requestsInPiece,
+ peersForPiece[j].requestsInPiece)
+ } else {
+ ml = ml.AndThen(byHasRequest)
+ }
+ ml = ml.Int(
peersForPiece[i].requestablePiecesRemaining,
peersForPiece[j].requestablePiecesRemaining,
).Float64(
peersForPiece[j].DownloadRate,
peersForPiece[i].DownloadRate,
)
- if byHasRequest != nil {
- _, iHas := peersForPiece[i].nextState.Requests[*byHasRequest]
- _, jHas := peersForPiece[j].nextState.Requests[*byHasRequest]
- ml = ml.Bool(jHas, iHas)
- }
+ ml = ml.AndThen(byHasRequest)
return ml.Int64(
int64(peersForPiece[j].Age), int64(peersForPiece[i].Age),
// TODO: Probably peer priority can come next
// Slower than the stealers, but has all requests already.
stealee := basePeer
stealee.DownloadRate = 1
- keepReqs := requestSetFromSlice(r(0, 0), r(0, 1), r(0, 2))
+ keepReqs := requestSetFromSlice(
+ r(3, 2), r(3, 4), r(3, 6), r(3, 8),
+ r(4, 0), r(4, 1), r(4, 7), r(4, 8))
stealee.HasExistingRequest = func(r Request) bool {
_, ok := keepReqs[r]
return ok
firstStealer.Id = intPeerId(2)
secondStealer := basePeer
secondStealer.Id = intPeerId(3)
+ secondStealer.HasPiece = func(i pieceIndex) bool {
+ switch i {
+ case 1, 3:
+ return true
+ default:
+ return false
+ }
+ }
results := Run(Input{Torrents: []Torrent{{
- Pieces: []Piece{{
- Request: true,
- NumPendingChunks: 9,
- IterPendingChunks: chunkIterRange(9),
- }},
+ Pieces: []Piece{
+ {
+ Request: true,
+ NumPendingChunks: 0,
+ IterPendingChunks: chunkIterRange(9),
+ },
+ {
+ Request: true,
+ NumPendingChunks: 7,
+ IterPendingChunks: chunkIterRange(7),
+ },
+ {
+ Request: true,
+ NumPendingChunks: 0,
+ IterPendingChunks: chunkIterRange(0),
+ },
+ {
+ Request: true,
+ NumPendingChunks: 9,
+ IterPendingChunks: chunkIterRange(9),
+ },
+ {
+ Request: true,
+ NumPendingChunks: 9,
+ IterPendingChunks: chunkIterRange(9),
+ }},
Peers: []Peer{
firstStealer,
stealee,
c.Check(results[p].Requests, qt.HasLen, l)
c.Check(results[p].Interested, qt.Equals, l > 0)
}
- check(firstStealer.Id, 3)
- check(secondStealer.Id, 3)
+ check(firstStealer.Id, 5)
+ check(secondStealer.Id, 7+9)
c.Check(results[stealee.Id], qt.ContentEquals, PeerNextRequestState{
Interested: true,
- Requests: keepReqs,
+ Requests: requestSetFromSlice(r(4, 0), r(4, 1), r(4, 7), r(4, 8)),
})
}