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 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)
}

View file

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

View file

@ -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_<infohash>" with an optional prefix.
// Users' keys are named "user_<passkey>" with an optional prefix.
// Torrents' keys are named "torrent:<infohash>" with an optional prefix.
// Users' keys are named "user:<passkey>" 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() {

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
Snatch(u *User, t *Torrent) error
Unprune(t *Torrent) error
Active(t *Torrent) error
// Peers
NewLeecher(t *Torrent, p *Peer) error