From cd6204f13dcae0f2251ec92a5ac2c580c653de6f Mon Sep 17 00:00:00 2001 From: Justin Li Date: Wed, 16 Jul 2014 19:38:51 -0400 Subject: [PATCH] Purge peers that have not announced for twice the announce interval --- config/config.go | 14 ++++++++--- drivers/tracker/memory/conn.go | 45 ++++++++++++++++++++++++++++++++++ drivers/tracker/routines.go | 30 +++++++++++++++++++++++ drivers/tracker/tracker.go | 3 +++ http/announce.go | 2 +- http/http.go | 7 ++++++ 6 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 drivers/tracker/routines.go diff --git a/config/config.go b/config/config.go index 84a73b7..22469f6 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,8 @@ type Config struct { Freeleech bool `json:"freeleech"` Whitelist bool `json:"whitelist"` + PurgeInactiveTorrents bool `json:"purge_inactive_torrents"` + Announce Duration `json:"announce"` MinAnnounce Duration `json:"min_announce"` RequestTimeout Duration `json:"request_timeout"` @@ -59,15 +61,21 @@ type Config struct { // DefaultConfig is a configuration that can be used as a fallback value. var DefaultConfig = Config{ Addr: "127.0.0.1:6881", + Tracker: DriverConfig{ Name: "memory", }, + Backend: DriverConfig{ Name: "noop", }, - Private: false, - Freeleech: false, - Whitelist: false, + + Private: false, + Freeleech: false, + Whitelist: false, + + PurgeInactiveTorrents: true, + Announce: Duration{30 * time.Minute}, MinAnnounce: Duration{15 * time.Minute}, RequestTimeout: Duration{10 * time.Second}, diff --git a/drivers/tracker/memory/conn.go b/drivers/tracker/memory/conn.go index d9a9ce4..6a3f3a0 100644 --- a/drivers/tracker/memory/conn.go +++ b/drivers/tracker/memory/conn.go @@ -5,6 +5,7 @@ package memory import ( + "runtime" "time" "github.com/chihaya/chihaya/drivers/tracker" @@ -223,3 +224,47 @@ func (c *Conn) DeleteClient(peerID string) error { return nil } +func (c *Conn) PurgeInactivePeers(purgeEmptyTorrents bool, before time.Time) error { + unixtime := before.Unix() + + // Build array of map keys to operate on. + c.torrentsM.RLock() + index := 0 + keys := make([]string, len(c.torrents)) + + for infohash, _ := range c.torrents { + keys[index] = infohash + index++ + } + + c.torrentsM.RUnlock() + + // Process keys. + for _, infohash := range keys { + runtime.Gosched() // Let other goroutines run, since this is low priority. + + c.torrentsM.Lock() + torrent := c.torrents[infohash] + + for key, peer := range torrent.Seeders { + if peer.LastAnnounce < unixtime { + delete(torrent.Seeders, key) + } + } + + for key, peer := range torrent.Leechers { + if peer.LastAnnounce < unixtime { + delete(torrent.Leechers, key) + } + } + + peers := torrent.PeerCount() + c.torrentsM.Unlock() + + if purgeEmptyTorrents && peers == 0 { + c.PurgeInactiveTorrent(infohash) + } + } + + return nil +} diff --git a/drivers/tracker/routines.go b/drivers/tracker/routines.go new file mode 100644 index 0000000..4c5dd53 --- /dev/null +++ b/drivers/tracker/routines.go @@ -0,0 +1,30 @@ +// Copyright 2014 The Chihaya Authors. All rights reserved. +// Use of this source code is governed by the BSD 2-Clause license, +// which can be found in the LICENSE file. + +package tracker + +import ( + "time" + + "github.com/golang/glog" +) + +func PurgeInactivePeers(p Pool, purgeEmptyTorrents bool, threshold, interval time.Duration) { + for _ = range time.NewTicker(interval).C { + before := time.Now().Add(-threshold) + glog.V(0).Infof("Purging peers with no announces since %s", before) + + conn, err := p.Get() + + if err != nil { + glog.Error("Unable to get connection for a routine") + continue + } + + err = conn.PurgeInactivePeers(purgeEmptyTorrents, before) + if err != nil { + glog.Errorf("Error purging torrents: %s", err) + } + } +} diff --git a/drivers/tracker/tracker.go b/drivers/tracker/tracker.go index 6e2dff5..1b15b01 100644 --- a/drivers/tracker/tracker.go +++ b/drivers/tracker/tracker.go @@ -9,6 +9,7 @@ package tracker import ( "errors" "fmt" + "time" "github.com/chihaya/chihaya/config" "github.com/chihaya/chihaya/models" @@ -79,7 +80,9 @@ type Conn interface { DeleteLeecher(infohash, peerkey string) error PutSeeder(infohash string, p *models.Peer) error DeleteSeeder(infohash, peerkey string) error + PurgeInactiveTorrent(infohash string) error + PurgeInactivePeers(purgeEmptyTorrents bool, before time.Time) error // User interactions FindUser(passkey string) (*models.User, error) diff --git a/http/announce.go b/http/announce.go index a298ed4..6cf877f 100644 --- a/http/announce.go +++ b/http/announce.go @@ -91,7 +91,7 @@ func (t *Tracker) ServeAnnounce(w http.ResponseWriter, r *http.Request, p httpro if err != nil { return http.StatusInternalServerError, err } - } else if torrent.PeerCount() == 0 { + } else if t.cfg.PurgeInactiveTorrents && torrent.PeerCount() == 0 { // Rather than deleting the torrent explicitly, let the tracker driver // ensure there are no race conditions. conn.PurgeInactiveTorrent(torrent.Infohash) diff --git a/http/http.go b/http/http.go index 8dd3f98..6db42ef 100644 --- a/http/http.go +++ b/http/http.go @@ -99,6 +99,13 @@ func Serve(cfg *config.Config) { glog.Fatal("New: ", err) } + go tracker.PurgeInactivePeers( + t.pool, + cfg.PurgeInactiveTorrents, + cfg.Announce.Duration*2, + cfg.Announce.Duration, + ) + glog.V(0).Info("Starting on ", cfg.Addr) graceful.Run(cfg.Addr, cfg.RequestTimeout.Duration, NewRouter(t, cfg)) }