initial work on redis scripts

This commit is contained in:
Jimmy Zelinskie 2013-07-24 03:08:38 -04:00
parent 2cd3473d18
commit 94187e4261
5 changed files with 89 additions and 22 deletions

View file

@ -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 the torrent was pruned and the user is seeding, unprune it
if torrent.Pruned && left == 0 { if !torrent.Active && left == 0 {
err := tx.Unprune(torrent) err := tx.Active(torrent)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }

View file

@ -19,7 +19,7 @@ type Peer struct {
type Torrent struct { type Torrent struct {
ID uint64 `json:"id"` ID uint64 `json:"id"`
Infohash string `json:"infohash"` Infohash string `json:"infohash"`
Pruned bool `json:"pruned"` Active bool `json:"active"`
Seeders map[string]Peer `json:"seeders"` Seeders map[string]Peer `json:"seeders"`
Leechers map[string]Peer `json:"leechers"` Leechers map[string]Peer `json:"leechers"`
Snatches uint `json:"snatches"` Snatches uint `json:"snatches"`

View file

@ -3,11 +3,11 @@
// which can be found in the LICENSE file. // which can be found in the LICENSE file.
// Package redis implements the storage interface for a BitTorrent tracker. // Package redis implements the storage interface for a BitTorrent tracker.
//
// The client whitelist is represented as a set with the key name "whitelist" // The client whitelist is represented as a set with the key name "whitelist"
// with an optional prefix. Torrents and users are JSON-formatted strings. // with an optional prefix. Torrents and users are JSON-formatted strings.
// Torrents' keys are named "torrent_<infohash>" with an optional prefix. // Torrents' keys are named "torrent:<infohash>" with an optional prefix.
// Users' keys are named "user_<passkey>" with an optional prefix. // Users' keys are named "user:<passkey>" with an optional prefix.
package redis package redis
import ( import (
@ -74,7 +74,7 @@ func (ds *DS) FindUser(passkey string) (*storage.User, bool, error) {
conn := ds.Get() conn := ds.Get()
defer conn.Close() defer conn.Close()
key := ds.conf.Prefix + "user_" + passkey key := ds.conf.Prefix + "user:" + passkey
reply, err := redis.String(conn.Do("GET", key)) reply, err := redis.String(conn.Do("GET", key))
if err != nil { if err != nil {
if err == redis.ErrNil { if err == redis.ErrNil {
@ -95,7 +95,7 @@ func (ds *DS) FindTorrent(infohash string) (*storage.Torrent, bool, error) {
conn := ds.Get() conn := ds.Get()
defer conn.Close() defer conn.Close()
key := ds.conf.Prefix + "torrent_" + infohash key := ds.conf.Prefix + "torrent:" + infohash
reply, err := redis.String(conn.Do("GET", key)) reply, err := redis.String(conn.Do("GET", key))
if err != nil { if err != nil {
if err == redis.ErrNil { if err == redis.ErrNil {
@ -180,16 +180,13 @@ func (tx *Tx) Snatch(user *storage.User, torrent *storage.Torrent) error {
return nil return nil
} }
func (tx *Tx) Unprune(t *storage.Torrent) error { func (tx *Tx) Active(t *storage.Torrent) error {
if tx.done { if tx.done {
return storage.ErrTxDone return storage.ErrTxDone
} }
key := tx.conf.Prefix + "Torrent:" + t.Infohash key := tx.conf.Prefix + "torrent:" + t.Infohash
err := tx.Send("HSET " + key + " Status 0") err := activeScript.Send(tx.Conn, key)
if err != nil { return err
return err
}
return nil
} }
func (tx *Tx) NewLeecher(t *storage.Torrent, p *storage.Peer) error { 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 { if tx.done {
return storage.ErrTxDone return storage.ErrTxDone
} }
// TODO key := tx.conf.Prefix + "torrent:" + t.Infohash
return nil err := rmSeederScript.Send(tx.Conn, key, p.ID)
return err
} }
func (tx *Tx) IncrementSlots(u *storage.User) error { func (tx *Tx) IncrementSlots(u *storage.User) error {
if tx.done { if tx.done {
return storage.ErrTxDone return storage.ErrTxDone
} }
// TODO key := tx.conf.Prefix + "user:" + u.Passkey
return nil err := incSlotsScript.Send(tx.Conn, key)
return err
} }
func (tx *Tx) DecrementSlots(u *storage.User) error { func (tx *Tx) DecrementSlots(u *storage.User) error {
if tx.done { if tx.done {
return storage.ErrTxDone return storage.ErrTxDone
} }
// TODO key := tx.conf.Prefix + "user:" + u.Passkey
return nil err := decSlotsScript.Send(tx.Conn, key)
return err
} }
func init() { func init() {

67
storage/redis/scripts.go Normal file
View file

@ -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
`

View file

@ -74,7 +74,7 @@ type Tx interface {
// Torrents // Torrents
Snatch(u *User, t *Torrent) error Snatch(u *User, t *Torrent) error
Unprune(t *Torrent) error Active(t *Torrent) error
// Peers // Peers
NewLeecher(t *Torrent, p *Peer) error NewLeecher(t *Torrent, p *Peer) error