initial work on redis scripts
This commit is contained in:
parent
2cd3473d18
commit
94187e4261
5 changed files with 89 additions and 22 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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
67
storage/redis/scripts.go
Normal 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
|
||||||
|
`
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue