Files
mochi/tracker/memory/conn.go
Jimmy Zelinskie e52e4d5f1d Introduce PeerKey
PeerKeys are used to prevent overwriting of peers which want to announce
for both IPv4 and IPv6.
2014-08-01 11:21:57 -04:00

269 lines
4.9 KiB
Go

// 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 memory
import (
"runtime"
"time"
"github.com/chihaya/chihaya/stats"
"github.com/chihaya/chihaya/tracker/models"
)
// Conn implements a connection to a memory-based tracker data store.
type Conn struct {
*Pool
}
func (c *Conn) Close() error {
return nil
}
func (c *Conn) FindUser(passkey string) (*models.User, error) {
c.usersM.RLock()
defer c.usersM.RUnlock()
user, exists := c.users[passkey]
if !exists {
return nil, models.ErrUserDNE
}
return &*user, nil
}
func (c *Conn) FindTorrent(infohash string) (*models.Torrent, error) {
c.torrentsM.RLock()
defer c.torrentsM.RUnlock()
torrent, exists := c.torrents[infohash]
if !exists {
return nil, models.ErrTorrentDNE
}
return &*torrent, nil
}
func (c *Conn) FindClient(peerID string) error {
c.whitelistM.RLock()
defer c.whitelistM.RUnlock()
_, ok := c.whitelist[peerID]
if !ok {
return models.ErrClientUnapproved
}
return nil
}
func (c *Conn) IncrementTorrentSnatches(infohash string) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, exists := c.torrents[infohash]
if !exists {
return models.ErrTorrentDNE
}
t.Snatches++
return nil
}
func (c *Conn) IncrementUserSnatches(userID string) error {
c.usersM.Lock()
defer c.usersM.Unlock()
u, exists := c.users[userID]
if !exists {
return models.ErrUserDNE
}
u.Snatches++
return nil
}
func (c *Conn) TouchTorrent(infohash string) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, ok := c.torrents[infohash]
if !ok {
return models.ErrTorrentDNE
}
t.LastAction = time.Now().Unix()
return nil
}
func (c *Conn) DeleteLeecher(infohash string, pk models.PeerKey) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, ok := c.torrents[infohash]
if !ok {
return models.ErrTorrentDNE
}
delete(t.Leechers, pk)
return nil
}
func (c *Conn) DeleteSeeder(infohash string, pk models.PeerKey) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, ok := c.torrents[infohash]
if !ok {
return models.ErrTorrentDNE
}
delete(t.Seeders, pk)
return nil
}
func (c *Conn) PutLeecher(infohash, ipv string, p *models.Peer) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, ok := c.torrents[infohash]
if !ok {
return models.ErrTorrentDNE
}
t.Leechers[models.NewPeerKey(p.ID, ipv)] = *p
return nil
}
func (c *Conn) PutSeeder(infohash, ipv string, p *models.Peer) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
t, ok := c.torrents[infohash]
if !ok {
return models.ErrTorrentDNE
}
t.Seeders[models.NewPeerKey(p.ID, ipv)] = *p
return nil
}
func (c *Conn) PutTorrent(t *models.Torrent) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
c.torrents[t.Infohash] = &*t
return nil
}
func (c *Conn) DeleteTorrent(infohash string) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
delete(c.torrents, infohash)
return nil
}
func (c *Conn) PurgeInactiveTorrent(infohash string) error {
c.torrentsM.Lock()
defer c.torrentsM.Unlock()
torrent, exists := c.torrents[infohash]
if !exists {
return models.ErrTorrentDNE
}
if torrent.PeerCount() == 0 {
delete(c.torrents, infohash)
}
return nil
}
func (c *Conn) PutUser(u *models.User) error {
c.usersM.Lock()
defer c.usersM.Unlock()
c.users[u.Passkey] = &*u
return nil
}
func (c *Conn) DeleteUser(passkey string) error {
c.usersM.Lock()
defer c.usersM.Unlock()
delete(c.users, passkey)
return nil
}
func (c *Conn) PutClient(peerID string) error {
c.whitelistM.Lock()
defer c.whitelistM.Unlock()
c.whitelist[peerID] = true
return nil
}
func (c *Conn) DeleteClient(peerID string) error {
c.whitelistM.Lock()
defer c.whitelistM.Unlock()
delete(c.whitelist, peerID)
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]
if torrent == nil {
continue // Torrent deleted since keys were computed.
}
for key, peer := range torrent.Seeders {
if peer.LastAnnounce <= unixtime {
delete(torrent.Seeders, key)
stats.RecordPeerEvent(stats.ReapedSeed, string(key[:4]))
}
}
for key, peer := range torrent.Leechers {
if peer.LastAnnounce <= unixtime {
delete(torrent.Leechers, key)
stats.RecordPeerEvent(stats.ReapedLeech, string(key[:4]))
}
}
peers := torrent.PeerCount()
c.torrentsM.Unlock()
if purgeEmptyTorrents && peers == 0 {
c.PurgeInactiveTorrent(infohash)
stats.RecordEvent(stats.ReapedTorrent)
}
}
return nil
}