diff --git a/server/announce.go b/server/announce.go index a559f1b..4914f2f 100644 --- a/server/announce.go +++ b/server/announce.go @@ -58,8 +58,8 @@ func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { } // If the torrent was pruned and the user is seeding, unprune it - if torrent.Pruned && left == 0 { - err := tx.Unprune(torrent) + if !torrent.Active && left == 0 { + err := tx.Active(torrent) if err != nil { log.Panicf("server: %s", err) } diff --git a/storage/data.go b/storage/data.go index 159b88c..ff4d1c7 100644 --- a/storage/data.go +++ b/storage/data.go @@ -19,7 +19,7 @@ type Peer struct { type Torrent struct { ID uint64 `json:"id"` Infohash string `json:"infohash"` - Pruned bool `json:"pruned"` + Active bool `json:"active"` Seeders map[string]Peer `json:"seeders"` Leechers map[string]Peer `json:"leechers"` Snatches uint `json:"snatches"` diff --git a/storage/redis/redis.go b/storage/redis/redis.go index fa62c30..3426645 100644 --- a/storage/redis/redis.go +++ b/storage/redis/redis.go @@ -3,11 +3,11 @@ // which can be found in the LICENSE file. // Package redis implements the storage interface for a BitTorrent tracker. - +// // The client whitelist is represented as a set with the key name "whitelist" // with an optional prefix. Torrents and users are JSON-formatted strings. -// Torrents' keys are named "torrent_" with an optional prefix. -// Users' keys are named "user_" with an optional prefix. +// Torrents' keys are named "torrent:" with an optional prefix. +// Users' keys are named "user:" with an optional prefix. package redis import ( @@ -74,7 +74,7 @@ func (ds *DS) FindUser(passkey string) (*storage.User, bool, error) { conn := ds.Get() defer conn.Close() - key := ds.conf.Prefix + "user_" + passkey + key := ds.conf.Prefix + "user:" + passkey reply, err := redis.String(conn.Do("GET", key)) if err != nil { if err == redis.ErrNil { @@ -95,7 +95,7 @@ func (ds *DS) FindTorrent(infohash string) (*storage.Torrent, bool, error) { conn := ds.Get() defer conn.Close() - key := ds.conf.Prefix + "torrent_" + infohash + key := ds.conf.Prefix + "torrent:" + infohash reply, err := redis.String(conn.Do("GET", key)) if err != nil { if err == redis.ErrNil { @@ -180,16 +180,13 @@ func (tx *Tx) Snatch(user *storage.User, torrent *storage.Torrent) error { return nil } -func (tx *Tx) Unprune(t *storage.Torrent) error { +func (tx *Tx) Active(t *storage.Torrent) error { if tx.done { return storage.ErrTxDone } - key := tx.conf.Prefix + "Torrent:" + t.Infohash - err := tx.Send("HSET " + key + " Status 0") - if err != nil { - return err - } - return nil + key := tx.conf.Prefix + "torrent:" + t.Infohash + err := activeScript.Send(tx.Conn, key) + return err } func (tx *Tx) NewLeecher(t *storage.Torrent, p *storage.Peer) error { @@ -236,24 +233,27 @@ func (tx *Tx) RmSeeder(t *storage.Torrent, p *storage.Peer) error { if tx.done { return storage.ErrTxDone } - // TODO - return nil + key := tx.conf.Prefix + "torrent:" + t.Infohash + err := rmSeederScript.Send(tx.Conn, key, p.ID) + return err } func (tx *Tx) IncrementSlots(u *storage.User) error { if tx.done { return storage.ErrTxDone } - // TODO - return nil + key := tx.conf.Prefix + "user:" + u.Passkey + err := incSlotsScript.Send(tx.Conn, key) + return err } func (tx *Tx) DecrementSlots(u *storage.User) error { if tx.done { return storage.ErrTxDone } - // TODO - return nil + key := tx.conf.Prefix + "user:" + u.Passkey + err := decSlotsScript.Send(tx.Conn, key) + return err } func init() { diff --git a/storage/redis/scripts.go b/storage/redis/scripts.go new file mode 100644 index 0000000..d3d53b7 --- /dev/null +++ b/storage/redis/scripts.go @@ -0,0 +1,67 @@ +package storage + +import ( + "github.com/garyburd/redigo/redis" +) + +var incSlotsScript = redis.NewScript(1, incSlotsScriptSrc) + +const incSlotsScriptSrc = ` +if redis.call("exists", keys[1]) == 1 then + local json = redis.call("get", keys[1]) + local user = cjson.decode(json) + user["slots_used"] = user["slots_used"] + 1 + json = cjson.encode(user) + redis.call("set", key, json) + return user["slots_used"] +else + return nil +end +` + +var decSlotsScript = redis.NewScript(1, incSlotsScriptSrc) + +const decSlotsScriptSrc = ` +if redis.call("exists", keys[1]) == 1 then + local json = redis.call("get", keys[1]) + local user = cjson.decode(json) + if user["slots_used"] > 0 + user["slots_used"] = user["slots_used"] - 1 + end + json = cjson.encode(user) + redis.call("set", key, json) + return user["slots_used"] +else + return nil +end +` + +var activeScript = redis.NewScript(1, decSlotsScriptSrc) + +const activeScriptSrc = ` +if redis.call("exists", keys[1]) == 1 then + local json = redis.call("get", keys[1]) + local torrent = cjson.decode(json) + torrent["active"] = true + json = cjson.encode(torrent) + redis.call("set", key, json) + return user["slots_used"] +else + return nil +end +` + +var rmSeederScript = redis.NewScript(2, rmSeederScriptSrc) + +const rmSeederScriptSrc = ` +if redis.call("EXISTS", keys[1]) == 1 then + local json = redis.call("GET", keys[1]) + local torrent = cjson.decode(json) + table.remove(torrent["seeders"], keys[2]) + json = cjson.encode(torrent) + redis.call("SET", key, json) + return 0 +else + return nil +end +` diff --git a/storage/storage.go b/storage/storage.go index 03e549d..d77369e 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -74,7 +74,7 @@ type Tx interface { // Torrents Snatch(u *User, t *Torrent) error - Unprune(t *Torrent) error + Active(t *Torrent) error // Peers NewLeecher(t *Torrent, p *Peer) error