choker

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package choker implements the BitTorrent choking algorithm (BEP 3).

Purpose

The choking algorithm controls which peers we upload data to. Uploading to everyone simultaneously would saturate our upload bandwidth without improving our download rate. By being selective, we implement tit-for-tat: we prefer peers who upload to us, which incentivises mutual cooperation.

Algorithm overview

Every RechokePeriod (10 s) the choker selects [Slots]−1 peers to receive regular unchoke slots and one additional peer for the optimistic unchoke slot.

Regular slot selection:

  • Leecher mode: rank interested peers by bytes downloaded FROM them in the last measurement window (descending). Top [Slots]−1 win regular slots.
  • Seeder mode: rank interested peers by bytes uploaded TO them (descending).
  • Snubbed peers (no data received for SnubTimeout seconds) are excluded from regular slots regardless of mode.

Optimistic unchoke (rotated every OptimisticPeriod = 30 s):

  • One slot is always reserved for a randomly selected choked-but-interested peer, giving them a chance to demonstrate they are a good upload partner.
  • Newly connected peers (< NewPeerWindow) are given 3× the selection probability to accelerate swarm discovery.

Integration

The caller provides two callbacks in Config:

  • GetPeers returns the current snapshot of all peer stats. Called every tick.
  • SetChoked applies a choke or unchoke wire message to a specific peer. This is called outside the choker's internal mutex.

Call New to start the background ticker goroutine, and [Stop] when the torrent session ends. [Trigger] forces an immediate rechoke cycle (useful when a peer connects or disconnects).

Index

Constants

View Source
const (
	// DefaultSlots is the default number of unchoke slots (regular + optimistic).
	DefaultSlots = 4

	// RechokePeriod is how often the regular unchoke set is recomputed.
	RechokePeriod = 10 * time.Second

	// OptimisticPeriod is how often the optimistic unchoke slot is rotated.
	OptimisticPeriod = 30 * time.Second

	// SnubTimeout is how long a peer can be silent (no piece data received)
	// before the choker marks it snubbed and excludes it from regular slots.
	SnubTimeout = 60 * time.Second

	// NewPeerWindow is the age threshold for 3× optimistic-unchoke weighting.
	// Peers connected more recently than this are preferred for discovery.
	NewPeerWindow = 3 * time.Minute
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Choker

type Choker struct {
	// contains filtered or unexported fields
}

Choker implements the BEP 3 choking algorithm.

func New

func New(cfg Config) *Choker

New creates a Choker and starts the background rechoke goroutine. Call Choker.Stop when the torrent session ends to release resources.

func (*Choker) SetSeeder

func (c *Choker) SetSeeder(seeder bool)

SetSeeder switches between leecher ranking (by download rate) and seeder ranking (by upload rate). Triggers an immediate rechoke.

func (*Choker) Stop

func (c *Choker) Stop()

Stop shuts down the background goroutine. Safe to call multiple times.

func (*Choker) Trigger

func (c *Choker) Trigger()

Trigger forces an immediate rechoke cycle without waiting for the next tick. Call this when a peer connects, disconnects, or changes interest state.

func (*Choker) Unchoked

func (c *Choker) Unchoked() []PeerID

Unchoked returns a copy of the current set of unchoked peer IDs (regular + optimistic). Safe to call from any goroutine.

type Config

type Config struct {
	// Slots is the total number of unchoke slots (regular + 1 optimistic).
	// 0 falls back to [DefaultSlots].
	Slots int

	// IsSeeder indicates we have 100% of the torrent's pieces.  Switches the
	// ranking metric from download rate to upload rate.
	IsSeeder bool

	// GetPeers is called each rechoke cycle to obtain the current peer stats.
	// It must be non-nil.
	GetPeers func() []PeerStats

	// SetChoked is called to apply a choke (true) or unchoke (false) decision
	// to a peer.  Called outside the choker's internal mutex.
	// It must be non-nil.
	SetChoked func(id PeerID, choked bool)
}

Config holds constructor parameters for New.

type PeerID

type PeerID string

PeerID uniquely identifies a peer connection. In practice this is the remote address string ("1.2.3.4:6881") or peer_id.

type PeerStats

type PeerStats struct {
	ID PeerID

	// Interested is true if the peer has sent us an Interested message and not
	// yet a NotInterested message.
	Interested bool

	// BytesDownloadedFrom is the bytes received from this peer in the current
	// measurement window (~20 s recommended).  Used in leecher mode ranking.
	BytesDownloadedFrom int64

	// BytesUploadedTo is the bytes sent to this peer in the current measurement
	// window.  Used in seeder mode ranking.
	BytesUploadedTo int64

	// LastReceived is the wall time of the most recent piece block received from
	// this peer.  Zero if no data has ever been received.  Used for snub detection.
	LastReceived time.Time

	// ConnectedAt is when the TCP connection to this peer was established.
	// Used to give newly connected peers a 3× optimistic-unchoke weight.
	ConnectedAt time.Time
}

PeerStats is a snapshot of the metrics the choker needs for one peer. The peer connection layer is responsible for updating these values before each GetPeers call.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL